1. 项目概述:二手手机交易平台的SpringBoot实现
去年帮学弟调试毕业设计时,发现用SpringBoot搭建二手交易平台是个高频选题。这个基于JavaEE的二手手机交易平台,本质上是个典型的B/S架构电商系统,核心在于商品管理、交易流程和用户交互三大模块。相比传统Servlet方案,SpringBoot的自动配置特性能让开发者更专注于业务逻辑,比如我用一个@EnableTransactionManagement注解就搞定了复杂的订单事务管理。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术选型与架构设计
2.1 为什么选择SpringBoot+MySQL组合
在技术选型阶段,我对比过SSM框架和SpringBoot方案。最终选择SpringBoot主要考虑到:
- 内嵌Tomcat省去服务器配置(对毕设演示特别友好)
- starter依赖自动管理JAR包版本(避免"jar包地狱")
- 与Thymeleaf模板引擎天然集成(适合快速开发)
数据库选用MySQL 8.0而非Oracle,原因很实际:
- 社区版完全免费
- 在1C2G的云服务器上跑得更流畅
- JSON字段支持方便存储手机参数(如
{"ram":"8GB","color":"黑色"})
2.2 前后端分离的妥协方案
严格来说这不是纯粹的前后端分离架构。考虑到毕业设计的时间成本,我采用了一种折中方案:
java复制@Controller
public class GoodsController {
@GetMapping("/detail/{id}")
public String detail(Model model, @PathVariable Long id) {
model.addAttribute("goods", goodsService.getDetail(id));
return "goods_detail"; // 返回Thymeleaf模板
}
}
这种模式虽然传统,但能在单工程内快速实现功能。后期要改造成前后端分离也很容易,只需把@Controller换成@RestController。
3. 核心功能实现细节
3.1 商品模块的数据库设计
手机商品表的设计有几个关键点:
sql复制CREATE TABLE `tb_phone` (
`id` bigint NOT NULL AUTO_INCREMENT,
`title` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '商品标题',
`spec_json` json DEFAULT NULL COMMENT '规格参数',
`price` decimal(10,2) NOT NULL COMMENT '售价',
`original_price` decimal(10,2) DEFAULT NULL COMMENT '原价',
`user_id` bigint NOT NULL COMMENT '卖家ID',
`status` tinyint NOT NULL DEFAULT '1' COMMENT '1上架 0下架',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
FULLTEXT KEY `ft_title` (`title`) WITH PARSER `ngram` -- 中文全文检索
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
特别注意:
- 使用utf8mb4字符集支持emoji(商品描述里很常见)
- JSON字段存储动态规格(不同手机型号参数差异大)
- 全文索引优化搜索性能
3.2 交易流程的状态机实现
订单状态流转是核心难点,我采用状态模式封装业务规则:
java复制public interface OrderState {
void pay(Order order);
void deliver(Order order);
void receive(Order order);
}
@Component
@Scope("prototype")
public class UnpaidState implements OrderState {
@Override
public void pay(Order order) {
order.setState(OrderConstant.PAID);
// 扣减库存等操作
}
// 其他方法抛出IllegalStateException
}
在Service层通过状态上下文控制流程:
java复制public class OrderService {
public void payOrder(Long orderId) {
Order order = orderMapper.selectById(orderId);
OrderState state = StateFactory.getState(order.getState());
state.pay(order); // 多态调用
}
}
4. 安全防护实战方案
4.1 XSS防御的三种武器
二手平台最怕用户上传恶意脚本,我的防御策略是:
- 前端过滤:使用vue-sanitize清理富文本
- 后端校验:Spring的@Validated注解
java复制@PostMapping("/post") public Result postGoods(@Valid @RequestBody GoodsDTO dto) { // 自动校验@NotBlank等注解 } - 存储层防护:MyBatis拦截器对入库数据转义
xml复制<update id="updateGoods"> UPDATE tb_phone SET description=#{description,typeHandler=HtmlEscapeTypeHandler} WHERE id=#{id} </update>
4.2 支付接口的防重放攻击
借鉴支付宝的机制实现简单防重:
java复制@Aspect
@Component
public class PayIdempotentAspect {
@Around("@annotation(idempotent)")
public Object checkRepeat(ProceedingJoinPoint pjp) throws Throwable {
String orderNo = getOrderNo(pjp.getArgs());
if (redisTemplate.opsForValue().setIfAbsent("pay:"+orderNo, "1", 5, TimeUnit.MINUTES)) {
return pjp.proceed();
}
throw new BusinessException("请勿重复支付");
}
}
5. 性能优化技巧
5.1 缓存策略的三层架构
- 本地缓存:Caffeine处理热点数据
java复制@Cacheable(cacheNames = "phone", key = "#id") public PhoneDetailVO getDetail(Long id) { // DB查询 } - Redis缓存:存储商品列表等
- 静态资源:Nginx缓存商品图片
5.2 MySQL查询优化实录
遇到过一个慢查询:根据多个条件筛选手机。解决方案:
sql复制-- 原始写法(全表扫描)
SELECT * FROM tb_phone
WHERE price BETWEEN 1000 AND 2000
AND brand LIKE '%华为%'
AND create_time > '2023-01-01';
-- 优化方案
ALTER TABLE tb_phone ADD INDEX idx_price_brand_time (price, brand, create_time);
-- 使用强制索引
SELECT * FROM tb_phone FORCE INDEX(idx_price_brand_time)
WHERE price BETWEEN 1000 AND 2000
AND brand LIKE '华为%' -- 左匹配
AND create_time > '2023-01-01';
关键点:
- 范围查询字段放索引最后
- 避免左模糊匹配
- 必要时force index
6. 部署踩坑记录
6.1 镜像体积瘦身术
用Docker部署时发现镜像高达800MB,通过多阶段构建优化:
dockerfile复制# 构建阶段
FROM maven:3.8.6-jdk-11 AS build
COPY . .
RUN mvn clean package -DskipTests
# 运行阶段
FROM openjdk:11-jre-slim
COPY --from=build /target/*.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","app.jar"]
最终镜像仅190MB,节省70%空间。
6.2 日志分割的实用方案
生产环境日志管理建议:
yaml复制# application.yml
logging:
file:
name: /var/log/trade/app.log
max-size: 50MB
max-history: 30
pattern:
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{50} - %msg%n"
配合logrotate每日切割:
code复制/var/log/trade/*.log {
daily
rotate 30
compress
missingok
notifempty
}
7. 扩展功能建议
如果想提升项目竞争力,可以考虑:
- 价格走势分析(使用ECharts可视化)
- 验机报告生成(PDFBox生成PDF)
- 聊天系统(WebSocket实现)
- 推荐算法(基于用户行为的协同过滤)
我在实现验机报告时,发现用Flying Saucer+Thymeleaf生成PDF特别方便:
java复制@GetMapping("/report/{id}")
public void generateReport(@PathVariable Long id, HttpServletResponse response) throws Exception {
PhoneReportVO data = reportService.getData(id);
Context ctx = new Context();
ctx.setVariable("data", data);
String html = templateEngine.process("report_template", ctx);
response.setContentType("application/pdf");
OutputStream os = response.getOutputStream();
ITextRenderer renderer = new ITextRenderer();
renderer.setDocumentFromString(html);
renderer.layout();
renderer.createPDF(os);
os.close();
}
这个项目最让我头疼的是事务管理,特别是"用户付款→卖家发货→确认收货"的分布式事务场景。最终采用的方案是本地消息表+定时任务补偿,核心表结构如下:
sql复制CREATE TABLE `transaction_log` (
`id` varchar(32) NOT NULL,
`business` varchar(50) NOT NULL COMMENT '业务类型',
`foreign_key` bigint NOT NULL COMMENT '外键ID',
`status` tinyint NOT NULL DEFAULT '0' COMMENT '0未处理 1已处理',
`retry_count` int NOT NULL DEFAULT '0',
`create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
调试微信支付回调接口时有个血泪教训:一定要用内网穿透工具测试,我当初在本地调试时因为没配置好Ngrok,白白浪费了两天时间排查假超时问题。推荐使用钉钉的内网穿透工具,配置简单且免费:
bash复制./ding -config=./ding.cfg -subdomain=yourname 8080
