旧物捐赠类应用在近几年逐渐成为校园和社会公益的热门载体。这个毕业设计选题结合了当前最主流的Java技术栈(SSM框架)和前端Vue.js框架,实现了一个完整的旧物捐赠平台。从技术层面来看,这个选题既考察了学生对基础框架的掌握程度,又需要处理典型的电商类业务逻辑,包括用户系统、物品管理、交易流程等核心模块。
我在实际开发过程中发现,这类系统最考验的是前后端数据交互的设计能力。比如捐赠物品的状态流转(待审核/已上架/已预约/已完成)、用户信用体系的建立、地理位置服务集成等,都是需要特别注意的技术难点。相比普通的电商系统,捐赠平台还需要考虑公益属性带来的特殊业务逻辑,比如捐赠记录的公开透明化、捐赠证书的生成等需求。
SSM(Spring+SpringMVC+MyBatis)组合是JavaEE开发的经典选择。在这个项目中,我采用了分层架构设计:
code复制com.donation
├── config # Spring配置类
├── controller # 表现层
├── service # 业务逻辑层
├── dao # 数据访问层
├── entity # 实体类
└── util # 工具包
特别需要注意Spring的事务管理配置。在捐赠业务中,一个完整的捐赠流程可能涉及多个数据库操作:
java复制@Transactional
public DonationResult confirmDonation(Long itemId, Long receiverId) {
// 1. 更新物品状态
donationItemMapper.updateStatus(itemId, ItemStatus.RECEIVED);
// 2. 创建捐赠记录
DonationRecord record = new DonationRecord();
record.setItemId(itemId);
record.setGiverId(getCurrentUserId());
record.setReceiverId(receiverId);
donationRecordMapper.insert(record);
// 3. 更新用户积分
userService.addPoints(receiverId, 10);
userService.addPoints(getCurrentUserId(), 5);
return buildResult(record);
}
前端采用Vue CLI搭建项目骨架,主要技术选型:
一个典型的物品列表组件实现:
vue复制<template>
<div class="item-list">
<el-card v-for="item in items" :key="item.id">
<img :src="item.coverUrl" class="item-cover">
<h3>{{ item.title }}</h3>
<el-tag :type="item.status | statusColor">
{{ item.status | statusText }}
</el-tag>
<el-button
v-if="canDonate(item)"
@click="handleDonate(item.id)">
立即捐赠
</el-button>
</el-card>
</div>
</template>
<script>
export default {
filters: {
statusColor(status) {
const map = {
'PENDING': 'info',
'ONLINE': 'success',
'RESERVED': 'warning'
}
return map[status] || ''
}
},
methods: {
async handleDonate(itemId) {
try {
await this.$store.dispatch('donateItem', itemId)
this.$message.success('捐赠请求已提交')
} catch (err) {
this.$message.error(err.message)
}
}
}
}
</script>
捐赠物品的状态流转是本系统的核心业务逻辑。我采用状态模式(State Pattern)来实现这一机制:
java复制public interface DonationState {
void reserve(DonationContext context);
void cancel(DonationContext context);
void complete(DonationContext context);
}
public class PendingState implements DonationState {
@Override
public void reserve(DonationContext context) {
if (context.isValidUser()) {
context.setState(new ReservedState());
context.save();
}
}
// 其他方法实现...
}
// 在Service层使用
public class DonationService {
public void reserveItem(Long itemId) {
DonationContext context = loadContext(itemId);
context.getState().reserve(context);
}
}
状态转换示意图:
code复制[待审核] --审核通过--> [已上架]
[已上架] --被预约--> [已预约]
[已预约] --取消预约--> [已上架]
[已预约] --完成捐赠--> [已完成]
为了方便捐赠者和受赠者交接物品,系统集成了高德地图API实现:
关键代码示例:
javascript复制// 前端获取地理位置
export function getLocation() {
return new Promise((resolve, reject) => {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(
position => resolve({
lng: position.coords.longitude,
lat: position.coords.latitude
}),
error => reject(error)
);
} else {
reject(new Error('浏览器不支持地理位置'));
}
});
}
java复制// 后端距离计算
public double calculateDistance(Location loc1, Location loc2) {
double radLat1 = Math.toRadians(loc1.getLatitude());
double radLat2 = Math.toRadians(loc2.getLatitude());
double a = radLat1 - radLat2;
double b = Math.toRadians(loc1.getLongitude()) - Math.toRadians(loc2.getLongitude());
double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a/2),2) +
Math.cos(radLat1)*Math.cos(radLat2)*Math.pow(Math.sin(b/2),2)));
return s * 6378137; // 地球半径
}
java复制public class XSSFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
chain.doFilter(new XSSRequestWrapper((HttpServletRequest) request), response);
}
}
SQL注入防护:
认证授权:
java复制@Cacheable(value = "items", key = "#id")
public DonationItem getItemById(Long id) {
return itemMapper.selectById(id);
}
数据库优化:
前端懒加载:
vue复制<template>
<div v-infinite-scroll="loadMore">
<!-- 物品列表 -->
</div>
</template>
<script>
export default {
methods: {
loadMore() {
if (!this.loading && this.hasMore) {
this.loading = true;
this.$axios.get('/items', {
params: { page: this.page++ }
}).then(res => {
this.items.push(...res.data);
this.hasMore = res.data.length > 0;
}).finally(() => {
this.loading = false;
});
}
}
}
}
</script>
java复制@SpringBootTest
public class DonationServiceTest {
@Autowired
private DonationService donationService;
@Test
@Transactional
public void testReserveItem() {
Long itemId = createTestItem(ItemStatus.ONLINE);
donationService.reserveItem(itemId);
DonationItem item = itemMapper.selectById(itemId);
assertEquals(ItemStatus.RESERVED, item.getStatus());
}
}
javascript复制describe('捐赠流程', () => {
it('可以成功捐赠物品', () => {
cy.login('test@example.com', '123456');
cy.visit('/items');
cy.get('.item-card').first().click();
cy.get('.donate-btn').click();
cy.contains('捐赠请求已提交').should('be.visible');
});
});
推荐采用Docker Compose部署:
yaml复制version: '3'
services:
mysql:
image: mysql:5.7
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: donation
volumes:
- mysql_data:/var/lib/mysql
backend:
build: ./backend
ports:
- "8080:8080"
depends_on:
- mysql
frontend:
build: ./frontend
ports:
- "80:80"
volumes:
mysql_data:
部署步骤:
docker-compose builddocker-compose up -d在实际开发过程中,我遇到了以下几个典型问题及解决方案:
跨域问题:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowCredentials(true)
.maxAge(3600);
}
}
文件上传大小限制:
yaml复制spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 20MB
Vue路由刷新404:
nginx复制location / {
try_files $uri $uri/ /index.html;
}
MyBatis结果映射问题:
xml复制<resultMap id="donationDetailMap" type="DonationDetail">
<id property="id" column="id"/>
<result property="title" column="title"/>
<association property="giver" javaType="User">
<id property="id" column="giver_id"/>
<result property="name" column="giver_name"/>
</association>
</resultMap>
基于这个项目撰写毕业论文时,建议重点关注以下几个章节:
系统架构设计:
核心算法实现:
性能优化分析:
安全防护方案:
论文写作时要注意:
这个基础版本完成后,还可以考虑以下扩展功能:
移动端适配:
智能推荐系统:
区块链存证:
社交功能扩展:
我在开发过程中最大的体会是,一个成功的捐赠平台不仅要技术实现完善,更需要关注用户体验和社会价值。比如我们增加了捐赠证书生成功能后,用户参与度明显提升。后续可以考虑引入更多游戏化元素,如成就系统、公益积分等,进一步激励用户参与。