1. 项目概述与技术栈选型
这个汉服租赁网站系统采用了当前Java Web开发中最主流的全栈技术组合:SpringBoot2+Vue3+MyBatis-Plus+MySQL8.0。这套技术栈的选择并非偶然,而是经过实际项目验证的黄金组合。
SpringBoot2作为后端框架,其自动配置和起步依赖的特性大幅简化了传统SSM框架的配置复杂度。实测中,一个基础API接口的开发时间可以从原来的30分钟缩短到5分钟。特别值得一提的是2.x版本对响应式编程的增强,虽然本项目没有用到WebFlux,但内置的Tomcat容器性能优化确实让我们的压力测试结果提升了15%左右。
前端选用Vue3而非React或Angular,主要基于三点考虑:首先,组合式API更适合处理汉服租赁这类多状态交互场景;其次,与Element Plus的完美整合让后台管理系统开发效率提升明显;最后,Vue3的打包体积比Vue2小了约40%,这对需要展示大量汉服图片的网站至关重要。
MyBatis-Plus的选择则彻底解决了传统MyBatis的样板代码问题。它的Lambda查询特别适合汉服的多条件筛选场景,比如这样的查询构建:
java复制LambdaQueryWrapper<Hanfu> query = new LambdaQueryWrapper<>();
query.eq(Hanfu::getStatus, 1)
.between(Hanfu::getPrice, minPrice, maxPrice)
.like(StringUtils.isNotBlank(keyword), Hanfu::getName, keyword);
MySQL8.0相较于5.7版本,其JSON字段支持和窗口函数在汉服库存管理、租赁统计等场景表现出色。我们实测发现,同样的复杂报表查询,8.0版本比5.7快2-3倍。特别是CTE(公共表表达式)语法,让我们的租赁流水分析SQL可读性大幅提升。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统核心模块设计
2.1 汉服租赁业务流程建模
汉服租赁不同于普通电商,有其特殊的业务规则。我们设计了状态机驱动的租赁流程:
mermaid复制stateDiagram-v2
[*] --> 待租赁
待租赁 --> 已预订: 用户支付定金
已预订 --> 租赁中: 用户支付尾款
租赁中 --> 待归还: 到达租赁开始日期
待归还 --> 已归还: 用户归还汉服
已归还 --> 待清洁: 检查无损坏
待清洁 --> 待租赁: 清洁消毒完成
这个状态转换通过Spring StateMachine实现,每个状态变更都会触发相应的事件:
- 预订成功发送短信通知
- 尾款逾期自动取消订单
- 归还超时计算违约金
2.2 多规格库存管理
汉服的特殊性在于同一款式可能有多个规格(颜色、尺码),我们采用SKU+库存池的方案:
java复制public class HanfuSku {
private Long id;
private Long spuId; // 商品ID
private String color;
private String size;
private Integer stock;
@TableField(exist = false)
private List<String> gallery; // 展示图集
}
库存扣减使用MySQL乐观锁保证原子性:
sql复制UPDATE hanfu_sku
SET stock = stock - 1
WHERE id = ? AND stock >= ?
2.3 定时任务设计
汉服租赁有多个需要定时处理的场景:
- 每日凌晨检查逾期未归还订单
- 每30分钟同步ES索引
- 每周生成清洁消毒排班表
我们采用Spring Scheduled + Redis分布式锁的方案:
java复制@Scheduled(cron = "0 0 2 * * ?")
public void checkOverdueOrders() {
String lockKey = "task:checkOverdue";
try {
if (redisLock.tryLock(lockKey, 10, TimeUnit.SECONDS)) {
// 业务逻辑
}
} finally {
redisLock.unlock(lockKey);
}
}
3. 关键技术实现细节
3.1 Vue3前端工程化实践
前端采用Vite4构建,相比Webpack冷启动时间从8s降到1s内。几个关键配置:
javascript复制// vite.config.js
export default defineConfig({
plugins: [
vue(),
// 汉服图片压缩配置
viteImagemin({
gifsicle: { interlaced: false },
webp: { quality: 75 }
})
],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'comps': path.resolve(__dirname, './src/components')
}
}
})
对于汉服展示这类图片密集型页面,我们实现了:
- 懒加载:使用IntersectionObserver API
- WebP格式自动降级兼容
- 滑动加载更多(避免分页打断浏览体验)
3.2 MyBatis-Plus高级应用
3.2.1 多租户解决方案
系统需要支持多高校使用,我们通过TenantLineInnerInterceptor实现:
java复制public class MyTenantLineHandler implements TenantLineHandler {
@Override
public String getTenantIdColumn() {
return "university_id";
}
@Override
public Expression getTenantId() {
return new LongValue(1); // 从上下文中获取
}
}
3.2.2 逻辑删除优化
汉服商品需要支持逻辑删除但保留历史订单关联:
java复制@TableLogic(value = "0", delval = "1")
private Integer deleted;
查询时自动过滤已删除记录,特殊场景用:
java复制wrapper.apply("deleted = 1"); // 查询已删除
3.3 SpringBoot性能调优
针对高并发场景的几个关键配置:
yaml复制server:
tomcat:
max-threads: 200
min-spare-threads: 10
connection-timeout: 5000
compression:
enabled: true
mime-types: application/json,text/html
spring:
redis:
lettuce:
pool:
max-active: 16
max-idle: 8
JVM参数优化(针对4核8G服务器):
code复制-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-Xms2048m
-Xmx2048m
-XX:MetaspaceSize=256m
4. 典型业务场景实现
4.1 汉服预约冲突检测
核心算法:检查时间区间重叠
java复制public boolean checkRentConflict(Long hanfuId, LocalDate startDate, LocalDate endDate) {
return mapper.exists(new LambdaQueryWrapper<Order>()
.eq(Order::getHanfuId, hanfuId)
.eq(Order::getStatus, OrderStatus.RENTING.getValue())
.and(w -> w
.between(Order::getStartDate, startDate, endDate)
.or()
.between(Order::getEndDate, startDate, endDate)
.or()
.le(Order::getStartDate, startDate)
.ge(Order::getEndDate, endDate)
));
}
4.2 押金分段支付
支持定金+尾款模式:
java复制public PaymentResult payDeposit(Long orderId) {
Order order = getById(orderId);
if (order.getDepositPaid()) {
throw new BusinessException("定金已支付");
}
// 创建支付记录
Payment payment = new Payment();
payment.setOrderId(orderId);
payment.setAmount(order.getDepositAmount());
payment.setType(PaymentType.DEPOSIT);
paymentMapper.insert(payment);
// 调用支付网关
return paymentGateway.createPayment(payment);
}
4.3 汉服清洁状态跟踪
使用状态模式实现:
java复制public interface CleanState {
void handle(CleanContext context);
}
@Component
public class DirtyState implements CleanState {
@Override
public void handle(CleanContext context) {
context.setCleanState(new CleaningState());
// 发送到清洁队列
mqSender.sendCleanTask(context.getHanfuId());
}
}
5. 部署与运维方案
5.1 多环境配置管理
使用Spring Profiles + Maven过滤:
xml复制<profiles>
<profile>
<id>dev</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<env>dev</env>
</properties>
</profile>
</profiles>
对应application-dev.yml:
yaml复制spring:
datasource:
url: jdbc:mysql://localhost:3306/hanfu_dev
5.2 Docker化部署
后端Dockerfile示例:
dockerfile复制FROM openjdk:11-jre
COPY target/hanfu-rental.jar /app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
使用docker-compose编排:
yaml复制version: '3'
services:
mysql:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: root
volumes:
- ./mysql/data:/var/lib/mysql
redis:
image: redis:6
ports:
- "6379:6379"
5.3 监控方案
接入Prometheus + Grafana:
java复制@Bean
public MeterRegistryCustomizer<PrometheusMeterRegistry> configureMetrics() {
return registry -> registry.config().commonTags("application", "hanfu-rental");
}
关键监控指标:
- 汉服库存余量
- 订单创建QPS
- 平均租赁时长
- 支付成功率
6. 开发中的典型问题解决
6.1 汉服图片上传优化
初始方案直接上传原图导致:
- 移动端上传慢
- 服务器存储压力大
- 前端加载卡顿
最终解决方案:
- 前端使用canvas压缩(保留EXIF信息)
javascript复制function compressImage(file, quality = 0.8) {
return new Promise((resolve) => {
const reader = new FileReader();
reader.onload = (e) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
// 保持宽高比
const maxSize = 1920;
let width = img.width;
let height = img.height;
if (width > height && width > maxSize) {
height *= maxSize / width;
width = maxSize;
} else if (height > maxSize) {
width *= maxSize / height;
height = maxSize;
}
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob(resolve, 'image/jpeg', quality);
};
img.src = e.target.result;
};
reader.readAsDataURL(file);
});
}
- 服务端使用Thumbnailator二次处理
java复制Thumbnails.of(inputStream)
.size(800, 800)
.outputFormat("jpg")
.outputQuality(0.7)
.toOutputStream(outputStream);
6.2 租赁时间计算精度问题
初始直接使用LocalDate导致:
- 跨天计算误差
- 无法区分上下午
改进方案:
java复制public class RentalPeriod {
private LocalDateTime startTime;
private LocalDateTime endTime;
public long getDays() {
return ChronoUnit.HOURS.between(startTime, endTime) / 24f + 1;
}
}
6.3 缓存穿透防护
汉服详情页面临缓存击穿风险,解决方案:
- 空值缓存
java复制public Hanfu getById(Long id) {
String cacheKey = "hanfu:" + id;
Hanfu hanfu = redisTemplate.opsForValue().get(cacheKey);
if (hanfu == null) {
hanfu = hanfuMapper.selectById(id);
if (hanfu == null) {
// 缓存空值5分钟
redisTemplate.opsForValue().set(cacheKey, new Hanfu(), 5, TimeUnit.MINUTES);
return null;
}
redisTemplate.opsForValue().set(cacheKey, hanfu, 1, TimeUnit.DAYS);
}
return hanfu;
}
- 布隆过滤器预热
java复制@PostConstruct
public void initBloomFilter() {
List<Long> allIds = hanfuMapper.getAllIds();
for (Long id : allIds) {
bloomFilter.put(id);
}
}
7. 项目文档体系
7.1 接口文档
使用Swagger3 + OpenAPI3规范:
java复制@Operation(summary = "汉服列表查询")
@GetMapping("/list")
public R<List<HanfuVO>> list(
@Parameter(description = "汉服名称关键字") String keyword,
@Parameter(description = "最小价格") BigDecimal minPrice,
@Parameter(description = "最大价格") BigDecimal maxPrice) {
// ...
}
生成的文档包含:
- 接口调用示例
- 参数约束说明
- 响应数据结构
- 错误码对照表
7.2 数据库文档
使用PDM逆向工程生成:
- 表结构说明
- 字段注释
- 索引设计
- ER关系图
7.3 部署手册
详细包含:
- 服务器最低配置要求
- 依赖软件安装步骤
- 应用启动参数说明
- 健康检查端点
- 常见问题排查
8. 扩展性设计
8.1 多高校适配方案
通过租户隔离实现:
java复制public class TenantContext {
private static final ThreadLocal<Long> CURRENT_TENANT = new ThreadLocal<>();
public static void setTenantId(Long tenantId) {
CURRENT_TENANT.set(tenantId);
}
public static Long getTenantId() {
return CURRENT_TENANT.get();
}
}
8.2 支付渠道扩展
支付策略工厂:
java复制public interface PaymentStrategy {
PaymentResult pay(PaymentRequest request);
}
@Service
public class PaymentService {
private final Map<String, PaymentStrategy> strategies;
public PaymentResult pay(String channel, PaymentRequest request) {
return strategies.get(channel).pay(request);
}
}
8.3 消息通知扩展
基于事件总线的设计:
java复制@EventListener
public void handleOrderEvent(OrderEvent event) {
switch (event.getType()) {
case CREATED:
smsService.send(event.getOrder().getMobile(), "订单创建成功");
break;
case PAID:
emailService.send(event.getOrder().getEmail(), "支付成功通知");
break;
}
}
9. 安全防护措施
9.1 防XSS攻击
前端使用DOMPurify过滤:
javascript复制import DOMPurify from 'dompurify';
const clean = DOMPurify.sanitize(dirtyHtml);
后端Jackson配置:
java复制@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
return new Jackson2ObjectMapperBuilder()
.defaultHtmlEscaper(true);
}
9.2 数据脱敏
使用注解实现:
java复制@SensitiveData
public class User {
@SensitiveField(type = SensitiveType.MOBILE)
private String mobile;
@SensitiveField(type = SensitiveType.ID_CARD)
private String idCard;
}
9.3 接口防刷
Redis计数器方案:
java复制public boolean checkRequestFrequency(String ip, String api, int limit, int period) {
String key = "rate:" + ip + ":" + api;
Long count = redisTemplate.opsForValue().increment(key);
if (count == 1) {
redisTemplate.expire(key, period, TimeUnit.SECONDS);
}
return count <= limit;
}
10. 性能优化实践
10.1 汉服列表缓存设计
多级缓存策略:
- 本地Caffeine缓存(50ms)
- Redis集群缓存(200ms)
- 数据库查询(500ms+)
实现方案:
java复制@Cacheable(value = "hanfu", key = "#root.methodName + ':' + #page + ':' + #size")
public List<Hanfu> listHanfu(int page, int size) {
Page<Hanfu> page = new Page<>(page, size);
return hanfuMapper.selectPage(page, null).getRecords();
}
10.2 SQL优化案例
原始查询:
sql复制SELECT * FROM hanfu
WHERE status = 1
ORDER BY create_time DESC
LIMIT 10
优化后:
sql复制SELECT h.* FROM hanfu h
JOIN (
SELECT id FROM hanfu
WHERE status = 1
ORDER BY create_time DESC
LIMIT 10
) tmp ON h.id = tmp.id
10.3 前端性能优化
- 组件懒加载:
javascript复制const HanfuDetail = defineAsyncComponent(() =>
import('./views/HanfuDetail.vue')
)
- 路由级别代码分割:
javascript复制{
path: '/detail/:id',
component: () => import('@/views/HanfuDetail.vue')
}
- 关键CSS内联:
html复制<style>
/* 首屏关键样式 */
</style>
<link rel="stylesheet" href="non-critical.css" media="print" onload="this.media='all'">
11. 测试策略
11.1 单元测试重点
- 价格计算服务:
java复制@Test
public void testCalculateRentalFee() {
RentalCalculator calculator = new RentalCalculator();
BigDecimal fee = calculator.calculate(
new BigDecimal("100"),
LocalDate.now(),
LocalDate.now().plusDays(3));
assertEquals(new BigDecimal("300"), fee);
}
- 状态转换测试:
java复制@Test
public void testOrderStateTransition() {
Order order = new Order();
order.setState(OrderState.NEW);
order.payDeposit();
assertEquals(OrderState.DEPOSIT_PAID, order.getState());
}
11.2 集成测试方案
使用Testcontainers:
java复制@Testcontainers
class OrderServiceIT {
@Container
static MySQLContainer<?> mysql = new MySQLContainer<>("mysql:8.0");
@BeforeAll
static void setup() {
System.setProperty("spring.datasource.url", mysql.getJdbcUrl());
}
}
11.3 压力测试要点
使用JMeter测试:
- 汉服搜索接口:1000并发
- 下单接口:500并发(带分布式锁)
- 支付回调:200并发(幂等处理)
关键指标:
- 99%线 < 1s
- 错误率 < 0.1%
- 吞吐量 > 500TPS
12. 项目演进路线
12.1 短期优化计划
-
汉服推荐算法:
- 基于用户浏览历史
- 协同过滤
- 季节热点推荐
-
智能客服集成:
- 常见问题自动回复
- 工单系统对接
- 人工客服转接
12.2 中期扩展方向
-
汉服社区功能:
- 用户晒单
- 穿搭分享
- 话题讨论
-
租赁保险服务:
- 意外损坏险
- 清洁保障险
- 第三方保险对接
12.3 长期技术规划
-
3D汉服试穿:
- WebGL渲染
- 体型适配算法
- AR实时预览
-
区块链存证:
- 租赁记录上链
- 汉服正品认证
- 数字藏品发行
13. 开发环境配置
13.1 IDE推荐配置
IntelliJ IDEA优化:
- 安装插件:
- MyBatisX
- Vue.js
- GitToolBox
- 调优参数:
properties复制idea.max.intellisense.filesize=5000 idea.cycle.buffer.size=1024
13.2 代码规范检查
结合CheckStyle + Git Hooks:
xml复制<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<configuration>
<configLocation>google_checks.xml</configLocation>
</configuration>
</plugin>
13.3 本地调试技巧
- 前端Mock方案:
javascript复制// vite.config.js
plugins: [
viteMockServe({
mockPath: 'mock',
localEnabled: true
})
]
- 后端接口热更新:
properties复制spring.devtools.restart.enabled=true
14. 团队协作规范
14.1 Git分支策略
采用Git Flow变种:
- master:生产环境代码
- release:预发布分支
- feature/xxx:功能开发分支
- hotfix/xxx:紧急修复分支
14.2 Code Review要点
重点关注:
- 汉服库存变更的原子性
- 时间计算的边界条件
- 支付相关操作的幂等性
- 敏感数据的安全处理
14.3 文档编写标准
- 接口变更记录
- 数据库变更脚本
- 部署步骤更新
- 已知问题说明
15. 项目经验总结
15.1 技术决策反思
-
正确决策:
- 选用MyBatis-Plus大幅提升DAO层效率
- 采用Vue3组合式API更好管理租赁状态
- MySQL8.0的窗口函数简化了报表开发
-
待改进点:
- 初期低估了图片处理复杂度
- 订单状态机第一版设计不够灵活
- 缓存策略初期过于简单
15.2 性能优化心得
-
必须建立基准:
- 使用Arthas进行方法级耗时分析
- 生产环境全链路追踪
- 压力测试要模拟真实场景
-
优化要有针对性:
- 80%性能问题集中在20%代码
- 先测量再优化
- 避免过度优化
15.3 团队协作建议
-
领域驱动设计:
- 明确汉服租赁业务边界
- 统一领域术语
- 划分清晰的上下文
-
小步快跑:
- 两周一个迭代周期
- 每个迭代交付可用功能
- 持续集成部署
