docs(deployment): 更新部署文档并添加自动化部署脚本

- 更新了 DEPLOYMENT.md 文档,增加了更多部署细节和说明
- 添加了 Linux 和 Windows 平台的自动化部署脚本
- 更新了 README.md,增加了部署相关说明
- 调整了 .env 文件配置,以适应新的部署流程
- 移除了部分不必要的代码和配置
This commit is contained in:
2025-09-10 14:16:27 +08:00
parent 18fe719f94
commit b2d940e014
114 changed files with 6990 additions and 247 deletions

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.jiebanke</groupId>
<artifactId>backend-java</artifactId>
<version>1.0.0</version>
</parent>
<artifactId>travel-service</artifactId>
<packaging>jar</packaging>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Eureka Client -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- OpenFeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- MyBatis Plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 结伴客公共模块 -->
<dependency>
<groupId>com.jiebanke</groupId>
<artifactId>common</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@@ -0,0 +1,19 @@
package com.jiebanke.travel;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@MapperScan("com.jiebanke.travel.mapper")
@ComponentScan(basePackages = "com.jiebanke")
public class TravelApplication {
public static void main(String[] args) {
SpringApplication.run(TravelApplication.class, args);
}
}

View File

@@ -0,0 +1,115 @@
package com.jiebanke.travel.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.jiebanke.common.vo.ApiResponse;
import com.jiebanke.travel.entity.TravelPlan;
import com.jiebanke.travel.service.TravelPlanService;
import com.jiebanke.travel.service.TravelStats;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.Map;
@RestController
@RequestMapping("/api/travel")
public class TravelPlanController {
@Autowired
private TravelPlanService travelPlanService;
/**
* 获取旅行计划列表
*/
@GetMapping("/plans")
public ApiResponse<Map<String, Object>> getTravelPlans(
@RequestHeader("userId") Long userId,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "10") Integer pageSize,
@RequestParam(required = false) String status) {
IPage<TravelPlan> plans = travelPlanService.getTravelPlans(userId, page, pageSize, status);
Map<String, Object> result = new HashMap<>();
result.put("plans", plans.getRecords());
result.put("pagination", Map.of(
"page", plans.getCurrent(),
"pageSize", plans.getSize(),
"total", plans.getTotal(),
"totalPages", plans.getPages()
));
return ApiResponse.success(result);
}
/**
* 获取单个旅行计划详情
*/
@GetMapping("/plans/{planId}")
public ApiResponse<TravelPlan> getTravelPlan(@PathVariable Long planId) {
TravelPlan plan = travelPlanService.getTravelPlanById(planId);
return ApiResponse.success(plan);
}
/**
* 创建旅行计划
*/
@PostMapping("/plans")
public ApiResponse<Map<String, Object>> createTravelPlan(
@RequestHeader("userId") Long userId,
@RequestBody TravelPlan travelPlan) {
Long planId = travelPlanService.createTravelPlan(userId, travelPlan);
TravelPlan plan = travelPlanService.getTravelPlanById(planId);
Map<String, Object> result = new HashMap<>();
result.put("plan", plan);
result.put("message", "旅行计划创建成功");
return ApiResponse.success(result);
}
/**
* 更新旅行计划
*/
@PutMapping("/plans/{planId}")
public ApiResponse<Map<String, Object>> updateTravelPlan(
@PathVariable Long planId,
@RequestHeader("userId") Long userId,
@RequestBody TravelPlan travelPlan) {
TravelPlan updatedPlan = travelPlanService.updateTravelPlan(planId, userId, travelPlan);
Map<String, Object> result = new HashMap<>();
result.put("plan", updatedPlan);
result.put("message", "旅行计划更新成功");
return ApiResponse.success(result);
}
/**
* 删除旅行计划
*/
@DeleteMapping("/plans/{planId}")
public ApiResponse<Map<String, Object>> deleteTravelPlan(
@PathVariable Long planId,
@RequestHeader("userId") Long userId) {
boolean deleted = travelPlanService.deleteTravelPlan(planId, userId);
Map<String, Object> result = new HashMap<>();
result.put("message", "旅行计划删除成功");
result.put("planId", planId);
return ApiResponse.success(result);
}
/**
* 获取用户旅行统计
*/
@GetMapping("/stats")
public ApiResponse<TravelStats> getTravelStats(@RequestHeader("userId") Long userId) {
TravelStats stats = travelPlanService.getUserTravelStats(userId);
return ApiResponse.success(stats);
}
}

View File

@@ -0,0 +1,22 @@
package com.jiebanke.travel.entity;
import com.jiebanke.common.entity.BaseEntity;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.time.LocalDate;
@Data
@EqualsAndHashCode(callSuper = true)
public class TravelPlan extends BaseEntity {
private Long userId;
private String destination;
private LocalDate startDate;
private LocalDate endDate;
private BigDecimal budget;
private String interests;
private String description;
private String visibility;
private String status;
}

View File

@@ -0,0 +1,29 @@
package com.jiebanke.travel.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.jiebanke.travel.entity.TravelPlan;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
@Mapper
public interface TravelPlanMapper extends BaseMapper<TravelPlan> {
/**
* 根据用户ID获取旅行计划列表
* @param userId 用户ID
* @return 旅行计划列表
*/
@Select("SELECT * FROM travel_plans WHERE user_id = #{userId} ORDER BY created_at DESC")
List<TravelPlan> selectByUserId(@Param("userId") Long userId);
/**
* 根据状态获取旅行计划列表
* @param status 状态
* @return 旅行计划列表
*/
@Select("SELECT * FROM travel_plans WHERE status = #{status} ORDER BY created_at DESC")
List<TravelPlan> selectByStatus(@Param("status") String status);
}

View File

@@ -0,0 +1,60 @@
package com.jiebanke.travel.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.jiebanke.travel.entity.TravelPlan;
import java.util.List;
public interface TravelPlanService extends IService<TravelPlan> {
/**
* 获取旅行计划列表
* @param userId 用户ID
* @param page 页码
* @param pageSize 每页数量
* @param status 状态
* @return 旅行计划分页列表
*/
IPage<TravelPlan> getTravelPlans(Long userId, Integer page, Integer pageSize, String status);
/**
* 获取单个旅行计划详情
* @param planId 计划ID
* @return 旅行计划
*/
TravelPlan getTravelPlanById(Long planId);
/**
* 创建旅行计划
* @param userId 用户ID
* @param travelPlan 旅行计划信息
* @return 创建的旅行计划ID
*/
Long createTravelPlan(Long userId, TravelPlan travelPlan);
/**
* 更新旅行计划
* @param planId 计划ID
* @param userId 用户ID
* @param travelPlan 更新的旅行计划信息
* @return 更新后的旅行计划
*/
TravelPlan updateTravelPlan(Long planId, Long userId, TravelPlan travelPlan);
/**
* 删除旅行计划
* @param planId 计划ID
* @param userId 用户ID
* @return 是否删除成功
*/
boolean deleteTravelPlan(Long planId, Long userId);
/**
* 获取用户旅行统计
* @param userId 用户ID
* @return 统计信息
*/
TravelStats getUserTravelStats(Long userId);
}

View File

@@ -0,0 +1,14 @@
package com.jiebanke.travel.service;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class TravelStats {
private Integer totalPlans;
private Integer completedPlans;
private Integer planningPlans;
private Integer cancelledPlans;
private BigDecimal totalBudget;
}

View File

@@ -0,0 +1,148 @@
package com.jiebanke.travel.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jiebanke.common.exception.BusinessException;
import com.jiebanke.travel.entity.TravelPlan;
import com.jiebanke.travel.mapper.TravelPlanMapper;
import com.jiebanke.travel.service.TravelPlanService;
import com.jiebanke.travel.service.TravelStats;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class TravelPlanServiceImpl extends ServiceImpl<TravelPlanMapper, TravelPlan> implements TravelPlanService {
@Override
public IPage<TravelPlan> getTravelPlans(Long userId, Integer page, Integer pageSize, String status) {
QueryWrapper<TravelPlan> queryWrapper = new QueryWrapper<>();
if (userId != null) {
queryWrapper.eq("user_id", userId);
}
if (status != null && !status.isEmpty()) {
queryWrapper.eq("status", status);
}
queryWrapper.orderByDesc("created_at");
Page<TravelPlan> pageObj = new Page<>(page != null ? page : 1, pageSize != null ? pageSize : 10);
return this.page(pageObj, queryWrapper);
}
@Override
public TravelPlan getTravelPlanById(Long planId) {
TravelPlan travelPlan = this.getById(planId);
if (travelPlan == null) {
throw new BusinessException("旅行计划不存在");
}
return travelPlan;
}
@Override
public Long createTravelPlan(Long userId, TravelPlan travelPlan) {
travelPlan.setUserId(userId);
this.save(travelPlan);
return travelPlan.getId();
}
@Override
public TravelPlan updateTravelPlan(Long planId, Long userId, TravelPlan travelPlan) {
TravelPlan existingPlan = this.getById(planId);
if (existingPlan == null) {
throw new BusinessException("旅行计划不存在");
}
if (!existingPlan.getUserId().equals(userId)) {
throw new BusinessException("没有权限修改此旅行计划");
}
// 更新字段
if (travelPlan.getDestination() != null) {
existingPlan.setDestination(travelPlan.getDestination());
}
if (travelPlan.getStartDate() != null) {
existingPlan.setStartDate(travelPlan.getStartDate());
}
if (travelPlan.getEndDate() != null) {
existingPlan.setEndDate(travelPlan.getEndDate());
}
if (travelPlan.getBudget() != null) {
existingPlan.setBudget(travelPlan.getBudget());
}
if (travelPlan.getInterests() != null) {
existingPlan.setInterests(travelPlan.getInterests());
}
if (travelPlan.getDescription() != null) {
existingPlan.setDescription(travelPlan.getDescription());
}
if (travelPlan.getVisibility() != null) {
existingPlan.setVisibility(travelPlan.getVisibility());
}
if (travelPlan.getStatus() != null) {
existingPlan.setStatus(travelPlan.getStatus());
}
this.updateById(existingPlan);
return existingPlan;
}
@Override
public boolean deleteTravelPlan(Long planId, Long userId) {
TravelPlan existingPlan = this.getById(planId);
if (existingPlan == null) {
throw new BusinessException("旅行计划不存在");
}
if (!existingPlan.getUserId().equals(userId)) {
throw new BusinessException("没有权限删除此旅行计划");
}
return this.removeById(planId);
}
@Override
public TravelStats getUserTravelStats(Long userId) {
QueryWrapper<TravelPlan> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id", userId);
List<TravelPlan> plans = this.list(queryWrapper);
TravelStats stats = new TravelStats();
stats.setTotalPlans(plans.size());
int completedPlans = 0;
int planningPlans = 0;
int cancelledPlans = 0;
java.math.BigDecimal totalBudget = java.math.BigDecimal.ZERO;
for (TravelPlan plan : plans) {
switch (plan.getStatus()) {
case "completed":
completedPlans++;
break;
case "planning":
planningPlans++;
break;
case "cancelled":
cancelledPlans++;
break;
}
if (plan.getBudget() != null) {
totalBudget = totalBudget.add(plan.getBudget());
}
}
stats.setCompletedPlans(completedPlans);
stats.setPlanningPlans(planningPlans);
stats.setCancelledPlans(cancelledPlans);
stats.setTotalBudget(totalBudget);
return stats;
}
}

View File

@@ -0,0 +1,32 @@
server:
port: 8083
spring:
application:
name: travel-service
datasource:
url: jdbc:mysql://localhost:3306/jiebanke?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
redis:
host: localhost
port: 6379
database: 0
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
id-type: auto