技术栈:Spring Boot 3.0 + Spring Cloud Alibaba + MyBatis-Plus + Vue 3 + Element Plus + Redis + MySQL 8 + RabbitMQ + Sentinel + ELK
步骤1:初始化后端项目
使用Spring Initializr创建项目,添加以下依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<version>2022.0.0.0-RC2</version>
</dependency>
步骤2:数据库设计与集成
创建MySQL数据库tlias_take_out
,设计以下核心表:
user
(用户信息)shop
(店铺信息)category
(菜品分类)dish
(菜品信息)order
(订单主表)order_detail
(订单详情)配置MyBatis-Plus:
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/tlias_take_out?useSSL=false&serverTimezone=UTC
username: root
password: 123456
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
global-config:
db-config:
id-type: auto
步骤1:JWT令牌集成
添加依赖:
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-impl</artifactId>
<version>0.11.5</version>
</dependency>
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-jackson</artifactId>
<version>0.11.5</version>
</dependency>
步骤2:创建JWT工具类
public class JwtUtil {
private static final String SECRET_KEY = "tlias_take_out_secret_key_2025";
private static final long EXPIRATION_TIME = 1000 * 60 * 60 * 24; // 24小时
public static String generateToken(Map<String, Object> claims) {
return Jwts.builder()
.setClaims(claims)
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION_TIME))
.signWith(SignatureAlgorithm.HS256, SECRET_KEY)
.compact();
}
public static Claims parseToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET_KEY)
.parseClaimsJws(token)
.getBody();
}
}
步骤3:实现拦截器验证令牌
public class JwtInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String token = request.getHeader("Authorization");
if (token != null && token.startsWith("Bearer ")) {
token = token.substring(7);
try {
Claims claims = JwtUtil.parseToken(token);
request.setAttribute("user_id", claims.get("user_id"));
return true;
} catch (Exception e) {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
}
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
}
步骤1:订单创建流程
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderDetailMapper orderDetailMapper;
@Autowired
private RabbitTemplate rabbitTemplate;
@Transactional
@Override
public void createOrder(OrderDTO orderDTO) {
// 1. 验证用户与店铺状态
validateUserAndShop(orderDTO.getUserId(), orderDTO.getShopId());
// 2. 创建订单主表记录
Order order = new Order();
BeanUtils.copyProperties(orderDTO, order);
order.setOrderNumber(generateOrderNumber());
order.setStatus(OrderStatus.PENDING_PAYMENT.getCode());
orderMapper.insert(order);
// 3. 创建订单详情记录
List<OrderDetail> orderDetails = buildOrderDetails(orderDTO.getCartItems(), order.getId());
orderDetailMapper.insertBatch(orderDetails);
// 4. 扣减库存(异步处理)
rabbitTemplate.convertAndSend("stock_queue", orderDTO.getCartItems());
// 5. 发送订单创建消息
rabbitTemplate.convertAndSend("order_create_exchange", "order.create", order.getId());
}
}
步骤2:订单支付处理
@RestController
@RequestMapping("/api/orders")
public class OrderController {
@Autowired
private OrderService orderService;
@PostMapping("/{orderId}/pay")
public Result payOrder(@PathVariable Long orderId, @RequestBody PayDTO payDTO) {
// 1. 验证支付参数
validatePayParams(payDTO);
// 2. 调用支付服务进行支付
PaymentResult paymentResult = paymentService.processPayment(payDTO);
// 3. 更新订单状态
if (paymentResult.isSuccess()) {
orderService.updateOrderStatus(orderId, OrderStatus.PAID.getCode());
return Result.success("支付成功");
} else {
return Result.error("支付失败: " + paymentResult.getMessage());
}
}
}
步骤1:Redis缓存集成
配置Redis连接:
spring:
redis:
host: localhost
port: 6379
password:
database: 0
步骤2:缓存商品信息
@Service
public class DishServiceImpl implements DishService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private DishMapper dishMapper;
@Override
public Dish getDishById(Long id) {
String key = "dish:" + id;
Dish dish = (Dish) redisTemplate.opsForValue().get(key);
if (dish == null) {
dish = dishMapper.selectById(id);
if (dish != null) {
redisTemplate.opsForValue().set(key, dish, 30, TimeUnit.MINUTES);
}
}
return dish;
}
@Override
@CacheEvict(value = "dish", key = "#dish.id")
public void updateDish(Dish dish) {
dishMapper.updateById(dish);
}
}
步骤3:Seata分布式事务配置
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-seata</artifactId>
<version>2022.0.0.0-RC2</version>
</dependency>
@Configuration
public class DataSourceProxyConfig {
@Bean
public DataSourceProxy dataSourceProxy(DataSource dataSource) {
return new DataSourceProxy(dataSource);
}
}
步骤1:创建商品列表组件
<template>
<div class="dish-list">
<el-row :gutter="20">
<el-col :span="6" v-for="dish in dishList" :key="dish.id">
<el-card shadow="hover">
<template #header>
<div class="card-header">
<span>{{ dish.name }}</span>
<el-badge :value="dish.sales" class="ml-2"></el-badge>
</div>
</template>
<img :src="dish.imageUrl" alt="菜品图片" class="dish-image">
<div class="card-content">
<div class="price">¥{{ dish.price }}</div>
<el-button type="primary" @click="addToCart(dish)">加入购物车</el-button>
</div>
</el-card>
</el-col>
</el-row>
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 30]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { useRouter } from 'vue-router';
const router = useRouter();
const dishList = ref([]);
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(0);
const fetchDishList = async () => {
try {
const res = await axios.get('/api/dishes', {
params: {
page: currentPage.value,
pageSize: pageSize.value
}
});
dishList.value = res.data.records;
total.value = res.data.total;
} catch (error) {
ElMessage.error('获取菜品列表失败');
}
};
const addToCart = (dish) => {
// 添加到购物车逻辑
};
onMounted(() => {
fetchDishList();
});
</script>
步骤1:集成Sentinel限流
添加依赖:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2022.0.0.0-RC2</version>
</dependency>
配置限流规则:
@Configuration
public class SentinelConfig {
@PostConstruct
public void initRules() {
List<FlowRule> rules = new ArrayList<>();
FlowRule rule = new FlowRule();
rule.setResource("getOrderById");
rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
rule.setCount(20); // 每秒最多20次请求
rules.add(rule);
FlowRuleManager.loadRules(rules);
}
}
步骤2:ELK日志收集
配置Logstash收集日志:
input {
file {
path => "/var/log/tlias-take-out/*.log"
start_position => "beginning"
}
}
filter {
json {
source => "message"
}
}
output {
elasticsearch {
hosts => ["localhost:9200"]
index => "tlias-take-out-%{+YYYY.MM.dd}"
}
}
步骤1:Docker化部署
创建Dockerfile:
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/tlias-take-out.jar ./
EXPOSE 8080
CMD ["java", "-jar", "tlias-take-out.jar"]
步骤2:使用Docker Compose编排服务
version: '3'
services:
mysql:
image: mysql:8.0
volumes:
- mysql-data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: tlias_take_out
ports:
- "3306:3306"
redis:
image: redis:7.0
ports:
- "6379:6379"
rabbitmq:
image: rabbitmq:3.11-management
ports:
- "5672:5672"
- "15672:15672"
backend:
build: .
ports:
- "8080:8080"
depends_on:
- mysql
- redis
- rabbitmq
volumes:
mysql-data:
通过以上步骤,你可以完成一个完整的JavaWeb项目开发,涵盖了从后端架构到前端展示的全过程,同时实现了缓存优化、分布式事务、限流熔断等高级特性。
Java Web, 苍穹外卖项目,综合案例,实操指南,Java 开发,Web 项目实战,外卖系统开发,Java 案例教程,项目实操步骤,Java Web 实战,长尾关键词解析,教程编程,Java 项目开发,Web 开发指南,外Web 开发指南卖系统案例
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。