去年接手的一个企业级图书商城项目让我深刻体会到,前后端分离架构在复杂业务场景下的优势。这个基于SpringBoot+Vue的双角色图书商城,不仅需要处理常规的商品展示、订单流程,还要同时满足普通消费者和管理员两种完全不同的操作需求。
技术选型上,后端采用SpringBoot 2.7 + MyBatis-Plus组合,前端使用Vue3 + Element Plus。这种组合在保证开发效率的同时,能很好地支撑高并发场景。项目最大的特色在于通过同一套接口服务,根据JWT令牌中的角色信息动态返回不同的数据结构和操作权限。
采用经典的RESTful API交互模式:
特别设计了API网关层处理跨域和权限过滤,所有请求先经过网关校验令牌有效性,再路由到对应微服务。这种设计使我们可以灵活扩展新的业务模块。
用户体系分为两类角色:
权限控制的关键在于Spring Security的@PreAuthorize注解和自定义的权限拦截器。例如商品删除接口:
java复制@DeleteMapping("/products/{id}")
@PreAuthorize("hasRole('ADMIN')")
public Result deleteProduct(@PathVariable Long id) {
// 实现逻辑
}
前端通过路由守卫动态加载不同组件:
javascript复制router.beforeEach((to, from, next) => {
const role = store.getters.role
if (to.meta.requiresAdmin && role !== 'ADMIN') {
next('/forbidden')
} else {
next()
}
})
商品列表API根据角色返回不同字段:
java复制public ProductVO convertToVO(Product product, String role) {
ProductVO vo = new ProductVO();
vo.setId(product.getId());
vo.setName(product.getName());
if("ADMIN".equals(role)){
vo.setCostPrice(product.getCostPrice());
vo.setStock(product.getStock());
}
return vo;
}
采用Redis缓存购物车数据,关键数据结构设计:
java复制// 购物车Hash结构
cart:userId -> {
"productId1": "quantity",
"productId2": "quantity"
}
// 商品库存缓存
stock:productId -> "100"
下单时使用Lua脚本保证原子性:
lua复制local stock = redis.call('get', KEYS[1])
if tonumber(stock) >= tonumber(ARGV[1]) then
redis.call('decrby', KEYS[1], ARGV[1])
return 1
else
return 0
end
采用双Token机制解决:
刷新流程:
采用分级缓存策略:
配合消息队列实现异步下单:
java复制@RabbitListener(queues = "order.queue")
public void processOrder(OrderMessage message) {
// 数据库库存操作
// 生成订单记录
}
javascript复制const AdminDashboard = () => import('./views/AdminDashboard.vue')
接口请求合并:将商品详情、库存状态、促销信息等并行请求
本地缓存策略:对静态商品信息使用localStorage缓存
nginx复制location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
expires 30d;
}
xml复制<cache eviction="LRU" flushInterval="60000" size="1024"/>
java复制@Scheduled(fixedRate = 3600000)
public void preloadHotProducts() {
// 加载销量前100的商品
}
Docker Compose编排文件示例:
yaml复制version: '3'
services:
frontend:
build: ./frontend
ports:
- "80:80"
backend:
build: ./backend
ports:
- "8080:8080"
redis:
image: redis:alpine
volumes:
- redis_data:/data
关键监控指标:
除了常规的@CrossOrigin注解,还需要注意:
完整解决方案:
java复制@Bean
public CorsFilter corsFilter() {
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
CorsConfiguration config = new CorsConfiguration();
config.setAllowCredentials(true);
config.addAllowedOriginPattern("*");
config.addAllowedHeader("*");
config.addAllowedMethod("*");
source.registerCorsConfiguration("/**", config);
return new CorsFilter(source);
}
需要同时配置:
properties复制spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=20MB
nginx复制client_max_body_size 20m;
javascript复制const instance = axios.create({
baseURL: '/api',
timeout: 30000,
maxContentLength: 20 * 1024 * 1024
})
这个项目的完整代码我已经整理成模块化的结构,每个关键功能点都有详细的实现注释。在实际开发中最深的体会是:前后端分离项目中,清晰的接口文档和类型定义能节省50%以上的沟通成本。我们采用Swagger + TypeScript接口类型生成的方式,极大提升了协作效率。