删除动物服务相关代码及配置文件
This commit is contained in:
@@ -1,326 +0,0 @@
|
||||
# 结伴客Java后端性能优化指南
|
||||
|
||||
## 1. JVM调优
|
||||
|
||||
### 1.1 堆内存设置
|
||||
```
|
||||
# 堆内存大小设置(根据服务器配置调整)
|
||||
-Xms512m
|
||||
-Xmx2g
|
||||
|
||||
# 新生代大小设置
|
||||
-Xmn256m
|
||||
|
||||
# Metaspace大小设置
|
||||
-XX:MetaspaceSize=256m
|
||||
-XX:MaxMetaspaceSize=512m
|
||||
```
|
||||
|
||||
### 1.2 垃圾回收器选择
|
||||
```
|
||||
# G1垃圾回收器(适用于大堆内存)
|
||||
-XX:+UseG1GC
|
||||
-XX:MaxGCPauseMillis=200
|
||||
-XX:G1HeapRegionSize=16m
|
||||
|
||||
# 或者CMS垃圾回收器(适用于低延迟要求)
|
||||
-XX:+UseConcMarkSweepGC
|
||||
-XX:+UseCMSInitiatingOccupancyOnly
|
||||
-XX:CMSInitiatingOccupancyFraction=70
|
||||
```
|
||||
|
||||
## 2. 数据库连接池优化
|
||||
|
||||
### 2.1 HikariCP配置(在application.yml中)
|
||||
```yaml
|
||||
spring:
|
||||
datasource:
|
||||
hikari:
|
||||
# 连接池大小
|
||||
maximum-pool-size: 20
|
||||
# 最小空闲连接数
|
||||
minimum-idle: 5
|
||||
# 连接超时时间
|
||||
connection-timeout: 30000
|
||||
# 空闲超时时间
|
||||
idle-timeout: 600000
|
||||
# 最大生命周期
|
||||
max-lifetime: 1800000
|
||||
# 连接测试查询
|
||||
connection-test-query: SELECT 1
|
||||
```
|
||||
|
||||
## 3. Redis性能优化
|
||||
|
||||
### 3.1 Redis连接池配置
|
||||
```yaml
|
||||
spring:
|
||||
redis:
|
||||
lettuce:
|
||||
pool:
|
||||
# 最大连接数
|
||||
max-active: 20
|
||||
# 最大空闲连接数
|
||||
max-idle: 10
|
||||
# 最小空闲连接数
|
||||
min-idle: 5
|
||||
# 获取连接最大等待时间
|
||||
max-wait: 2000ms
|
||||
```
|
||||
|
||||
### 3.2 Redis序列化优化
|
||||
```java
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
|
||||
// 使用更高效的序列化方式
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
|
||||
template.setHashKeySerializer(new StringRedisSerializer());
|
||||
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
|
||||
|
||||
return template;
|
||||
}
|
||||
```
|
||||
|
||||
## 4. RabbitMQ性能优化
|
||||
|
||||
### 4.1 连接池配置
|
||||
```yaml
|
||||
spring:
|
||||
rabbitmq:
|
||||
listener:
|
||||
simple:
|
||||
# 并发消费者数量
|
||||
concurrency: 5
|
||||
# 最大并发消费者数量
|
||||
max-concurrency: 20
|
||||
# 每个消费者预取的消息数量
|
||||
prefetch: 10
|
||||
```
|
||||
|
||||
## 5. Feign客户端优化
|
||||
|
||||
### 5.1 Feign配置
|
||||
```java
|
||||
@Configuration
|
||||
public class FeignConfig {
|
||||
|
||||
@Bean
|
||||
public Request.Options options() {
|
||||
// 连接超时时间和读取超时时间
|
||||
return new Request.Options(5000, 10000);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Retryer retryer() {
|
||||
// 重试策略
|
||||
return new Retryer.Default(1000, 2000, 3);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 6. 线程池优化
|
||||
|
||||
### 6.1 自定义线程池
|
||||
```java
|
||||
@Configuration
|
||||
public class ThreadPoolConfig {
|
||||
|
||||
@Bean("taskExecutor")
|
||||
public ExecutorService taskExecutor() {
|
||||
return new ThreadPoolExecutor(
|
||||
10, // 核心线程数
|
||||
20, // 最大线程数
|
||||
60L, // 空闲线程存活时间
|
||||
TimeUnit.SECONDS,
|
||||
new LinkedBlockingQueue<>(100), // 任务队列
|
||||
new ThreadFactoryBuilder().setNameFormat("task-pool-%d").build(),
|
||||
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 7. 缓存策略优化
|
||||
|
||||
### 7.1 多级缓存设计
|
||||
```java
|
||||
@Service
|
||||
public class UserService {
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
// 本地缓存(Caffeine)
|
||||
private Cache<String, Object> localCache = Caffeine.newBuilder()
|
||||
.maximumSize(1000)
|
||||
.expireAfterWrite(10, TimeUnit.MINUTES)
|
||||
.build();
|
||||
|
||||
public User getUserById(Long userId) {
|
||||
// 1. 先查本地缓存
|
||||
User user = (User) localCache.getIfPresent("user:" + userId);
|
||||
if (user != null) {
|
||||
return user;
|
||||
}
|
||||
|
||||
// 2. 再查Redis缓存
|
||||
user = (User) redisTemplate.opsForValue().get("user:" + userId);
|
||||
if (user != null) {
|
||||
localCache.put("user:" + userId, user);
|
||||
return user;
|
||||
}
|
||||
|
||||
// 3. 最后查数据库
|
||||
user = userMapper.selectById(userId);
|
||||
if (user != null) {
|
||||
redisTemplate.opsForValue().set("user:" + userId, user, 30, TimeUnit.MINUTES);
|
||||
localCache.put("user:" + userId, user);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 8. 数据库查询优化
|
||||
|
||||
### 8.1 MyBatis-Plus分页优化
|
||||
```java
|
||||
@Configuration
|
||||
public class MybatisPlusConfig {
|
||||
|
||||
@Bean
|
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() {
|
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
|
||||
// 分页插件
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
|
||||
// 乐观锁插件
|
||||
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
|
||||
return interceptor;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 8.2 索引优化建议
|
||||
```sql
|
||||
-- 用户表索引优化
|
||||
CREATE INDEX idx_user_phone ON users(phone);
|
||||
CREATE INDEX idx_user_email ON users(email);
|
||||
CREATE INDEX idx_user_status ON users(status);
|
||||
|
||||
-- 旅行表索引优化
|
||||
CREATE INDEX idx_travel_creator ON travels(creator_id);
|
||||
CREATE INDEX idx_travel_status ON travels(status);
|
||||
CREATE INDEX idx_travel_start_time ON travels(start_time);
|
||||
|
||||
-- 动物表索引优化
|
||||
CREATE INDEX idx_animal_shelter ON animals(shelter_id);
|
||||
CREATE INDEX idx_animal_status ON animals(status);
|
||||
|
||||
-- 订单表索引优化
|
||||
CREATE INDEX idx_order_user ON orders(user_id);
|
||||
CREATE INDEX idx_order_status ON orders(status);
|
||||
CREATE INDEX idx_order_create_time ON orders(create_time);
|
||||
```
|
||||
|
||||
## 9. API网关优化
|
||||
|
||||
### 9.1 限流配置
|
||||
```yaml
|
||||
spring:
|
||||
cloud:
|
||||
gateway:
|
||||
routes:
|
||||
- id: user-service
|
||||
uri: lb://user-service
|
||||
predicates:
|
||||
- Path=/api/users/**
|
||||
filters:
|
||||
- name: RequestRateLimiter
|
||||
args:
|
||||
redis-rate-limiter.replenishRate: 10
|
||||
redis-rate-limiter.burstCapacity: 20
|
||||
```
|
||||
|
||||
## 10. 监控和日志优化
|
||||
|
||||
### 10.1 Actuator配置
|
||||
```yaml
|
||||
management:
|
||||
endpoints:
|
||||
web:
|
||||
exposure:
|
||||
include: health,info,metrics,httptrace
|
||||
endpoint:
|
||||
health:
|
||||
show-details: always
|
||||
```
|
||||
|
||||
### 10.2 日志配置优化
|
||||
```yaml
|
||||
logging:
|
||||
level:
|
||||
com.jiebanke: INFO
|
||||
org.springframework: WARN
|
||||
org.mybatis: WARN
|
||||
pattern:
|
||||
console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
|
||||
file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"
|
||||
file:
|
||||
name: logs/application.log
|
||||
```
|
||||
|
||||
## 11. Docker部署优化
|
||||
|
||||
### 11.1 JVM参数优化(Dockerfile)
|
||||
```dockerfile
|
||||
FROM openjdk:17-jdk-slim
|
||||
|
||||
LABEL maintainer="jiebanke-team"
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY target/*.jar app.jar
|
||||
|
||||
# JVM参数优化
|
||||
ENV JAVA_OPTS="-Xms512m -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200"
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
|
||||
```
|
||||
|
||||
## 12. 负载均衡优化
|
||||
|
||||
### 12.1 Ribbon配置
|
||||
```yaml
|
||||
ribbon:
|
||||
# 连接超时时间
|
||||
ConnectTimeout: 3000
|
||||
# 读取超时时间
|
||||
ReadTimeout: 10000
|
||||
# 是否启用重试
|
||||
OkToRetryOnAllOperations: false
|
||||
# 切换实例重试次数
|
||||
MaxAutoRetriesNextServer: 1
|
||||
# 当前实例重试次数
|
||||
MaxAutoRetries: 0
|
||||
```
|
||||
|
||||
## 13. 性能测试建议
|
||||
|
||||
### 13.1 压力测试工具
|
||||
- JMeter:用于API接口压力测试
|
||||
- wrk:用于HTTP基准测试
|
||||
- ab (Apache Bench):简单的HTTP性能测试工具
|
||||
|
||||
### 13.2 监控工具
|
||||
- Prometheus + Grafana:系统指标监控
|
||||
- ELK Stack:日志分析
|
||||
- SkyWalking:分布式追踪
|
||||
|
||||
通过以上优化措施,可以显著提升结伴客Java后端服务的性能和稳定性。
|
||||
@@ -1,159 +0,0 @@
|
||||
# 结伴客Java后端
|
||||
|
||||
结伴客Java微服务架构后端系统,基于Spring Boot和Spring Cloud实现。
|
||||
|
||||
## 系统架构
|
||||
|
||||
- **服务注册与发现**: Eureka Server
|
||||
- **API网关**: Spring Cloud Gateway
|
||||
- **认证服务**: Auth Service
|
||||
- **用户服务**: User Service
|
||||
- **旅行服务**: Travel Service
|
||||
- **动物服务**: Animal Service
|
||||
- **订单服务**: Order Service
|
||||
- **推广服务**: Promotion Service
|
||||
|
||||
## 环境要求
|
||||
|
||||
- **JDK**: Java 17
|
||||
- **构建工具**: Maven 3.6+
|
||||
- **数据库**: MySQL 8.0+
|
||||
- **缓存**: Redis 6.0+
|
||||
- **消息队列**: RabbitMQ 3.8+
|
||||
|
||||
## 环境安装
|
||||
|
||||
### 1. 安装Java 17
|
||||
|
||||
#### macOS (使用Homebrew)
|
||||
```bash
|
||||
brew install openjdk@17
|
||||
```
|
||||
|
||||
#### Ubuntu/Debian
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install openjdk-17-jdk
|
||||
```
|
||||
|
||||
#### CentOS/RHEL
|
||||
```bash
|
||||
sudo yum install java-17-openjdk-devel
|
||||
```
|
||||
|
||||
### 2. 安装Maven
|
||||
|
||||
#### macOS (使用Homebrew)
|
||||
```bash
|
||||
brew install maven
|
||||
```
|
||||
|
||||
#### Ubuntu/Debian
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install maven
|
||||
```
|
||||
|
||||
#### CentOS/RHEL
|
||||
```bash
|
||||
sudo yum install maven
|
||||
```
|
||||
|
||||
### 3. 验证安装
|
||||
|
||||
```bash
|
||||
java -version
|
||||
mvn -version
|
||||
```
|
||||
|
||||
应该看到类似以下输出:
|
||||
```
|
||||
openjdk version "17.0.8" 2023-07-18
|
||||
Apache Maven 3.8.6
|
||||
```
|
||||
|
||||
## 数据库配置
|
||||
|
||||
1. 安装MySQL 8.0+
|
||||
2. 创建数据库:
|
||||
```sql
|
||||
CREATE DATABASE jiebanke CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
```
|
||||
3. 执行初始化脚本:
|
||||
```bash
|
||||
mysql -u root -p jiebanke < scripts/init-database.sql
|
||||
```
|
||||
|
||||
## 缓存和消息队列
|
||||
|
||||
1. 安装Redis 6.0+
|
||||
2. 安装RabbitMQ 3.8+
|
||||
|
||||
## 构建项目
|
||||
|
||||
```bash
|
||||
# 进入项目目录
|
||||
cd backend-java
|
||||
|
||||
# 清理并构建项目
|
||||
./build-services.sh
|
||||
```
|
||||
|
||||
## 运行服务
|
||||
|
||||
### 方式一:使用启动脚本(推荐)
|
||||
|
||||
```bash
|
||||
# 进入项目目录
|
||||
cd backend-java
|
||||
|
||||
# 启动所有服务
|
||||
./start-services.sh
|
||||
|
||||
# 停止所有服务
|
||||
./stop-services.sh
|
||||
```
|
||||
|
||||
### 方式二:手动启动
|
||||
|
||||
1. 启动Eureka Server:
|
||||
```bash
|
||||
cd eureka-server
|
||||
mvn spring-boot:run
|
||||
```
|
||||
|
||||
2. 等待Eureka启动完成后,依次启动其他服务:
|
||||
```bash
|
||||
# 在新的终端窗口中启动Auth Service
|
||||
cd auth-service
|
||||
mvn spring-boot:run
|
||||
|
||||
# 在新的终端窗口中启动User Service
|
||||
cd user-service
|
||||
mvn spring-boot:run
|
||||
|
||||
# 以此类推启动其他服务...
|
||||
```
|
||||
|
||||
## 访问服务
|
||||
|
||||
- **Eureka Dashboard**: http://localhost:8761
|
||||
- **API Gateway**: http://localhost:8080
|
||||
- **API文档**: http://localhost:8080/doc.html
|
||||
|
||||
## 服务端口
|
||||
|
||||
| 服务名称 | 端口 |
|
||||
|---------|------|
|
||||
| Eureka Server | 8761 |
|
||||
| API Gateway | 8080 |
|
||||
| Auth Service | 8081 |
|
||||
| User Service | 8082 |
|
||||
| Travel Service | 8083 |
|
||||
| Animal Service | 8084 |
|
||||
| Order Service | 8085 |
|
||||
| Promotion Service | 8086 |
|
||||
|
||||
## 性能优化
|
||||
|
||||
详细的性能优化指南请参考 [PERFORMANCE_OPTIMIZATION.md](PERFORMANCE_OPTIMIZATION.md) 文件。
|
||||
@@ -1,56 +0,0 @@
|
||||
<?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>animal-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>
|
||||
|
||||
<!-- 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>
|
||||
@@ -1,19 +0,0 @@
|
||||
package com.jiebanke.animal;
|
||||
|
||||
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.animal.mapper")
|
||||
@ComponentScan(basePackages = "com.jiebanke")
|
||||
public class AnimalApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AnimalApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -1,138 +0,0 @@
|
||||
package com.jiebanke.animal.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.jiebanke.animal.entity.Animal;
|
||||
import com.jiebanke.animal.service.AnimalService;
|
||||
import com.jiebanke.common.vo.ApiResponse;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/animals")
|
||||
public class AnimalController {
|
||||
|
||||
@Autowired
|
||||
private AnimalService animalService;
|
||||
|
||||
/**
|
||||
* 搜索动物(公开接口)
|
||||
*/
|
||||
@GetMapping("/search")
|
||||
public ApiResponse<Map<String, Object>> searchAnimals(
|
||||
@RequestParam(required = false) String keyword,
|
||||
@RequestParam(required = false) String species,
|
||||
@RequestParam(required = false) Double minPrice,
|
||||
@RequestParam(required = false) Double maxPrice,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize) {
|
||||
|
||||
IPage<Animal> animals = animalService.searchAnimals(keyword, species, minPrice, maxPrice, page, pageSize);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("animals", animals.getRecords());
|
||||
result.put("pagination", Map.of(
|
||||
"page", animals.getCurrent(),
|
||||
"pageSize", animals.getSize(),
|
||||
"total", animals.getTotal(),
|
||||
"totalPages", animals.getPages()
|
||||
));
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取动物列表(商家)
|
||||
*/
|
||||
@GetMapping
|
||||
public ApiResponse<Map<String, Object>> getAnimals(
|
||||
@RequestHeader("userId") Long merchantId,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) String species,
|
||||
@RequestParam(required = false) String status) {
|
||||
|
||||
IPage<Animal> animals = animalService.getAnimals(merchantId, page, pageSize, species, status);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("animals", animals.getRecords());
|
||||
result.put("pagination", Map.of(
|
||||
"page", animals.getCurrent(),
|
||||
"pageSize", animals.getSize(),
|
||||
"total", animals.getTotal(),
|
||||
"totalPages", animals.getPages()
|
||||
));
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取动物统计信息
|
||||
*/
|
||||
@GetMapping("/stats")
|
||||
public ApiResponse<Map<String, Object>> getAnimalStatistics() {
|
||||
Map<String, Object> statistics = animalService.getAnimalStatistics();
|
||||
return ApiResponse.success(statistics);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取单个动物详情
|
||||
*/
|
||||
@GetMapping("/{animalId}")
|
||||
public ApiResponse<Animal> getAnimal(@PathVariable Long animalId) {
|
||||
Animal animal = animalService.getAnimalById(animalId);
|
||||
return ApiResponse.success(animal);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建动物信息
|
||||
*/
|
||||
@PostMapping
|
||||
public ApiResponse<Map<String, Object>> createAnimal(
|
||||
@RequestHeader("userId") Long merchantId,
|
||||
@RequestBody Animal animal) {
|
||||
|
||||
animal.setMerchantId(merchantId);
|
||||
Long animalId = animalService.createAnimal(animal);
|
||||
Animal createdAnimal = animalService.getAnimalById(animalId);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("animal", createdAnimal);
|
||||
result.put("message", "动物信息创建成功");
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新动物信息
|
||||
*/
|
||||
@PutMapping("/{animalId}")
|
||||
public ApiResponse<Map<String, Object>> updateAnimal(
|
||||
@PathVariable Long animalId,
|
||||
@RequestBody Animal animal) {
|
||||
|
||||
Animal updatedAnimal = animalService.updateAnimal(animalId, animal);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("animal", updatedAnimal);
|
||||
result.put("message", "动物信息更新成功");
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除动物信息
|
||||
*/
|
||||
@DeleteMapping("/{animalId}")
|
||||
public ApiResponse<Map<String, Object>> deleteAnimal(@PathVariable Long animalId) {
|
||||
boolean deleted = animalService.deleteAnimal(animalId);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("message", "动物信息删除成功");
|
||||
result.put("animalId", animalId);
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.jiebanke.animal.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 Animal extends BaseEntity {
|
||||
private Long merchantId;
|
||||
private String name;
|
||||
private String species;
|
||||
private String breed;
|
||||
private LocalDate birthDate;
|
||||
private String personality;
|
||||
private String farmLocation;
|
||||
private BigDecimal price;
|
||||
private Integer claimCount;
|
||||
private String status;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.jiebanke.animal.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.jiebanke.animal.entity.Animal;
|
||||
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 AnimalMapper extends BaseMapper<Animal> {
|
||||
|
||||
/**
|
||||
* 根据商家ID获取动物列表
|
||||
* @param merchantId 商家ID
|
||||
* @return 动物列表
|
||||
*/
|
||||
@Select("SELECT * FROM animals WHERE merchant_id = #{merchantId} ORDER BY created_at DESC")
|
||||
List<Animal> selectByMerchantId(@Param("merchantId") Long merchantId);
|
||||
|
||||
/**
|
||||
* 根据物种获取动物列表
|
||||
* @param species 物种
|
||||
* @return 动物列表
|
||||
*/
|
||||
@Select("SELECT * FROM animals WHERE species = #{species} ORDER BY created_at DESC")
|
||||
List<Animal> selectBySpecies(@Param("species") String species);
|
||||
|
||||
/**
|
||||
* 根据状态获取动物列表
|
||||
* @param status 状态
|
||||
* @return 动物列表
|
||||
*/
|
||||
@Select("SELECT * FROM animals WHERE status = #{status} ORDER BY created_at DESC")
|
||||
List<Animal> selectByStatus(@Param("status") String status);
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package com.jiebanke.animal.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.jiebanke.animal.entity.Animal;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public interface AnimalService extends IService<Animal> {
|
||||
|
||||
/**
|
||||
* 获取动物列表
|
||||
* @param merchantId 商家ID
|
||||
* @param page 页码
|
||||
* @param pageSize 每页数量
|
||||
* @param species 物种
|
||||
* @param status 状态
|
||||
* @return 动物分页列表
|
||||
*/
|
||||
IPage<Animal> getAnimals(Long merchantId, Integer page, Integer pageSize, String species, String status);
|
||||
|
||||
/**
|
||||
* 获取单个动物详情
|
||||
* @param animalId 动物ID
|
||||
* @return 动物信息
|
||||
*/
|
||||
Animal getAnimalById(Long animalId);
|
||||
|
||||
/**
|
||||
* 创建动物
|
||||
* @param animal 动物信息
|
||||
* @return 创建的动物ID
|
||||
*/
|
||||
Long createAnimal(Animal animal);
|
||||
|
||||
/**
|
||||
* 更新动物信息
|
||||
* @param animalId 动物ID
|
||||
* @param animal 更新的动物信息
|
||||
* @return 更新后的动物信息
|
||||
*/
|
||||
Animal updateAnimal(Long animalId, Animal animal);
|
||||
|
||||
/**
|
||||
* 删除动物
|
||||
* @param animalId 动物ID
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
boolean deleteAnimal(Long animalId);
|
||||
|
||||
/**
|
||||
* 获取动物统计信息
|
||||
* @return 统计信息
|
||||
*/
|
||||
Map<String, Object> getAnimalStatistics();
|
||||
|
||||
/**
|
||||
* 搜索动物
|
||||
* @param keyword 关键词
|
||||
* @param species 物种
|
||||
* @param minPrice 最低价格
|
||||
* @param maxPrice 最高价格
|
||||
* @param page 页码
|
||||
* @param pageSize 每页数量
|
||||
* @return 动物分页列表
|
||||
*/
|
||||
IPage<Animal> searchAnimals(String keyword, String species, Double minPrice, Double maxPrice, Integer page, Integer pageSize);
|
||||
}
|
||||
@@ -1,156 +0,0 @@
|
||||
package com.jiebanke.animal.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.animal.entity.Animal;
|
||||
import com.jiebanke.animal.mapper.AnimalMapper;
|
||||
import com.jiebanke.animal.service.AnimalService;
|
||||
import com.jiebanke.common.exception.BusinessException;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class AnimalServiceImpl extends ServiceImpl<AnimalMapper, Animal> implements AnimalService {
|
||||
|
||||
@Override
|
||||
public IPage<Animal> getAnimals(Long merchantId, Integer page, Integer pageSize, String species, String status) {
|
||||
QueryWrapper<Animal> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
if (merchantId != null) {
|
||||
queryWrapper.eq("merchant_id", merchantId);
|
||||
}
|
||||
|
||||
if (species != null && !species.isEmpty()) {
|
||||
queryWrapper.eq("species", species);
|
||||
}
|
||||
|
||||
if (status != null && !status.isEmpty()) {
|
||||
queryWrapper.eq("status", status);
|
||||
}
|
||||
|
||||
queryWrapper.orderByDesc("created_at");
|
||||
|
||||
Page<Animal> pageObj = new Page<>(page != null ? page : 1, pageSize != null ? pageSize : 10);
|
||||
return this.page(pageObj, queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Animal getAnimalById(Long animalId) {
|
||||
Animal animal = this.getById(animalId);
|
||||
if (animal == null) {
|
||||
throw new BusinessException("动物不存在");
|
||||
}
|
||||
return animal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long createAnimal(Animal animal) {
|
||||
this.save(animal);
|
||||
return animal.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Animal updateAnimal(Long animalId, Animal animal) {
|
||||
Animal existingAnimal = this.getById(animalId);
|
||||
if (existingAnimal == null) {
|
||||
throw new BusinessException("动物不存在");
|
||||
}
|
||||
|
||||
// 更新字段
|
||||
if (animal.getName() != null) {
|
||||
existingAnimal.setName(animal.getName());
|
||||
}
|
||||
if (animal.getSpecies() != null) {
|
||||
existingAnimal.setSpecies(animal.getSpecies());
|
||||
}
|
||||
if (animal.getBreed() != null) {
|
||||
existingAnimal.setBreed(animal.getBreed());
|
||||
}
|
||||
if (animal.getBirthDate() != null) {
|
||||
existingAnimal.setBirthDate(animal.getBirthDate());
|
||||
}
|
||||
if (animal.getPersonality() != null) {
|
||||
existingAnimal.setPersonality(animal.getPersonality());
|
||||
}
|
||||
if (animal.getFarmLocation() != null) {
|
||||
existingAnimal.setFarmLocation(animal.getFarmLocation());
|
||||
}
|
||||
if (animal.getPrice() != null) {
|
||||
existingAnimal.setPrice(animal.getPrice());
|
||||
}
|
||||
if (animal.getStatus() != null) {
|
||||
existingAnimal.setStatus(animal.getStatus());
|
||||
}
|
||||
|
||||
this.updateById(existingAnimal);
|
||||
return existingAnimal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteAnimal(Long animalId) {
|
||||
return this.removeById(animalId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getAnimalStatistics() {
|
||||
// 获取动物总数
|
||||
int total = Math.toIntExact(this.count());
|
||||
|
||||
// 按物种统计
|
||||
QueryWrapper<Animal> speciesWrapper = new QueryWrapper<>();
|
||||
speciesWrapper.select("species", "COUNT(*) as count");
|
||||
speciesWrapper.groupBy("species");
|
||||
List<Map<String, Object>> speciesStats = this.listMaps(speciesWrapper);
|
||||
|
||||
// 按状态统计
|
||||
QueryWrapper<Animal> statusWrapper = new QueryWrapper<>();
|
||||
statusWrapper.select("status", "COUNT(*) as count");
|
||||
statusWrapper.groupBy("status");
|
||||
List<Map<String, Object>> statusStats = this.listMaps(statusWrapper);
|
||||
|
||||
// 构建返回结果
|
||||
Map<String, Object> statistics = new HashMap<>();
|
||||
statistics.put("total", total);
|
||||
statistics.put("bySpecies", speciesStats);
|
||||
statistics.put("byStatus", statusStats);
|
||||
|
||||
return statistics;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<Animal> searchAnimals(String keyword, String species, Double minPrice, Double maxPrice, Integer page, Integer pageSize) {
|
||||
QueryWrapper<Animal> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("status", "available");
|
||||
|
||||
if (keyword != null && !keyword.isEmpty()) {
|
||||
queryWrapper.and(wrapper -> wrapper
|
||||
.like("name", keyword)
|
||||
.or()
|
||||
.like("personality", keyword)
|
||||
.or()
|
||||
.like("species", keyword));
|
||||
}
|
||||
|
||||
if (species != null && !species.isEmpty()) {
|
||||
queryWrapper.eq("species", species);
|
||||
}
|
||||
|
||||
if (minPrice != null) {
|
||||
queryWrapper.ge("price", minPrice);
|
||||
}
|
||||
|
||||
if (maxPrice != null) {
|
||||
queryWrapper.le("price", maxPrice);
|
||||
}
|
||||
|
||||
queryWrapper.orderByDesc("created_at");
|
||||
|
||||
Page<Animal> pageObj = new Page<>(page != null ? page : 1, pageSize != null ? pageSize : 10);
|
||||
return this.page(pageObj, queryWrapper);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
server:
|
||||
port: 8084
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: animal-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
|
||||
@@ -1,32 +0,0 @@
|
||||
server:
|
||||
port: 8084
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: animal-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
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,96 +0,0 @@
|
||||
<?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>auth-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>
|
||||
|
||||
<!-- JWT -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- BCrypt -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-crypto</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Redis -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- RabbitMQ -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-amqp</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>
|
||||
@@ -1,26 +0,0 @@
|
||||
package com.jiebanke.auth;
|
||||
|
||||
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.Bean;
|
||||
import org.springframework.context.annotation.ComponentScan;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
@EnableFeignClients
|
||||
@MapperScan("com.jiebanke.auth.mapper")
|
||||
@ComponentScan(basePackages = "com.jiebanke")
|
||||
public class AuthApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(AuthApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public BCryptPasswordEncoder passwordEncoder() {
|
||||
return new BCryptPasswordEncoder();
|
||||
}
|
||||
}
|
||||
@@ -1,187 +0,0 @@
|
||||
package com.jiebanke.auth.controller;
|
||||
|
||||
import com.jiebanke.auth.service.AuthRedisService;
|
||||
import com.jiebanke.common.vo.ApiResponse;
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/auth")
|
||||
public class AuthController {
|
||||
|
||||
@Autowired
|
||||
private AuthService authService;
|
||||
|
||||
@Value("${jwt.secret}")
|
||||
private String jwtSecret;
|
||||
|
||||
@Autowired
|
||||
private AuthRedisService authRedisService;
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
*/
|
||||
@PostMapping("/register")
|
||||
public ApiResponse<AuthResult> register(@RequestBody RegisterRequest request) {
|
||||
User user = new User();
|
||||
user.setUsername(request.getUsername());
|
||||
user.setEmail(request.getEmail());
|
||||
user.setPhone(request.getPhone());
|
||||
user.setRealName(request.getNickname());
|
||||
|
||||
AuthResult result = authService.register(user, request.getPassword());
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
*/
|
||||
@PostMapping("/login")
|
||||
public ApiResponse<AuthResult> login(@RequestBody LoginRequest request) {
|
||||
AuthResult result = authService.login(request.getUsername(), request.getPassword());
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
*/
|
||||
@GetMapping("/me")
|
||||
public ApiResponse<User> getCurrentUser(@RequestHeader("userId") Long userId) {
|
||||
User user = authService.getCurrentUser(userId);
|
||||
return ApiResponse.success(user);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新用户个人信息
|
||||
*/
|
||||
@PutMapping("/profile")
|
||||
public ApiResponse<User> updateProfile(
|
||||
@RequestHeader("userId") Long userId,
|
||||
@RequestBody User user) {
|
||||
User updatedUser = authService.updateProfile(userId, user);
|
||||
return ApiResponse.success(updatedUser);
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
*/
|
||||
@PutMapping("/password")
|
||||
public ApiResponse<Map<String, String>> changePassword(
|
||||
@RequestHeader("userId") Long userId,
|
||||
@RequestBody ChangePasswordRequest request) {
|
||||
boolean success = authService.changePassword(userId, request.getCurrentPassword(), request.getNewPassword());
|
||||
|
||||
Map<String, String> result = new HashMap<>();
|
||||
result.put("message", success ? "密码修改成功" : "密码修改失败");
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信登录/注册
|
||||
*/
|
||||
@PostMapping("/wechat")
|
||||
public ApiResponse<AuthResult> wechatLogin(@RequestBody WechatLoginRequest request) {
|
||||
AuthResult result = authService.wechatLogin(request.getCode(), request.getUserInfo());
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员登录
|
||||
*/
|
||||
@PostMapping("/admin/login")
|
||||
public ApiResponse<AuthResult> adminLogin(@RequestBody LoginRequest request) {
|
||||
AuthResult result = authService.adminLogin(request.getUsername(), request.getPassword());
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
@GetMapping("/validate")
|
||||
public ApiResponse<Boolean> validateToken(@RequestHeader("Authorization") String token) {
|
||||
try {
|
||||
// 移除 "Bearer " 前缀
|
||||
if (token.startsWith("Bearer ")) {
|
||||
token = token.substring(7);
|
||||
}
|
||||
|
||||
// 解析JWT令牌
|
||||
Claims claims = Jwts.parserBuilder()
|
||||
.setSigningKey(jwtSecret.getBytes())
|
||||
.build()
|
||||
.parseClaimsJws(token)
|
||||
.getBody();
|
||||
|
||||
// 从Redis中获取用户登录状态
|
||||
Long userId = authRedisService.getUserLoginStatus(token);
|
||||
if (userId != null && userId.equals(claims.get("userId", Long.class))) {
|
||||
return ApiResponse.success(true);
|
||||
}
|
||||
|
||||
return ApiResponse.success(false);
|
||||
} catch (Exception e) {
|
||||
return ApiResponse.success(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 请求体类
|
||||
static class RegisterRequest {
|
||||
private String username;
|
||||
private String password;
|
||||
private String nickname;
|
||||
private String email;
|
||||
private String phone;
|
||||
|
||||
// Getters and setters
|
||||
public String getUsername() { return username; }
|
||||
public void setUsername(String username) { this.username = username; }
|
||||
|
||||
public String getPassword() { return password; }
|
||||
public void setPassword(String password) { this.password = password; }
|
||||
|
||||
public String getNickname() { return nickname; }
|
||||
public void setNickname(String nickname) { this.nickname = nickname; }
|
||||
|
||||
public String getEmail() { return email; }
|
||||
public void setEmail(String email) { this.email = email; }
|
||||
|
||||
public String getPhone() { return phone; }
|
||||
public void setPhone(String phone) { this.phone = phone; }
|
||||
}
|
||||
|
||||
static class LoginRequest {
|
||||
private String username;
|
||||
private String password;
|
||||
|
||||
// Getters and setters
|
||||
public String getUsername() { return username; }
|
||||
public void setUsername(String username) { this.username = username; }
|
||||
|
||||
public String getPassword() { return password; }
|
||||
public void setPassword(String password) { this.password = password; }
|
||||
}
|
||||
|
||||
static class ChangePasswordRequest {
|
||||
private String currentPassword;
|
||||
private String newPassword;
|
||||
|
||||
// Getters and setters
|
||||
public String getCurrentPassword() { return currentPassword; }
|
||||
public void setCurrentPassword(String currentPassword) { this.currentPassword = currentPassword; }
|
||||
|
||||
public String getNewPassword() { return newPassword; }
|
||||
public void setNewPassword(String newPassword) { this.newPassword = newPassword; }
|
||||
}
|
||||
|
||||
static class WechatLoginRequest {
|
||||
private String code;
|
||||
private Object userInfo;
|
||||
|
||||
// Getters and setters
|
||||
public String getCode() { return code; }
|
||||
public void setCode(String code) { this.code = code; }
|
||||
|
||||
public Object getUserInfo() { return userInfo; }
|
||||
public void setUserInfo(Object userInfo) { this.userInfo = userInfo; }
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.jiebanke.auth.entity;
|
||||
|
||||
import com.jiebanke.common.entity.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class User extends BaseEntity {
|
||||
private String username;
|
||||
private String password;
|
||||
private String email;
|
||||
private String phone;
|
||||
private String realName;
|
||||
private String idCard;
|
||||
private String status;
|
||||
private BigDecimal balance;
|
||||
private Integer creditScore;
|
||||
private LocalDateTime lastLogin;
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
package com.jiebanke.auth.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.jiebanke.auth.entity.User;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
@Mapper
|
||||
public interface UserMapper extends BaseMapper<User> {
|
||||
|
||||
/**
|
||||
* 根据用户名查找用户
|
||||
* @param username 用户名
|
||||
* @return 用户
|
||||
*/
|
||||
@Select("SELECT * FROM users WHERE username = #{username}")
|
||||
User selectByUsername(@Param("username") String username);
|
||||
|
||||
/**
|
||||
* 根据邮箱查找用户
|
||||
* @param email 邮箱
|
||||
* @return 用户
|
||||
*/
|
||||
@Select("SELECT * FROM users WHERE email = #{email}")
|
||||
User selectByEmail(@Param("email") String email);
|
||||
|
||||
/**
|
||||
* 根据手机号查找用户
|
||||
* @param phone 手机号
|
||||
* @return 用户
|
||||
*/
|
||||
@Select("SELECT * FROM users WHERE phone = #{phone}")
|
||||
User selectByPhone(@Param("phone") String phone);
|
||||
|
||||
/**
|
||||
* 检查用户名是否存在
|
||||
* @param username 用户名
|
||||
* @return 是否存在
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM users WHERE username = #{username}")
|
||||
int existsByUsername(@Param("username") String username);
|
||||
|
||||
/**
|
||||
* 检查邮箱是否存在
|
||||
* @param email 邮箱
|
||||
* @return 是否存在
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM users WHERE email = #{email}")
|
||||
int existsByEmail(@Param("email") String email);
|
||||
|
||||
/**
|
||||
* 检查手机号是否存在
|
||||
* @param phone 手机号
|
||||
* @return 是否存在
|
||||
*/
|
||||
@Select("SELECT COUNT(*) FROM users WHERE phone = #{phone}")
|
||||
int existsByPhone(@Param("phone") String phone);
|
||||
|
||||
/**
|
||||
* 更新最后登录时间
|
||||
* @param id 用户ID
|
||||
* @return 更新记录数
|
||||
*/
|
||||
@Update("UPDATE users SET last_login = NOW(), updated_at = NOW() WHERE id = #{id}")
|
||||
int updateLastLogin(@Param("id") Long id);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package com.jiebanke.auth.service;
|
||||
|
||||
import com.jiebanke.common.config.RabbitMQConfig;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class AuthRabbitMQService {
|
||||
|
||||
@Autowired
|
||||
private RabbitTemplate rabbitTemplate;
|
||||
|
||||
// 发送登录成功消息
|
||||
public void sendLoginSuccessMessage(Long userId, String username, String ip) {
|
||||
Map<String, Object> message = new HashMap<>();
|
||||
message.put("userId", userId);
|
||||
message.put("username", username);
|
||||
message.put("ip", ip);
|
||||
message.put("eventType", "USER_LOGIN_SUCCESS");
|
||||
|
||||
rabbitTemplate.convertAndSend(
|
||||
RabbitMQConfig.EXCHANGE_NAME,
|
||||
RabbitMQConfig.USER_ROUTING_KEY,
|
||||
message
|
||||
);
|
||||
}
|
||||
|
||||
// 发送登录失败消息
|
||||
public void sendLoginFailureMessage(String username, String ip, String reason) {
|
||||
Map<String, Object> message = new HashMap<>();
|
||||
message.put("username", username);
|
||||
message.put("ip", ip);
|
||||
message.put("reason", reason);
|
||||
message.put("eventType", "USER_LOGIN_FAILURE");
|
||||
|
||||
rabbitTemplate.convertAndSend(
|
||||
RabbitMQConfig.EXCHANGE_NAME,
|
||||
RabbitMQConfig.USER_ROUTING_KEY,
|
||||
message
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
package com.jiebanke.auth.service;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Service
|
||||
public class AuthRedisService {
|
||||
|
||||
@Autowired
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
// 缓存验证码
|
||||
public void cacheVerificationCode(String phone, String code) {
|
||||
redisTemplate.opsForValue().set("verification:code:" + phone, code, 5, TimeUnit.MINUTES);
|
||||
}
|
||||
|
||||
// 获取缓存的验证码
|
||||
public String getCachedVerificationCode(String phone) {
|
||||
return (String) redisTemplate.opsForValue().get("verification:code:" + phone);
|
||||
}
|
||||
|
||||
// 删除缓存的验证码
|
||||
public void removeCachedVerificationCode(String phone) {
|
||||
redisTemplate.delete("verification:code:" + phone);
|
||||
}
|
||||
|
||||
// 缓存登录失败次数
|
||||
public void cacheLoginFailures(String identifier, Integer failures) {
|
||||
redisTemplate.opsForValue().set("login:failures:" + identifier, failures, 1, TimeUnit.HOURS);
|
||||
}
|
||||
|
||||
// 获取登录失败次数
|
||||
public Integer getLoginFailures(String identifier) {
|
||||
return (Integer) redisTemplate.opsForValue().get("login:failures:" + identifier);
|
||||
}
|
||||
|
||||
// 增加登录失败次数
|
||||
public void incrementLoginFailures(String identifier) {
|
||||
Integer failures = getLoginFailures(identifier);
|
||||
if (failures == null) {
|
||||
failures = 0;
|
||||
}
|
||||
redisTemplate.opsForValue().set("login:failures:" + identifier, failures + 1, 1, TimeUnit.HOURS);
|
||||
}
|
||||
|
||||
// 清除登录失败次数
|
||||
public void clearLoginFailures(String identifier) {
|
||||
redisTemplate.delete("login:failures:" + identifier);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.jiebanke.auth.service;
|
||||
|
||||
import com.jiebanke.auth.entity.User;
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class AuthResult {
|
||||
private User user;
|
||||
private String token;
|
||||
private String message;
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
package com.jiebanke.auth.service;
|
||||
|
||||
import com.jiebanke.auth.entity.User;
|
||||
|
||||
public interface AuthService {
|
||||
|
||||
/**
|
||||
* 用户注册
|
||||
* @param user 用户信息
|
||||
* @return 注册后的用户信息和Token
|
||||
*/
|
||||
AuthResult register(User user, String password);
|
||||
|
||||
/**
|
||||
* 用户登录
|
||||
* @param username 用户名/邮箱/手机号
|
||||
* @param password 密码
|
||||
* @return 登录后的用户信息和Token
|
||||
*/
|
||||
AuthResult login(String username, String password);
|
||||
|
||||
/**
|
||||
* 获取当前用户信息
|
||||
* @param userId 用户ID
|
||||
* @return 用户信息
|
||||
*/
|
||||
User getCurrentUser(Long userId);
|
||||
|
||||
/**
|
||||
* 更新用户信息
|
||||
* @param userId 用户ID
|
||||
* @param user 更新的用户信息
|
||||
* @return 更新后的用户信息
|
||||
*/
|
||||
User updateProfile(Long userId, User user);
|
||||
|
||||
/**
|
||||
* 修改密码
|
||||
* @param userId 用户ID
|
||||
* @param currentPassword 当前密码
|
||||
* @param newPassword 新密码
|
||||
* @return 是否修改成功
|
||||
*/
|
||||
boolean changePassword(Long userId, String currentPassword, String newPassword);
|
||||
|
||||
/**
|
||||
* 微信登录/注册
|
||||
* @param code 微信授权码
|
||||
* @param userInfo 微信用户信息
|
||||
* @return 登录后的用户信息和Token
|
||||
*/
|
||||
AuthResult wechatLogin(String code, Object userInfo);
|
||||
|
||||
/**
|
||||
* 管理员登录
|
||||
* @param username 用户名/邮箱/手机号
|
||||
* @param password 密码
|
||||
* @return 登录后的管理员信息和Token
|
||||
*/
|
||||
AuthResult adminLogin(String username, String password);
|
||||
}
|
||||
@@ -1,255 +0,0 @@
|
||||
package com.jiebanke.auth.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.jiebanke.auth.entity.User;
|
||||
import com.jiebanke.auth.mapper.UserMapper;
|
||||
import com.jiebanke.auth.service.AuthResult;
|
||||
import com.jiebanke.auth.service.AuthService;
|
||||
import com.jiebanke.auth.util.JwtUtil;
|
||||
import com.jiebanke.common.exception.BusinessException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
@Service
|
||||
public class AuthServiceImpl extends ServiceImpl<UserMapper, User> implements AuthService {
|
||||
|
||||
@Autowired
|
||||
private UserMapper userMapper;
|
||||
|
||||
@Autowired
|
||||
private JwtUtil jwtUtil;
|
||||
|
||||
@Autowired
|
||||
private BCryptPasswordEncoder passwordEncoder;
|
||||
|
||||
@Override
|
||||
public AuthResult register(User user, String password) {
|
||||
// 检查用户名是否已存在
|
||||
if (userMapper.existsByUsername(user.getUsername()) > 0) {
|
||||
throw new BusinessException("用户名已存在");
|
||||
}
|
||||
|
||||
// 检查邮箱是否已存在
|
||||
if (user.getEmail() != null && userMapper.existsByEmail(user.getEmail()) > 0) {
|
||||
throw new BusinessException("邮箱已存在");
|
||||
}
|
||||
|
||||
// 检查手机号是否已存在
|
||||
if (user.getPhone() != null && userMapper.existsByPhone(user.getPhone()) > 0) {
|
||||
throw new BusinessException("手机号已存在");
|
||||
}
|
||||
|
||||
// 加密密码
|
||||
String hashedPassword = passwordEncoder.encode(password);
|
||||
user.setPassword(hashedPassword);
|
||||
|
||||
// 设置默认值
|
||||
if (user.getStatus() == null) {
|
||||
user.setStatus("active");
|
||||
}
|
||||
|
||||
// 创建新用户
|
||||
this.save(user);
|
||||
|
||||
// 生成Token
|
||||
String token = jwtUtil.generateToken(user.getId());
|
||||
|
||||
// 更新最后登录时间
|
||||
userMapper.updateLastLogin(user.getId());
|
||||
|
||||
// 创建返回结果
|
||||
AuthResult result = new AuthResult();
|
||||
result.setUser(user);
|
||||
result.setToken(token);
|
||||
result.setMessage("注册成功");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResult login(String username, String password) {
|
||||
if (username == null || username.isEmpty() || password == null || password.isEmpty()) {
|
||||
throw new BusinessException("用户名和密码不能为空");
|
||||
}
|
||||
|
||||
// 查找用户(支持用户名、邮箱、手机号登录)
|
||||
User user = userMapper.selectByUsername(username);
|
||||
if (user == null) {
|
||||
user = userMapper.selectByEmail(username);
|
||||
}
|
||||
if (user == null) {
|
||||
user = userMapper.selectByPhone(username);
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
throw new BusinessException("用户不存在");
|
||||
}
|
||||
|
||||
// 检查用户状态
|
||||
if (!"active".equals(user.getStatus())) {
|
||||
throw new BusinessException("账户已被禁用");
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
if (!passwordEncoder.matches(password, user.getPassword())) {
|
||||
throw new BusinessException("密码错误");
|
||||
}
|
||||
|
||||
// 生成Token
|
||||
String token = jwtUtil.generateToken(user.getId());
|
||||
|
||||
// 更新最后登录时间
|
||||
userMapper.updateLastLogin(user.getId());
|
||||
|
||||
// 创建返回结果
|
||||
AuthResult result = new AuthResult();
|
||||
result.setUser(user);
|
||||
result.setToken(token);
|
||||
result.setMessage("登录成功");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User getCurrentUser(Long userId) {
|
||||
User user = this.getById(userId);
|
||||
if (user == null) {
|
||||
throw new BusinessException("用户不存在");
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
@Override
|
||||
public User updateProfile(Long userId, User user) {
|
||||
User existingUser = this.getById(userId);
|
||||
if (existingUser == null) {
|
||||
throw new BusinessException("用户不存在");
|
||||
}
|
||||
|
||||
// 更新字段
|
||||
if (user.getRealName() != null) {
|
||||
existingUser.setRealName(user.getRealName());
|
||||
}
|
||||
if (user.getEmail() != null) {
|
||||
existingUser.setEmail(user.getEmail());
|
||||
}
|
||||
if (user.getPhone() != null) {
|
||||
existingUser.setPhone(user.getPhone());
|
||||
}
|
||||
|
||||
this.updateById(existingUser);
|
||||
return existingUser;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean changePassword(Long userId, String currentPassword, String newPassword) {
|
||||
if (currentPassword == null || currentPassword.isEmpty() || newPassword == null || newPassword.isEmpty()) {
|
||||
throw new BusinessException("当前密码和新密码不能为空");
|
||||
}
|
||||
|
||||
if (newPassword.length() < 6) {
|
||||
throw new BusinessException("新密码长度不能少于6位");
|
||||
}
|
||||
|
||||
User user = this.getById(userId);
|
||||
if (user == null) {
|
||||
throw new BusinessException("用户不存在");
|
||||
}
|
||||
|
||||
// 验证当前密码
|
||||
if (!passwordEncoder.matches(currentPassword, user.getPassword())) {
|
||||
throw new BusinessException("当前密码错误");
|
||||
}
|
||||
|
||||
// 加密新密码
|
||||
String hashedPassword = passwordEncoder.encode(newPassword);
|
||||
user.setPassword(hashedPassword);
|
||||
|
||||
// 更新密码
|
||||
return this.updateById(user);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResult wechatLogin(String code, Object userInfo) {
|
||||
if (code == null || code.isEmpty()) {
|
||||
throw new BusinessException("微信授权码不能为空");
|
||||
}
|
||||
|
||||
// 这里应该调用微信API获取openid和unionid
|
||||
// 模拟获取微信用户信息
|
||||
String openid = "mock_openid_" + System.currentTimeMillis();
|
||||
|
||||
// 查找是否已存在微信用户
|
||||
// 注意:在实际实现中,应该有一个专门的字段来存储微信openid
|
||||
User user = null;
|
||||
|
||||
if (user == null) {
|
||||
// 创建新用户(微信注册)
|
||||
user = new User();
|
||||
user.setUsername("wx_" + openid.substring(Math.max(0, openid.length() - 8)));
|
||||
String randomPassword = String.valueOf(System.currentTimeMillis()).substring(0, 8);
|
||||
String hashedPassword = passwordEncoder.encode(randomPassword);
|
||||
user.setPassword(hashedPassword);
|
||||
user.setRealName("微信用户");
|
||||
user.setStatus("active");
|
||||
|
||||
this.save(user);
|
||||
}
|
||||
|
||||
// 生成Token
|
||||
String token = jwtUtil.generateToken(user.getId());
|
||||
|
||||
// 创建返回结果
|
||||
AuthResult result = new AuthResult();
|
||||
result.setUser(user);
|
||||
result.setToken(token);
|
||||
result.setMessage("微信登录成功");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuthResult adminLogin(String username, String password) {
|
||||
if (username == null || username.isEmpty() || password == null || password.isEmpty()) {
|
||||
throw new BusinessException("用户名和密码不能为空");
|
||||
}
|
||||
|
||||
// 查找用户(支持用户名、邮箱、手机号登录)
|
||||
User user = userMapper.selectByUsername(username);
|
||||
if (user == null) {
|
||||
user = userMapper.selectByEmail(username);
|
||||
}
|
||||
if (user == null) {
|
||||
user = userMapper.selectByPhone(username);
|
||||
}
|
||||
|
||||
if (user == null) {
|
||||
throw new BusinessException("用户不存在");
|
||||
}
|
||||
|
||||
// 检查用户状态
|
||||
if (!"active".equals(user.getStatus())) {
|
||||
throw new BusinessException("账户已被禁用");
|
||||
}
|
||||
|
||||
// 验证密码
|
||||
if (!passwordEncoder.matches(password, user.getPassword())) {
|
||||
throw new BusinessException("密码错误");
|
||||
}
|
||||
|
||||
// 生成Token
|
||||
String token = jwtUtil.generateToken(user.getId());
|
||||
|
||||
// 更新最后登录时间
|
||||
userMapper.updateLastLogin(user.getId());
|
||||
|
||||
// 创建返回结果
|
||||
AuthResult result = new AuthResult();
|
||||
result.setUser(user);
|
||||
result.setToken(token);
|
||||
result.setMessage("管理员登录成功");
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
package com.jiebanke.auth.util;
|
||||
|
||||
import io.jsonwebtoken.Claims;
|
||||
import io.jsonwebtoken.Jwts;
|
||||
import io.jsonwebtoken.SignatureAlgorithm;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
|
||||
@Component
|
||||
public class JwtUtil {
|
||||
|
||||
@Value("${jwt.secret:mySecretKey}")
|
||||
private String secret;
|
||||
|
||||
@Value("${jwt.expiration:604800}")
|
||||
private Long expiration;
|
||||
|
||||
/**
|
||||
* 生成JWT Token
|
||||
* @param userId 用户ID
|
||||
* @return JWT Token
|
||||
*/
|
||||
public String generateToken(Long userId) {
|
||||
Map<String, Object> claims = new HashMap<>();
|
||||
return createToken(claims, userId.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从JWT Token中提取用户ID
|
||||
* @param token JWT Token
|
||||
* @return 用户ID
|
||||
*/
|
||||
public Long extractUserId(String token) {
|
||||
return Long.valueOf(extractClaim(token, Claims::getSubject));
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证JWT Token是否有效
|
||||
* @param token JWT Token
|
||||
* @param userId 用户ID
|
||||
* @return 是否有效
|
||||
*/
|
||||
public Boolean validateToken(String token, Long userId) {
|
||||
final Long extractedUserId = extractUserId(token);
|
||||
return (extractedUserId.equals(userId) && !isTokenExpired(token));
|
||||
}
|
||||
|
||||
/**
|
||||
* 从JWT Token中提取声明
|
||||
* @param token JWT Token
|
||||
* @param claimsResolver 声明解析器
|
||||
* @param <T> 声明类型
|
||||
* @return 声明值
|
||||
*/
|
||||
public <T> T extractClaim(String token, Function<Claims, T> claimsResolver) {
|
||||
final Claims claims = extractAllClaims(token);
|
||||
return claimsResolver.apply(claims);
|
||||
}
|
||||
|
||||
/**
|
||||
* 从JWT Token中提取所有声明
|
||||
* @param token JWT Token
|
||||
* @return 所有声明
|
||||
*/
|
||||
private Claims extractAllClaims(String token) {
|
||||
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查JWT Token是否过期
|
||||
* @param token JWT Token
|
||||
* @return 是否过期
|
||||
*/
|
||||
private Boolean isTokenExpired(String token) {
|
||||
return extractExpiration(token).before(new Date());
|
||||
}
|
||||
|
||||
/**
|
||||
* 从JWT Token中提取过期时间
|
||||
* @param token JWT Token
|
||||
* @return 过期时间
|
||||
*/
|
||||
public Date extractExpiration(String token) {
|
||||
return extractClaim(token, Claims::getExpiration);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建JWT Token
|
||||
* @param claims 声明
|
||||
* @param subject 主题
|
||||
* @return JWT Token
|
||||
*/
|
||||
private String createToken(Map<String, Object> claims, String subject) {
|
||||
return Jwts.builder()
|
||||
.setClaims(claims)
|
||||
.setSubject(subject)
|
||||
.setIssuedAt(new Date(System.currentTimeMillis()))
|
||||
.setExpiration(new Date(System.currentTimeMillis() + expiration * 1000))
|
||||
.signWith(SignatureAlgorithm.HS512, secret)
|
||||
.compact();
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
server:
|
||||
port: 8081
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: auth-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/
|
||||
|
||||
jwt:
|
||||
secret: mySecretKey
|
||||
expiration: 604800
|
||||
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: auto
|
||||
@@ -1,55 +0,0 @@
|
||||
package com.jiebanke.auth.service;
|
||||
|
||||
import com.jiebanke.common.config.RabbitMQConfig;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class AuthRabbitMQServiceTest {
|
||||
|
||||
@Mock
|
||||
private RabbitTemplate rabbitTemplate;
|
||||
|
||||
@InjectMocks
|
||||
private AuthRabbitMQService authRabbitMQService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSendLoginSuccessMessage() {
|
||||
Long userId = 1L;
|
||||
String username = "testUser";
|
||||
String ip = "127.0.0.1";
|
||||
|
||||
authRabbitMQService.sendLoginSuccessMessage(userId, username, ip);
|
||||
|
||||
verify(rabbitTemplate).convertAndSend(
|
||||
RabbitMQConfig.EXCHANGE_NAME,
|
||||
RabbitMQConfig.USER_ROUTING_KEY,
|
||||
anyMap()
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testSendLoginFailureMessage() {
|
||||
String username = "testUser";
|
||||
String ip = "127.0.0.1";
|
||||
String reason = "Invalid credentials";
|
||||
|
||||
authRabbitMQService.sendLoginFailureMessage(username, ip, reason);
|
||||
|
||||
verify(rabbitTemplate).convertAndSend(
|
||||
RabbitMQConfig.EXCHANGE_NAME,
|
||||
RabbitMQConfig.USER_ROUTING_KEY,
|
||||
anyMap()
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
package com.jiebanke.auth.service;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.core.ValueOperations;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class AuthRedisServiceTest {
|
||||
|
||||
@Mock
|
||||
private RedisTemplate<String, Object> redisTemplate;
|
||||
|
||||
@Mock
|
||||
private ValueOperations<String, Object> valueOperations;
|
||||
|
||||
@InjectMocks
|
||||
private AuthRedisService authRedisService;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
MockitoAnnotations.openMocks(this);
|
||||
when(redisTemplate.opsForValue()).thenReturn(valueOperations);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCacheVerificationCode() {
|
||||
String phone = "13800138000";
|
||||
String code = "123456";
|
||||
|
||||
authRedisService.cacheVerificationCode(phone, code);
|
||||
|
||||
verify(valueOperations).set(eq("verification:code:" + phone), eq(code), anyLong(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetCachedVerificationCode() {
|
||||
String phone = "13800138000";
|
||||
String code = "123456";
|
||||
when(valueOperations.get("verification:code:" + phone)).thenReturn(code);
|
||||
|
||||
String result = authRedisService.getCachedVerificationCode(phone);
|
||||
|
||||
assertEquals(code, result);
|
||||
verify(valueOperations).get("verification:code:" + phone);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testRemoveCachedVerificationCode() {
|
||||
String phone = "13800138000";
|
||||
|
||||
authRedisService.removeCachedVerificationCode(phone);
|
||||
|
||||
verify(redisTemplate).delete("verification:code:" + phone);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testCacheLoginFailures() {
|
||||
String identifier = "testUser";
|
||||
Integer failures = 3;
|
||||
|
||||
authRedisService.cacheLoginFailures(identifier, failures);
|
||||
|
||||
verify(valueOperations).set(eq("login:failures:" + identifier), eq(failures), anyLong(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testGetLoginFailures() {
|
||||
String identifier = "testUser";
|
||||
Integer failures = 3;
|
||||
when(valueOperations.get("login:failures:" + identifier)).thenReturn(failures);
|
||||
|
||||
Integer result = authRedisService.getLoginFailures(identifier);
|
||||
|
||||
assertEquals(failures, result);
|
||||
verify(valueOperations).get("login:failures:" + identifier);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIncrementLoginFailures() {
|
||||
String identifier = "testUser";
|
||||
when(valueOperations.get("login:failures:" + identifier)).thenReturn(null);
|
||||
|
||||
authRedisService.incrementLoginFailures(identifier);
|
||||
|
||||
verify(valueOperations).set(eq("login:failures:" + identifier), eq(1), anyLong(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIncrementLoginFailuresWithExistingValue() {
|
||||
String identifier = "testUser";
|
||||
when(valueOperations.get("login:failures:" + identifier)).thenReturn(2);
|
||||
|
||||
authRedisService.incrementLoginFailures(identifier);
|
||||
|
||||
verify(valueOperations).set(eq("login:failures:" + identifier), eq(3), anyLong(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testClearLoginFailures() {
|
||||
String identifier = "testUser";
|
||||
|
||||
authRedisService.clearLoginFailures(identifier);
|
||||
|
||||
verify(redisTemplate).delete("login:failures:" + identifier);
|
||||
}
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
server:
|
||||
port: 8081
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: auth-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/
|
||||
|
||||
jwt:
|
||||
secret: mySecretKey
|
||||
expiration: 604800
|
||||
|
||||
mybatis-plus:
|
||||
configuration:
|
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
|
||||
global-config:
|
||||
db-config:
|
||||
id-type: auto
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 构建结伴客Java后端服务脚本
|
||||
|
||||
echo "开始构建结伴客Java后端服务..."
|
||||
|
||||
# 清理之前的构建
|
||||
echo "清理项目..."
|
||||
mvn clean
|
||||
|
||||
# 构建所有模块
|
||||
echo "构建所有模块..."
|
||||
mvn install
|
||||
|
||||
# 检查构建是否成功
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "构建成功!"
|
||||
|
||||
# 显示构建结果
|
||||
echo "构建产物位置:"
|
||||
echo " Eureka Server: eureka-server/target/"
|
||||
echo " Gateway Service: gateway-service/target/"
|
||||
echo " Auth Service: auth-service/target/"
|
||||
echo " User Service: user-service/target/"
|
||||
echo " Travel Service: travel-service/target/"
|
||||
echo " Animal Service: animal-service/target/"
|
||||
echo " Order Service: order-service/target/"
|
||||
echo " Promotion Service: promotion-service/target/"
|
||||
|
||||
# 复制jar包到各自目录以便Docker构建
|
||||
echo "复制jar包..."
|
||||
cp eureka-server/target/eureka-server.jar eureka-server/
|
||||
cp gateway-service/target/gateway-service.jar gateway-service/
|
||||
cp auth-service/target/auth-service.jar auth-service/
|
||||
cp user-service/target/user-service.jar user-service/
|
||||
cp travel-service/target/travel-service.jar travel-service/
|
||||
cp animal-service/target/animal-service.jar animal-service/
|
||||
cp order-service/target/order-service.jar order-service/
|
||||
cp promotion-service/target/promotion-service.jar promotion-service/
|
||||
|
||||
echo "所有服务构建完成,可以使用docker-compose启动服务"
|
||||
else
|
||||
echo "构建失败,请检查错误信息"
|
||||
fi
|
||||
@@ -1,36 +0,0 @@
|
||||
<?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>common</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Boot Web -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Lombok -->
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
|
||||
<!-- Validation -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.jiebanke.common.config;
|
||||
|
||||
import feign.Request;
|
||||
import feign.Retryer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Configuration
|
||||
public class FeignConfig {
|
||||
|
||||
@Bean
|
||||
public Request.Options options() {
|
||||
return new Request.Options(5, TimeUnit.SECONDS, 30, TimeUnit.SECONDS, true);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Retryer retryer() {
|
||||
return new Retryer.Default(100, 1000, 3);
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
package com.jiebanke.common.config;
|
||||
|
||||
import org.springframework.amqp.core.*;
|
||||
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||
import org.springframework.amqp.support.converter.MessageConverter;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class RabbitMQConfig {
|
||||
|
||||
// 定义队列名称
|
||||
public static final String USER_QUEUE = "user.queue";
|
||||
public static final String TRAVEL_QUEUE = "travel.queue";
|
||||
public static final String ANIMAL_QUEUE = "animal.queue";
|
||||
public static final String ORDER_QUEUE = "order.queue";
|
||||
public static final String PROMOTION_QUEUE = "promotion.queue";
|
||||
|
||||
// 定义交换机名称
|
||||
public static final String EXCHANGE_NAME = "jiebanke.exchange";
|
||||
|
||||
// 定义路由键
|
||||
public static final String USER_ROUTING_KEY = "user.routing.key";
|
||||
public static final String TRAVEL_ROUTING_KEY = "travel.routing.key";
|
||||
public static final String ANIMAL_ROUTING_KEY = "animal.routing.key";
|
||||
public static final String ORDER_ROUTING_KEY = "order.routing.key";
|
||||
public static final String PROMOTION_ROUTING_KEY = "promotion.routing.key";
|
||||
|
||||
// 队列声明
|
||||
@Bean
|
||||
public Queue userQueue() {
|
||||
return new Queue(USER_QUEUE, true);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Queue travelQueue() {
|
||||
return new Queue(TRAVEL_QUEUE, true);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Queue animalQueue() {
|
||||
return new Queue(ANIMAL_QUEUE, true);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Queue orderQueue() {
|
||||
return new Queue(ORDER_QUEUE, true);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Queue promotionQueue() {
|
||||
return new Queue(PROMOTION_QUEUE, true);
|
||||
}
|
||||
|
||||
// 交换机声明
|
||||
@Bean
|
||||
public TopicExchange exchange() {
|
||||
return new TopicExchange(EXCHANGE_NAME);
|
||||
}
|
||||
|
||||
// 绑定关系
|
||||
@Bean
|
||||
public Binding userBinding() {
|
||||
return BindingBuilder.bind(userQueue()).to(exchange()).with(USER_ROUTING_KEY);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Binding travelBinding() {
|
||||
return BindingBuilder.bind(travelQueue()).to(exchange()).with(TRAVEL_ROUTING_KEY);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Binding animalBinding() {
|
||||
return BindingBuilder.bind(animalQueue()).to(exchange()).with(ANIMAL_ROUTING_KEY);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Binding orderBinding() {
|
||||
return BindingBuilder.bind(orderQueue()).to(exchange()).with(ORDER_ROUTING_KEY);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public Binding promotionBinding() {
|
||||
return BindingBuilder.bind(promotionQueue()).to(exchange()).with(PROMOTION_ROUTING_KEY);
|
||||
}
|
||||
|
||||
// 消息转换器
|
||||
@Bean
|
||||
public MessageConverter messageConverter() {
|
||||
return new Jackson2JsonMessageConverter();
|
||||
}
|
||||
|
||||
// RabbitTemplate配置
|
||||
@Bean
|
||||
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
|
||||
RabbitTemplate template = new RabbitTemplate(connectionFactory);
|
||||
template.setMessageConverter(messageConverter());
|
||||
return template;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.jiebanke.common.config;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.data.redis.connection.RedisConnectionFactory;
|
||||
import org.springframework.data.redis.core.RedisTemplate;
|
||||
import org.springframework.data.redis.serializer.StringRedisSerializer;
|
||||
|
||||
@Configuration
|
||||
public class RedisConfig {
|
||||
|
||||
@Bean
|
||||
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
|
||||
RedisTemplate<String, Object> template = new RedisTemplate<>();
|
||||
template.setConnectionFactory(connectionFactory);
|
||||
template.setKeySerializer(new StringRedisSerializer());
|
||||
template.setValueSerializer(new StringRedisSerializer());
|
||||
return template;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
package com.jiebanke.common.entity;
|
||||
|
||||
import lombok.Data;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
public class BaseEntity {
|
||||
private Long id;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime updatedAt;
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
package com.jiebanke.common.exception;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class BusinessException extends RuntimeException {
|
||||
private int code;
|
||||
private String message;
|
||||
|
||||
public BusinessException(String message) {
|
||||
super(message);
|
||||
this.code = 400;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public BusinessException(int code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package com.jiebanke.common.exception;
|
||||
|
||||
import com.jiebanke.common.vo.ApiResponse;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
@ExceptionHandler(BusinessException.class)
|
||||
public ApiResponse<Void> handleBusinessException(BusinessException e) {
|
||||
return ApiResponse.error(e.getCode(), e.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ApiResponse<Void> handleException(Exception e) {
|
||||
e.printStackTrace();
|
||||
return ApiResponse.error(500, "服务器内部错误");
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
package com.jiebanke.common.vo;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class ApiResponse<T> {
|
||||
private boolean success;
|
||||
private int code;
|
||||
private String message;
|
||||
private T data;
|
||||
private long timestamp;
|
||||
|
||||
public static <T> ApiResponse<T> success(T data) {
|
||||
ApiResponse<T> response = new ApiResponse<>();
|
||||
response.success = true;
|
||||
response.code = 200;
|
||||
response.message = "操作成功";
|
||||
response.data = data;
|
||||
response.timestamp = System.currentTimeMillis();
|
||||
return response;
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> success(T data, String message) {
|
||||
ApiResponse<T> response = new ApiResponse<>();
|
||||
response.success = true;
|
||||
response.code = 200;
|
||||
response.message = message;
|
||||
response.data = data;
|
||||
response.timestamp = System.currentTimeMillis();
|
||||
return response;
|
||||
}
|
||||
|
||||
public static <T> ApiResponse<T> error(int code, String message) {
|
||||
ApiResponse<T> response = new ApiResponse<>();
|
||||
response.success = false;
|
||||
response.code = code;
|
||||
response.message = message;
|
||||
response.timestamp = System.currentTimeMillis();
|
||||
return response;
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,147 +0,0 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# MySQL数据库
|
||||
mysql:
|
||||
image: mysql:8.0
|
||||
container_name: jiebanke-mysql
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
MYSQL_DATABASE: jiebanke
|
||||
ports:
|
||||
- "3306:3306"
|
||||
volumes:
|
||||
- mysql_data:/var/lib/mysql
|
||||
- ./scripts/init-database.sql:/docker-entrypoint-initdb.d/init-database.sql
|
||||
networks:
|
||||
- jiebanke-network
|
||||
|
||||
# Redis缓存
|
||||
redis:
|
||||
image: redis:6.0
|
||||
container_name: jiebanke-redis
|
||||
ports:
|
||||
- "6379:6379"
|
||||
networks:
|
||||
- jiebanke-network
|
||||
|
||||
# RabbitMQ消息队列
|
||||
rabbitmq:
|
||||
image: rabbitmq:3.8-management
|
||||
container_name: jiebanke-rabbitmq
|
||||
ports:
|
||||
- "5672:5672"
|
||||
- "15672:15672"
|
||||
networks:
|
||||
- jiebanke-network
|
||||
|
||||
# Eureka服务注册中心
|
||||
eureka-server:
|
||||
build:
|
||||
context: ./eureka-server
|
||||
container_name: jiebanke-eureka
|
||||
ports:
|
||||
- "8761:8761"
|
||||
networks:
|
||||
- jiebanke-network
|
||||
depends_on:
|
||||
- mysql
|
||||
- redis
|
||||
- rabbitmq
|
||||
|
||||
# API网关
|
||||
gateway-service:
|
||||
build:
|
||||
context: ./gateway-service
|
||||
container_name: jiebanke-gateway
|
||||
ports:
|
||||
- "8080:8080"
|
||||
networks:
|
||||
- jiebanke-network
|
||||
depends_on:
|
||||
- eureka-server
|
||||
|
||||
# 认证服务
|
||||
auth-service:
|
||||
build:
|
||||
context: ./auth-service
|
||||
container_name: jiebanke-auth
|
||||
ports:
|
||||
- "8081:8081"
|
||||
networks:
|
||||
- jiebanke-network
|
||||
depends_on:
|
||||
- eureka-server
|
||||
- mysql
|
||||
|
||||
# 用户服务
|
||||
user-service:
|
||||
build:
|
||||
context: ./user-service
|
||||
container_name: jiebanke-user
|
||||
ports:
|
||||
- "8082:8082"
|
||||
networks:
|
||||
- jiebanke-network
|
||||
depends_on:
|
||||
- eureka-server
|
||||
- mysql
|
||||
|
||||
# 旅行服务
|
||||
travel-service:
|
||||
build:
|
||||
context: ./travel-service
|
||||
container_name: jiebanke-travel
|
||||
ports:
|
||||
- "8083:8083"
|
||||
networks:
|
||||
- jiebanke-network
|
||||
depends_on:
|
||||
- eureka-server
|
||||
- mysql
|
||||
|
||||
# 动物服务
|
||||
animal-service:
|
||||
build:
|
||||
context: ./animal-service
|
||||
container_name: jiebanke-animal
|
||||
ports:
|
||||
- "8084:8084"
|
||||
networks:
|
||||
- jiebanke-network
|
||||
depends_on:
|
||||
- eureka-server
|
||||
- mysql
|
||||
|
||||
# 订单服务
|
||||
order-service:
|
||||
build:
|
||||
context: ./order-service
|
||||
container_name: jiebanke-order
|
||||
ports:
|
||||
- "8085:8085"
|
||||
networks:
|
||||
- jiebanke-network
|
||||
depends_on:
|
||||
- eureka-server
|
||||
- mysql
|
||||
|
||||
# 推广服务
|
||||
promotion-service:
|
||||
build:
|
||||
context: ./promotion-service
|
||||
container_name: jiebanke-promotion
|
||||
ports:
|
||||
- "8086:8086"
|
||||
networks:
|
||||
- jiebanke-network
|
||||
depends_on:
|
||||
- eureka-server
|
||||
- mysql
|
||||
|
||||
volumes:
|
||||
mysql_data:
|
||||
|
||||
networks:
|
||||
jiebanke-network:
|
||||
driver: bridge
|
||||
@@ -1,32 +0,0 @@
|
||||
<?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>eureka-server</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<!-- Eureka Server -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,13 +0,0 @@
|
||||
package com.jiebanke.eureka;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableEurekaServer
|
||||
public class EurekaServerApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(EurekaServerApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
server:
|
||||
port: 8761
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: eureka-server
|
||||
|
||||
eureka:
|
||||
instance:
|
||||
hostname: localhost
|
||||
client:
|
||||
register-with-eureka: false
|
||||
fetch-registry: false
|
||||
service-url:
|
||||
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
|
||||
server:
|
||||
enable-self-preservation: false
|
||||
@@ -1,17 +0,0 @@
|
||||
server:
|
||||
port: 8761
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: eureka-server
|
||||
|
||||
eureka:
|
||||
instance:
|
||||
hostname: localhost
|
||||
client:
|
||||
register-with-eureka: false
|
||||
fetch-registry: false
|
||||
service-url:
|
||||
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
|
||||
server:
|
||||
enable-self-preservation: false
|
||||
Binary file not shown.
@@ -1,54 +0,0 @@
|
||||
<?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>gateway-service</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<!-- Spring Cloud Gateway -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-gateway</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Eureka Client -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- JWT -->
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-impl</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.jiebanke.gateway;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
|
||||
import org.springframework.cloud.gateway.route.RouteLocator;
|
||||
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableDiscoveryClient
|
||||
public class GatewayApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(GatewayApplication.class, args);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
|
||||
return builder.routes()
|
||||
.route("auth-service", r -> r.path("/api/auth/**")
|
||||
.uri("lb://auth-service"))
|
||||
.route("user-service", r -> r.path("/api/users/**")
|
||||
.uri("lb://user-service"))
|
||||
.route("travel-service", r -> r.path("/api/travel/**")
|
||||
.uri("lb://travel-service"))
|
||||
.route("animal-service", r -> r.path("/api/animals/**")
|
||||
.uri("lb://animal-service"))
|
||||
.route("order-service", r -> r.path("/api/orders/**")
|
||||
.uri("lb://order-service"))
|
||||
.route("promotion-service", r -> r.path("/api/promotion/**")
|
||||
.uri("lb://promotion-service"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: gateway-service
|
||||
cloud:
|
||||
gateway:
|
||||
routes:
|
||||
- id: auth-service
|
||||
uri: lb://auth-service
|
||||
predicates:
|
||||
- Path=/api/auth/**
|
||||
- id: user-service
|
||||
uri: lb://user-service
|
||||
predicates:
|
||||
- Path=/api/users/**
|
||||
- id: travel-service
|
||||
uri: lb://travel-service
|
||||
predicates:
|
||||
- Path=/api/travel/**
|
||||
- id: animal-service
|
||||
uri: lb://animal-service
|
||||
predicates:
|
||||
- Path=/api/animals/**
|
||||
- id: order-service
|
||||
uri: lb://order-service
|
||||
predicates:
|
||||
- Path=/api/orders/**
|
||||
- id: promotion-service
|
||||
uri: lb://promotion-service
|
||||
predicates:
|
||||
- Path=/api/promotion/**
|
||||
|
||||
eureka:
|
||||
client:
|
||||
service-url:
|
||||
defaultZone: http://localhost:8761/eureka/
|
||||
@@ -1,38 +0,0 @@
|
||||
server:
|
||||
port: 8080
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: gateway-service
|
||||
cloud:
|
||||
gateway:
|
||||
routes:
|
||||
- id: auth-service
|
||||
uri: lb://auth-service
|
||||
predicates:
|
||||
- Path=/api/auth/**
|
||||
- id: user-service
|
||||
uri: lb://user-service
|
||||
predicates:
|
||||
- Path=/api/users/**
|
||||
- id: travel-service
|
||||
uri: lb://travel-service
|
||||
predicates:
|
||||
- Path=/api/travel/**
|
||||
- id: animal-service
|
||||
uri: lb://animal-service
|
||||
predicates:
|
||||
- Path=/api/animals/**
|
||||
- id: order-service
|
||||
uri: lb://order-service
|
||||
predicates:
|
||||
- Path=/api/orders/**
|
||||
- id: promotion-service
|
||||
uri: lb://promotion-service
|
||||
predicates:
|
||||
- Path=/api/promotion/**
|
||||
|
||||
eureka:
|
||||
client:
|
||||
service-url:
|
||||
defaultZone: http://localhost:8761/eureka/
|
||||
Binary file not shown.
@@ -1,56 +0,0 @@
|
||||
<?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>order-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>
|
||||
|
||||
<!-- 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>
|
||||
@@ -1,19 +0,0 @@
|
||||
package com.jiebanke.order;
|
||||
|
||||
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.order.mapper")
|
||||
@ComponentScan(basePackages = "com.jiebanke")
|
||||
public class OrderApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(OrderApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -1,140 +0,0 @@
|
||||
package com.jiebanke.order.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.jiebanke.common.vo.ApiResponse;
|
||||
import com.jiebanke.order.entity.Order;
|
||||
import com.jiebanke.order.service.OrderService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/orders")
|
||||
public class OrderController {
|
||||
|
||||
@Autowired
|
||||
private OrderService orderService;
|
||||
|
||||
/**
|
||||
* 创建订单
|
||||
*/
|
||||
@PostMapping
|
||||
public ApiResponse<Map<String, Object>> createOrder(
|
||||
@RequestHeader("userId") Long userId,
|
||||
@RequestBody Order order) {
|
||||
|
||||
Long orderId = orderService.createOrder(order, userId);
|
||||
Order createdOrder = orderService.getOrderById(orderId);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("order", createdOrder);
|
||||
result.put("message", "订单创建成功");
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单详情
|
||||
*/
|
||||
@GetMapping("/{orderId}")
|
||||
public ApiResponse<Order> getOrder(@PathVariable Long orderId) {
|
||||
Order order = orderService.getOrderById(orderId);
|
||||
return ApiResponse.success(order);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户订单列表
|
||||
*/
|
||||
@GetMapping
|
||||
public ApiResponse<Map<String, Object>> getUserOrders(
|
||||
@RequestHeader("userId") Long userId,
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) String status) {
|
||||
|
||||
IPage<Order> orders = orderService.getUserOrders(userId, page, pageSize, status);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("orders", orders.getRecords());
|
||||
result.put("pagination", Map.of(
|
||||
"page", orders.getCurrent(),
|
||||
"pageSize", orders.getSize(),
|
||||
"total", orders.getTotal(),
|
||||
"totalPages", orders.getPages()
|
||||
));
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新订单状态
|
||||
*/
|
||||
@PutMapping("/{orderId}/status")
|
||||
public ApiResponse<Map<String, Object>> updateOrderStatus(
|
||||
@PathVariable Long orderId,
|
||||
@RequestBody Map<String, String> requestBody,
|
||||
@RequestHeader("userId") Long userId) {
|
||||
|
||||
String status = requestBody.get("status");
|
||||
Order updatedOrder = orderService.updateOrderStatus(orderId, status, userId);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("order", updatedOrder);
|
||||
result.put("message", "订单状态更新成功");
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除订单
|
||||
*/
|
||||
@DeleteMapping("/{orderId}")
|
||||
public ApiResponse<Map<String, Object>> deleteOrder(
|
||||
@PathVariable Long orderId,
|
||||
@RequestHeader("userId") Long userId) {
|
||||
|
||||
boolean deleted = orderService.deleteOrder(orderId, userId);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("message", "订单删除成功");
|
||||
result.put("orderId", orderId);
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单统计信息
|
||||
*/
|
||||
@GetMapping("/statistics")
|
||||
public ApiResponse<Map<String, Object>> getOrderStatistics(@RequestHeader("merchantId") Long merchantId) {
|
||||
Map<String, Object> statistics = orderService.getOrderStats(merchantId);
|
||||
return ApiResponse.success(statistics);
|
||||
}
|
||||
|
||||
/**
|
||||
* 管理员获取所有订单
|
||||
*/
|
||||
@GetMapping("/admin")
|
||||
public ApiResponse<Map<String, Object>> getAllOrders(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) String status,
|
||||
@RequestParam(required = false) Long merchantId,
|
||||
@RequestParam(required = false) Long userId) {
|
||||
|
||||
IPage<Order> orders = orderService.getAllOrders(page, pageSize, status, merchantId, userId);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("orders", orders.getRecords());
|
||||
result.put("pagination", Map.of(
|
||||
"page", orders.getCurrent(),
|
||||
"pageSize", orders.getSize(),
|
||||
"total", orders.getTotal(),
|
||||
"totalPages", orders.getPages()
|
||||
));
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
package com.jiebanke.order.entity;
|
||||
|
||||
import com.jiebanke.common.entity.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class Order extends BaseEntity {
|
||||
private Long userId;
|
||||
private String orderNo;
|
||||
private BigDecimal amount;
|
||||
private String status;
|
||||
private String type;
|
||||
private String description;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
package com.jiebanke.order.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.jiebanke.order.entity.Order;
|
||||
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 OrderMapper extends BaseMapper<Order> {
|
||||
|
||||
/**
|
||||
* 根据用户ID获取订单列表
|
||||
* @param userId 用户ID
|
||||
* @return 订单列表
|
||||
*/
|
||||
@Select("SELECT * FROM orders WHERE user_id = #{userId} ORDER BY created_at DESC")
|
||||
List<Order> selectByUserId(@Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 根据状态获取订单列表
|
||||
* @param status 状态
|
||||
* @return 订单列表
|
||||
*/
|
||||
@Select("SELECT * FROM orders WHERE status = #{status} ORDER BY created_at DESC")
|
||||
List<Order> selectByStatus(@Param("status") String status);
|
||||
|
||||
/**
|
||||
* 根据订单号获取订单
|
||||
* @param orderNo 订单号
|
||||
* @return 订单
|
||||
*/
|
||||
@Select("SELECT * FROM orders WHERE order_no = #{orderNo}")
|
||||
Order selectByOrderNo(@Param("orderNo") String orderNo);
|
||||
}
|
||||
@@ -1,70 +0,0 @@
|
||||
package com.jiebanke.order.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.jiebanke.order.entity.Order;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface OrderService extends IService<Order> {
|
||||
|
||||
/**
|
||||
* 创建订单
|
||||
* @param order 订单信息
|
||||
* @param userId 用户ID
|
||||
* @return 创建的订单ID
|
||||
*/
|
||||
Long createOrder(Order order, Long userId);
|
||||
|
||||
/**
|
||||
* 根据ID获取订单
|
||||
* @param orderId 订单ID
|
||||
* @return 订单信息
|
||||
*/
|
||||
Order getOrderById(Long orderId);
|
||||
|
||||
/**
|
||||
* 获取用户订单列表
|
||||
* @param userId 用户ID
|
||||
* @param page 页码
|
||||
* @param pageSize 每页数量
|
||||
* @param status 状态
|
||||
* @return 订单分页列表
|
||||
*/
|
||||
IPage<Order> getUserOrders(Long userId, Integer page, Integer pageSize, String status);
|
||||
|
||||
/**
|
||||
* 更新订单状态
|
||||
* @param orderId 订单ID
|
||||
* @param status 新状态
|
||||
* @param userId 操作人ID
|
||||
* @return 更新后的订单
|
||||
*/
|
||||
Order updateOrderStatus(Long orderId, String status, Long userId);
|
||||
|
||||
/**
|
||||
* 删除订单(软删除)
|
||||
* @param orderId 订单ID
|
||||
* @param userId 用户ID
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
boolean deleteOrder(Long orderId, Long userId);
|
||||
|
||||
/**
|
||||
* 获取订单统计信息
|
||||
* @param merchantId 商家ID
|
||||
* @return 统计信息
|
||||
*/
|
||||
Map<String, Object> getOrderStats(Long merchantId);
|
||||
|
||||
/**
|
||||
* 获取所有订单(管理员)
|
||||
* @param page 页码
|
||||
* @param pageSize 每页数量
|
||||
* @param status 状态
|
||||
* @param merchantId 商家ID
|
||||
* @param userId 用户ID
|
||||
* @return 订单分页列表
|
||||
*/
|
||||
IPage<Order> getAllOrders(Integer page, Integer pageSize, String status, Long merchantId, Long userId);
|
||||
}
|
||||
@@ -1,159 +0,0 @@
|
||||
package com.jiebanke.order.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.order.entity.Order;
|
||||
import com.jiebanke.order.mapper.OrderMapper;
|
||||
import com.jiebanke.order.service.OrderService;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Service
|
||||
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements OrderService {
|
||||
|
||||
@Override
|
||||
public Long createOrder(Order order, Long userId) {
|
||||
// 生成订单号
|
||||
String orderNo = "ORD" + System.currentTimeMillis() + UUID.randomUUID().toString().substring(0, 8).toUpperCase();
|
||||
order.setOrderNo(orderNo);
|
||||
order.setUserId(userId);
|
||||
|
||||
// 设置默认状态
|
||||
if (order.getStatus() == null) {
|
||||
order.setStatus("pending");
|
||||
}
|
||||
|
||||
this.save(order);
|
||||
return order.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Order getOrderById(Long orderId) {
|
||||
Order order = this.getById(orderId);
|
||||
if (order == null) {
|
||||
throw new BusinessException("订单不存在");
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<Order> getUserOrders(Long userId, Integer page, Integer pageSize, String status) {
|
||||
QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("user_id", userId);
|
||||
|
||||
if (status != null && !status.isEmpty()) {
|
||||
queryWrapper.eq("status", status);
|
||||
}
|
||||
|
||||
queryWrapper.orderByDesc("created_at");
|
||||
|
||||
Page<Order> pageObj = new Page<>(page != null ? page : 1, pageSize != null ? pageSize : 10);
|
||||
return this.page(pageObj, queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Order updateOrderStatus(Long orderId, String status, Long userId) {
|
||||
Order existingOrder = this.getById(orderId);
|
||||
if (existingOrder == null) {
|
||||
throw new BusinessException("订单不存在");
|
||||
}
|
||||
|
||||
// 验证状态是否有效
|
||||
String[] validStatuses = {"pending", "processing", "completed", "cancelled", "failed"};
|
||||
boolean isValidStatus = false;
|
||||
for (String validStatus : validStatuses) {
|
||||
if (validStatus.equals(status)) {
|
||||
isValidStatus = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isValidStatus) {
|
||||
throw new BusinessException("无效的订单状态");
|
||||
}
|
||||
|
||||
existingOrder.setStatus(status);
|
||||
this.updateById(existingOrder);
|
||||
return existingOrder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deleteOrder(Long orderId, Long userId) {
|
||||
return this.removeById(orderId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getOrderStats(Long merchantId) {
|
||||
QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
|
||||
queryWrapper.eq("merchant_id", merchantId);
|
||||
|
||||
int totalOrders = Math.toIntExact(this.count(queryWrapper));
|
||||
|
||||
QueryWrapper<Order> pendingWrapper = new QueryWrapper<>();
|
||||
pendingWrapper.eq("merchant_id", merchantId).eq("status", "pending");
|
||||
int pendingOrders = Math.toIntExact(this.count(pendingWrapper));
|
||||
|
||||
QueryWrapper<Order> processingWrapper = new QueryWrapper<>();
|
||||
processingWrapper.eq("merchant_id", merchantId).eq("status", "processing");
|
||||
int processingOrders = Math.toIntExact(this.count(processingWrapper));
|
||||
|
||||
QueryWrapper<Order> completedWrapper = new QueryWrapper<>();
|
||||
completedWrapper.eq("merchant_id", merchantId).eq("status", "completed");
|
||||
int completedOrders = Math.toIntExact(this.count(completedWrapper));
|
||||
|
||||
QueryWrapper<Order> cancelledWrapper = new QueryWrapper<>();
|
||||
cancelledWrapper.eq("merchant_id", merchantId).eq("status", "cancelled");
|
||||
int cancelledOrders = Math.toIntExact(this.count(cancelledWrapper));
|
||||
|
||||
QueryWrapper<Order> failedWrapper = new QueryWrapper<>();
|
||||
failedWrapper.eq("merchant_id", merchantId).eq("status", "failed");
|
||||
int failedOrders = Math.toIntExact(this.count(failedWrapper));
|
||||
|
||||
// 计算总收入
|
||||
QueryWrapper<Order> revenueWrapper = new QueryWrapper<>();
|
||||
revenueWrapper.eq("merchant_id", merchantId).select("SUM(amount) as totalRevenue");
|
||||
Map<String, Object> revenueMap = this.getMap(revenueWrapper);
|
||||
BigDecimal totalRevenue = revenueMap != null && revenueMap.get("totalRevenue") != null ?
|
||||
new BigDecimal(revenueMap.get("totalRevenue").toString()) : BigDecimal.ZERO;
|
||||
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
stats.put("totalOrders", totalOrders);
|
||||
stats.put("pendingOrders", pendingOrders);
|
||||
stats.put("processingOrders", processingOrders);
|
||||
stats.put("completedOrders", completedOrders);
|
||||
stats.put("cancelledOrders", cancelledOrders);
|
||||
stats.put("failedOrders", failedOrders);
|
||||
stats.put("totalRevenue", totalRevenue);
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<Order> getAllOrders(Integer page, Integer pageSize, String status, Long merchantId, Long userId) {
|
||||
QueryWrapper<Order> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
if (status != null && !status.isEmpty()) {
|
||||
queryWrapper.eq("status", status);
|
||||
}
|
||||
|
||||
if (merchantId != null) {
|
||||
queryWrapper.eq("merchant_id", merchantId);
|
||||
}
|
||||
|
||||
if (userId != null) {
|
||||
queryWrapper.eq("user_id", userId);
|
||||
}
|
||||
|
||||
queryWrapper.orderByDesc("created_at");
|
||||
|
||||
Page<Order> pageObj = new Page<>(page != null ? page : 1, pageSize != null ? pageSize : 10);
|
||||
return this.page(pageObj, queryWrapper);
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
server:
|
||||
port: 8085
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: order-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
|
||||
@@ -1,32 +0,0 @@
|
||||
server:
|
||||
port: 8085
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: order-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
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,163 +0,0 @@
|
||||
<?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>
|
||||
|
||||
<groupId>com.jiebanke</groupId>
|
||||
<artifactId>backend-java</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>结伴客Java后端</name>
|
||||
<description>结伴客Java微服务架构后端系统</description>
|
||||
|
||||
<modules>
|
||||
<module>eureka-server</module>
|
||||
<module>gateway-service</module>
|
||||
<module>auth-service</module>
|
||||
<module>user-service</module>
|
||||
<module>travel-service</module>
|
||||
<module>animal-service</module>
|
||||
<module>order-service</module>
|
||||
<module>promotion-service</module>
|
||||
<module>common</module>
|
||||
</modules>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>17</maven.compiler.source>
|
||||
<maven.compiler.target>17</maven.compiler.target>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<spring.boot.version>3.1.0</spring.boot.version>
|
||||
<spring.cloud.version>2022.0.3</spring.cloud.version>
|
||||
<mysql.version>8.0.33</mysql.version>
|
||||
<mybatis.plus.version>3.5.3.1</mybatis.plus.version>
|
||||
<junit.version>5.9.2</junit.version>
|
||||
<mockito.version>5.2.0</mockito.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<!-- Spring Boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-dependencies</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Cloud -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-dependencies</artifactId>
|
||||
<version>${spring.cloud.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- MySQL -->
|
||||
<dependency>
|
||||
<groupId>mysql</groupId>
|
||||
<artifactId>mysql-connector-java</artifactId>
|
||||
<version>${mysql.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- MyBatis Plus -->
|
||||
<dependency>
|
||||
<groupId>com.baomidou</groupId>
|
||||
<artifactId>mybatis-plus-boot-starter</artifactId>
|
||||
<version>${mybatis.plus.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- 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>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.jsonwebtoken</groupId>
|
||||
<artifactId>jjwt-jackson</artifactId>
|
||||
<version>0.11.5</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Redis -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-data-redis</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- RabbitMQ -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- OpenFeign -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-openfeign</artifactId>
|
||||
<version>${spring.cloud.version}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- JUnit 5 -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<version>${junit.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Mockito -->
|
||||
<dependency>
|
||||
<groupId>org.mockito</groupId>
|
||||
<artifactId>mockito-core</artifactId>
|
||||
<version>${mockito.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Spring Boot Test -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 结伴客公共模块 -->
|
||||
<dependency>
|
||||
<groupId>com.jiebanke</groupId>
|
||||
<artifactId>common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
<version>${spring.boot.version}</version>
|
||||
<configuration>
|
||||
<excludes>
|
||||
<exclude>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</exclude>
|
||||
</excludes>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
@@ -1,56 +0,0 @@
|
||||
<?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>promotion-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>
|
||||
|
||||
<!-- 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>
|
||||
@@ -1,19 +0,0 @@
|
||||
package com.jiebanke.promotion;
|
||||
|
||||
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.promotion.mapper"})
|
||||
@ComponentScan(basePackages = "com.jiebanke")
|
||||
public class PromotionApplication {
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(PromotionApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
package com.jiebanke.promotion.controller;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.jiebanke.common.vo.ApiResponse;
|
||||
import com.jiebanke.promotion.entity.PromotionActivity;
|
||||
import com.jiebanke.promotion.entity.RewardRecord;
|
||||
import com.jiebanke.promotion.service.PromotionService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/api/promotion")
|
||||
public class PromotionController {
|
||||
|
||||
@Autowired
|
||||
private PromotionService promotionService;
|
||||
|
||||
/**
|
||||
* 获取推广活动列表
|
||||
*/
|
||||
@GetMapping("/activities")
|
||||
public ApiResponse<Map<String, Object>> getPromotionActivities(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) String name,
|
||||
@RequestParam(required = false) String status) {
|
||||
|
||||
IPage<PromotionActivity> activities = promotionService.getPromotionActivities(page, pageSize, name, status);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("activities", activities.getRecords());
|
||||
result.put("pagination", Map.of(
|
||||
"current", activities.getCurrent(),
|
||||
"pageSize", activities.getSize(),
|
||||
"total", activities.getTotal(),
|
||||
"totalPages", activities.getPages()
|
||||
));
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取推广活动详情
|
||||
*/
|
||||
@GetMapping("/activities/{id}")
|
||||
public ApiResponse<PromotionActivity> getPromotionActivity(@PathVariable Long id) {
|
||||
PromotionActivity activity = promotionService.getPromotionActivityById(id);
|
||||
return ApiResponse.success(activity);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建推广活动
|
||||
*/
|
||||
@PostMapping("/activities")
|
||||
public ApiResponse<Map<String, Object>> createPromotionActivity(@RequestBody PromotionActivity activity) {
|
||||
Long activityId = promotionService.createPromotionActivity(activity);
|
||||
PromotionActivity createdActivity = promotionService.getPromotionActivityById(activityId);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("activity", createdActivity);
|
||||
result.put("message", "推广活动创建成功");
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新推广活动
|
||||
*/
|
||||
@PutMapping("/activities/{id}")
|
||||
public ApiResponse<Map<String, Object>> updatePromotionActivity(
|
||||
@PathVariable Long id,
|
||||
@RequestBody PromotionActivity activity) {
|
||||
|
||||
PromotionActivity updatedActivity = promotionService.updatePromotionActivity(id, activity);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("activity", updatedActivity);
|
||||
result.put("message", "推广活动更新成功");
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除推广活动
|
||||
*/
|
||||
@DeleteMapping("/activities/{id}")
|
||||
public ApiResponse<Map<String, Object>> deletePromotionActivity(@PathVariable Long id) {
|
||||
boolean deleted = promotionService.deletePromotionActivity(id);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("message", "推广活动删除成功");
|
||||
result.put("id", id);
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂停推广活动
|
||||
*/
|
||||
@PostMapping("/activities/{id}/pause")
|
||||
public ApiResponse<Map<String, Object>> pausePromotionActivity(@PathVariable Long id) {
|
||||
boolean paused = promotionService.pausePromotionActivity(id);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("message", "推广活动已暂停");
|
||||
result.put("id", id);
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复推广活动
|
||||
*/
|
||||
@PostMapping("/activities/{id}/resume")
|
||||
public ApiResponse<Map<String, Object>> resumePromotionActivity(@PathVariable Long id) {
|
||||
boolean resumed = promotionService.resumePromotionActivity(id);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("message", "推广活动已恢复");
|
||||
result.put("id", id);
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取奖励记录列表
|
||||
*/
|
||||
@GetMapping("/rewards")
|
||||
public ApiResponse<Map<String, Object>> getRewardRecords(
|
||||
@RequestParam(defaultValue = "1") Integer page,
|
||||
@RequestParam(defaultValue = "10") Integer pageSize,
|
||||
@RequestParam(required = false) String user,
|
||||
@RequestParam(required = false) String rewardType,
|
||||
@RequestParam(required = false) String status) {
|
||||
|
||||
IPage<RewardRecord> rewards = promotionService.getRewardRecords(page, pageSize, user, rewardType, status);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("rewards", rewards.getRecords());
|
||||
result.put("pagination", Map.of(
|
||||
"current", rewards.getCurrent(),
|
||||
"pageSize", rewards.getSize(),
|
||||
"total", rewards.getTotal(),
|
||||
"totalPages", rewards.getPages()
|
||||
));
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发放奖励
|
||||
*/
|
||||
@PostMapping("/rewards/{id}/issue")
|
||||
public ApiResponse<Map<String, Object>> issueReward(@PathVariable Long id) {
|
||||
boolean issued = promotionService.issueReward(id);
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("message", "奖励已发放");
|
||||
result.put("id", id);
|
||||
|
||||
return ApiResponse.success(result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取推广统计数据
|
||||
*/
|
||||
@GetMapping("/statistics")
|
||||
public ApiResponse<Map<String, Object>> getPromotionStatistics() {
|
||||
Map<String, Object> statistics = promotionService.getPromotionStatistics();
|
||||
return ApiResponse.success(statistics);
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.jiebanke.promotion.entity;
|
||||
|
||||
import com.jiebanke.common.entity.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class PromotionActivity extends BaseEntity {
|
||||
private String name;
|
||||
private String description;
|
||||
private String rewardType;
|
||||
private BigDecimal rewardAmount;
|
||||
private String status;
|
||||
private LocalDateTime startTime;
|
||||
private LocalDateTime endTime;
|
||||
private Integer maxParticipants;
|
||||
private Integer currentParticipants;
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
package com.jiebanke.promotion.entity;
|
||||
|
||||
import com.jiebanke.common.entity.BaseEntity;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class RewardRecord extends BaseEntity {
|
||||
private Long userId;
|
||||
private String userName;
|
||||
private String userPhone;
|
||||
private Long activityId;
|
||||
private String activityName;
|
||||
private String rewardType;
|
||||
private BigDecimal rewardAmount;
|
||||
private String status;
|
||||
private LocalDateTime issuedAt;
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
package com.jiebanke.promotion.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.jiebanke.promotion.entity.PromotionActivity;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface PromotionActivityMapper extends BaseMapper<PromotionActivity> {
|
||||
|
||||
/**
|
||||
* 根据状态获取推广活动列表
|
||||
* @param status 状态
|
||||
* @return 推广活动列表
|
||||
*/
|
||||
@Select("SELECT * FROM promotion_activities WHERE status = #{status} ORDER BY created_at DESC")
|
||||
List<PromotionActivity> selectByStatus(@Param("status") String status);
|
||||
|
||||
/**
|
||||
* 根据名称模糊查询推广活动
|
||||
* @param name 名称
|
||||
* @return 推广活动列表
|
||||
*/
|
||||
@Select("SELECT * FROM promotion_activities WHERE name LIKE CONCAT('%', #{name}, '%') ORDER BY created_at DESC")
|
||||
List<PromotionActivity> selectByName(@Param("name") String name);
|
||||
|
||||
/**
|
||||
* 暂停推广活动
|
||||
* @param id 活动ID
|
||||
* @return 更新记录数
|
||||
*/
|
||||
@Update("UPDATE promotion_activities SET status = 'paused', updated_at = NOW() WHERE id = #{id}")
|
||||
int pauseActivity(@Param("id") Long id);
|
||||
|
||||
/**
|
||||
* 恢复推广活动
|
||||
* @param id 活动ID
|
||||
* @return 更新记录数
|
||||
*/
|
||||
@Update("UPDATE promotion_activities SET status = 'active', updated_at = NOW() WHERE id = #{id}")
|
||||
int resumeActivity(@Param("id") Long id);
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
package com.jiebanke.promotion.mapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||
import com.jiebanke.promotion.entity.RewardRecord;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import org.apache.ibatis.annotations.Update;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface RewardRecordMapper extends BaseMapper<RewardRecord> {
|
||||
|
||||
/**
|
||||
* 根据用户ID获取奖励记录列表
|
||||
* @param userId 用户ID
|
||||
* @return 奖励记录列表
|
||||
*/
|
||||
@Select("SELECT * FROM reward_records WHERE user_id = #{userId} ORDER BY created_at DESC")
|
||||
List<RewardRecord> selectByUserId(@Param("userId") Long userId);
|
||||
|
||||
/**
|
||||
* 根据状态获取奖励记录列表
|
||||
* @param status 状态
|
||||
* @return 奖励记录列表
|
||||
*/
|
||||
@Select("SELECT * FROM reward_records WHERE status = #{status} ORDER BY created_at DESC")
|
||||
List<RewardRecord> selectByStatus(@Param("status") String status);
|
||||
|
||||
/**
|
||||
* 发放奖励
|
||||
* @param id 奖励记录ID
|
||||
* @return 更新记录数
|
||||
*/
|
||||
@Update("UPDATE reward_records SET status = 'issued', issued_at = NOW() WHERE id = #{id}")
|
||||
int issueReward(@Param("id") Long id);
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
package com.jiebanke.promotion.service;
|
||||
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.extension.service.IService;
|
||||
import com.jiebanke.promotion.entity.PromotionActivity;
|
||||
import com.jiebanke.promotion.entity.RewardRecord;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public interface PromotionService extends IService<PromotionActivity> {
|
||||
|
||||
/**
|
||||
* 获取推广活动列表
|
||||
* @param page 页码
|
||||
* @param pageSize 每页数量
|
||||
* @param name 活动名称
|
||||
* @param status 状态
|
||||
* @return 推广活动分页列表
|
||||
*/
|
||||
IPage<PromotionActivity> getPromotionActivities(Integer page, Integer pageSize, String name, String status);
|
||||
|
||||
/**
|
||||
* 获取推广活动详情
|
||||
* @param id 活动ID
|
||||
* @return 推广活动
|
||||
*/
|
||||
PromotionActivity getPromotionActivityById(Long id);
|
||||
|
||||
/**
|
||||
* 创建推广活动
|
||||
* @param activity 活动信息
|
||||
* @return 创建的活动ID
|
||||
*/
|
||||
Long createPromotionActivity(PromotionActivity activity);
|
||||
|
||||
/**
|
||||
* 更新推广活动
|
||||
* @param id 活动ID
|
||||
* @param activity 更新的活动信息
|
||||
* @return 更新后的活动
|
||||
*/
|
||||
PromotionActivity updatePromotionActivity(Long id, PromotionActivity activity);
|
||||
|
||||
/**
|
||||
* 删除推广活动
|
||||
* @param id 活动ID
|
||||
* @return 是否删除成功
|
||||
*/
|
||||
boolean deletePromotionActivity(Long id);
|
||||
|
||||
/**
|
||||
* 暂停推广活动
|
||||
* @param id 活动ID
|
||||
* @return 是否暂停成功
|
||||
*/
|
||||
boolean pausePromotionActivity(Long id);
|
||||
|
||||
/**
|
||||
* 恢复推广活动
|
||||
* @param id 活动ID
|
||||
* @return 是否恢复成功
|
||||
*/
|
||||
boolean resumePromotionActivity(Long id);
|
||||
|
||||
/**
|
||||
* 获取奖励记录列表
|
||||
* @param page 页码
|
||||
* @param pageSize 每页数量
|
||||
* @param user 用户
|
||||
* @param rewardType 奖励类型
|
||||
* @param status 状态
|
||||
* @return 奖励记录分页列表
|
||||
*/
|
||||
IPage<RewardRecord> getRewardRecords(Integer page, Integer pageSize, String user, String rewardType, String status);
|
||||
|
||||
/**
|
||||
* 发放奖励
|
||||
* @param id 奖励记录ID
|
||||
* @return 是否发放成功
|
||||
*/
|
||||
boolean issueReward(Long id);
|
||||
|
||||
/**
|
||||
* 获取推广统计数据
|
||||
* @return 统计数据
|
||||
*/
|
||||
Map<String, Object> getPromotionStatistics();
|
||||
}
|
||||
@@ -1,181 +0,0 @@
|
||||
package com.jiebanke.promotion.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.promotion.entity.PromotionActivity;
|
||||
import com.jiebanke.promotion.entity.RewardRecord;
|
||||
import com.jiebanke.promotion.mapper.PromotionActivityMapper;
|
||||
import com.jiebanke.promotion.mapper.RewardRecordMapper;
|
||||
import com.jiebanke.promotion.service.PromotionService;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@Service
|
||||
public class PromotionServiceImpl extends ServiceImpl<PromotionActivityMapper, PromotionActivity> implements PromotionService {
|
||||
|
||||
@Autowired
|
||||
private PromotionActivityMapper promotionActivityMapper;
|
||||
|
||||
@Autowired
|
||||
private RewardRecordMapper rewardRecordMapper;
|
||||
|
||||
@Override
|
||||
public IPage<PromotionActivity> getPromotionActivities(Integer page, Integer pageSize, String name, String status) {
|
||||
QueryWrapper<PromotionActivity> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
if (name != null && !name.isEmpty()) {
|
||||
queryWrapper.like("name", name);
|
||||
}
|
||||
|
||||
if (status != null && !status.isEmpty()) {
|
||||
queryWrapper.eq("status", status);
|
||||
}
|
||||
|
||||
queryWrapper.orderByDesc("created_at");
|
||||
|
||||
Page<PromotionActivity> pageObj = new Page<>(page != null ? page : 1, pageSize != null ? pageSize : 10);
|
||||
return this.page(pageObj, queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PromotionActivity getPromotionActivityById(Long id) {
|
||||
PromotionActivity activity = this.getById(id);
|
||||
if (activity == null) {
|
||||
throw new BusinessException("推广活动不存在");
|
||||
}
|
||||
return activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Long createPromotionActivity(PromotionActivity activity) {
|
||||
this.save(activity);
|
||||
return activity.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PromotionActivity updatePromotionActivity(Long id, PromotionActivity activity) {
|
||||
PromotionActivity existingActivity = this.getById(id);
|
||||
if (existingActivity == null) {
|
||||
throw new BusinessException("推广活动不存在");
|
||||
}
|
||||
|
||||
// 更新字段
|
||||
if (activity.getName() != null) {
|
||||
existingActivity.setName(activity.getName());
|
||||
}
|
||||
if (activity.getDescription() != null) {
|
||||
existingActivity.setDescription(activity.getDescription());
|
||||
}
|
||||
if (activity.getRewardType() != null) {
|
||||
existingActivity.setRewardType(activity.getRewardType());
|
||||
}
|
||||
if (activity.getRewardAmount() != null) {
|
||||
existingActivity.setRewardAmount(activity.getRewardAmount());
|
||||
}
|
||||
if (activity.getStatus() != null) {
|
||||
existingActivity.setStatus(activity.getStatus());
|
||||
}
|
||||
if (activity.getStartTime() != null) {
|
||||
existingActivity.setStartTime(activity.getStartTime());
|
||||
}
|
||||
if (activity.getEndTime() != null) {
|
||||
existingActivity.setEndTime(activity.getEndTime());
|
||||
}
|
||||
if (activity.getMaxParticipants() != null) {
|
||||
existingActivity.setMaxParticipants(activity.getMaxParticipants());
|
||||
}
|
||||
if (activity.getCurrentParticipants() != null) {
|
||||
existingActivity.setCurrentParticipants(activity.getCurrentParticipants());
|
||||
}
|
||||
|
||||
this.updateById(existingActivity);
|
||||
return existingActivity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deletePromotionActivity(Long id) {
|
||||
return this.removeById(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pausePromotionActivity(Long id) {
|
||||
int result = promotionActivityMapper.pauseActivity(id);
|
||||
return result > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean resumePromotionActivity(Long id) {
|
||||
int result = promotionActivityMapper.resumeActivity(id);
|
||||
return result > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<RewardRecord> getRewardRecords(Integer page, Integer pageSize, String user, String rewardType, String status) {
|
||||
QueryWrapper<RewardRecord> queryWrapper = new QueryWrapper<>();
|
||||
|
||||
if (user != null && !user.isEmpty()) {
|
||||
queryWrapper.like("user_name", user).or().like("user_phone", user);
|
||||
}
|
||||
|
||||
if (rewardType != null && !rewardType.isEmpty()) {
|
||||
queryWrapper.eq("reward_type", rewardType);
|
||||
}
|
||||
|
||||
if (status != null && !status.isEmpty()) {
|
||||
queryWrapper.eq("status", status);
|
||||
}
|
||||
|
||||
queryWrapper.orderByDesc("created_at");
|
||||
|
||||
Page<RewardRecord> pageObj = new Page<>(page != null ? page : 1, pageSize != null ? pageSize : 10);
|
||||
return rewardRecordMapper.selectPage(pageObj, queryWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean issueReward(Long id) {
|
||||
int result = rewardRecordMapper.issueReward(id);
|
||||
return result > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, Object> getPromotionStatistics() {
|
||||
// 获取活动总数
|
||||
int totalActivities = Math.toIntExact(this.count());
|
||||
|
||||
// 获取进行中的活动数
|
||||
QueryWrapper<PromotionActivity> activeWrapper = new QueryWrapper<>();
|
||||
activeWrapper.eq("status", "active");
|
||||
int activeActivities = Math.toIntExact(this.count(activeWrapper));
|
||||
|
||||
// 获取奖励记录总数
|
||||
int totalRewards = Math.toIntExact(rewardRecordMapper.selectCount(null));
|
||||
|
||||
// 获取已发放的奖励数
|
||||
QueryWrapper<RewardRecord> issuedWrapper = new QueryWrapper<>();
|
||||
issuedWrapper.eq("status", "issued");
|
||||
int issuedRewards = Math.toIntExact(rewardRecordMapper.selectCount(issuedWrapper));
|
||||
|
||||
// 计算总奖励金额
|
||||
QueryWrapper<RewardRecord> amountWrapper = new QueryWrapper<>();
|
||||
amountWrapper.eq("status", "issued").select("SUM(reward_amount) as totalAmount");
|
||||
Map<String, Object> amountMap = rewardRecordMapper.selectMap(amountWrapper);
|
||||
BigDecimal totalAmount = amountMap != null && amountMap.get("totalAmount") != null ?
|
||||
new BigDecimal(amountMap.get("totalAmount").toString()) : BigDecimal.ZERO;
|
||||
|
||||
Map<String, Object> statistics = new HashMap<>();
|
||||
statistics.put("totalActivities", totalActivities);
|
||||
statistics.put("activeActivities", activeActivities);
|
||||
statistics.put("totalRewards", totalRewards);
|
||||
statistics.put("issuedRewards", issuedRewards);
|
||||
statistics.put("totalAmount", totalAmount);
|
||||
|
||||
return statistics;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
server:
|
||||
port: 8086
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: promotion-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
|
||||
@@ -1,32 +0,0 @@
|
||||
server:
|
||||
port: 8086
|
||||
|
||||
spring:
|
||||
application:
|
||||
name: promotion-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
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,137 +0,0 @@
|
||||
-- 创建数据库
|
||||
CREATE DATABASE IF NOT EXISTS jiebanke CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||
USE jiebanke;
|
||||
|
||||
-- 创建管理员表
|
||||
CREATE TABLE IF NOT EXISTS admins (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(50) NOT NULL UNIQUE,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(100),
|
||||
role VARCHAR(20) DEFAULT 'admin',
|
||||
status VARCHAR(20) DEFAULT 'active',
|
||||
last_login TIMESTAMP NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 创建用户表
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
username VARCHAR(50) NOT NULL UNIQUE,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(100) UNIQUE,
|
||||
phone VARCHAR(20),
|
||||
real_name VARCHAR(100),
|
||||
id_card VARCHAR(20),
|
||||
status VARCHAR(20) DEFAULT 'active',
|
||||
balance DECIMAL(15,2) DEFAULT 0.00,
|
||||
credit_score INT DEFAULT 100,
|
||||
last_login TIMESTAMP NULL,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 创建订单表
|
||||
CREATE TABLE IF NOT EXISTS orders (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL,
|
||||
order_no VARCHAR(50) NOT NULL UNIQUE,
|
||||
amount DECIMAL(15,2) NOT NULL,
|
||||
status VARCHAR(20) DEFAULT 'pending',
|
||||
type VARCHAR(20) NOT NULL,
|
||||
description TEXT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 创建旅行计划表
|
||||
CREATE TABLE IF NOT EXISTS travel_plans (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id BIGINT NOT NULL,
|
||||
destination VARCHAR(100) NOT NULL,
|
||||
start_date DATE NOT NULL,
|
||||
end_date DATE NOT NULL,
|
||||
budget DECIMAL(10,2),
|
||||
interests TEXT,
|
||||
description TEXT,
|
||||
visibility VARCHAR(20) DEFAULT 'public',
|
||||
status VARCHAR(20) DEFAULT 'active',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 创建动物表
|
||||
CREATE TABLE IF NOT EXISTS animals (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
merchant_id BIGINT NOT NULL,
|
||||
name VARCHAR(50) NOT NULL,
|
||||
species VARCHAR(50) NOT NULL,
|
||||
breed VARCHAR(50),
|
||||
birth_date DATE,
|
||||
personality TEXT,
|
||||
farm_location VARCHAR(255),
|
||||
price DECIMAL(10,2) NOT NULL,
|
||||
claim_count INT DEFAULT 0,
|
||||
status VARCHAR(20) DEFAULT 'available',
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 创建推广活动表
|
||||
CREATE TABLE IF NOT EXISTS promotion_activities (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description TEXT,
|
||||
reward_type VARCHAR(20),
|
||||
reward_amount DECIMAL(10,2),
|
||||
status VARCHAR(20) DEFAULT 'active',
|
||||
start_time TIMESTAMP,
|
||||
end_time TIMESTAMP,
|
||||
max_participants INT,
|
||||
current_participants INT DEFAULT 0,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 创建奖励记录表
|
||||
CREATE TABLE IF NOT EXISTS reward_records (
|
||||
id BIGINT AUTO_INCREMENT PRIMARY KEY,
|
||||
user_id BIGINT,
|
||||
user_name VARCHAR(50),
|
||||
user_phone VARCHAR(20),
|
||||
activity_id BIGINT,
|
||||
activity_name VARCHAR(100),
|
||||
reward_type VARCHAR(20),
|
||||
reward_amount DECIMAL(10,2),
|
||||
status VARCHAR(20) DEFAULT 'pending',
|
||||
issued_at TIMESTAMP,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
-- 插入默认管理员账号
|
||||
INSERT INTO admins (username, password, email, role) VALUES
|
||||
('admin', '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'admin@jiebanke.com', 'super_admin'),
|
||||
('manager', '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'manager@jiebanke.com', 'admin');
|
||||
|
||||
-- 插入测试用户账号
|
||||
INSERT INTO users (username, password, email, phone, real_name, id_card, balance, credit_score) VALUES
|
||||
('user1', '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'user1@jiebanke.com', '13800138001', '张三', '110101199001011234', 1000.00, 95),
|
||||
('user2', '$2a$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'user2@jiebanke.com', '13800138002', '李四', '110101199002022345', 500.00, 85);
|
||||
|
||||
-- 创建索引
|
||||
CREATE INDEX idx_admins_username ON admins(username);
|
||||
CREATE INDEX idx_admins_email ON admins(email);
|
||||
CREATE INDEX idx_users_username ON users(username);
|
||||
CREATE INDEX idx_users_email ON users(email);
|
||||
CREATE INDEX idx_users_phone ON users(phone);
|
||||
CREATE INDEX idx_orders_user_id ON orders(user_id);
|
||||
CREATE INDEX idx_orders_order_no ON orders(order_no);
|
||||
CREATE INDEX idx_orders_status ON orders(status);
|
||||
CREATE INDEX idx_travel_plans_user_id ON travel_plans(user_id);
|
||||
CREATE INDEX idx_travel_plans_destination ON travel_plans(destination);
|
||||
CREATE INDEX idx_animals_species ON animals(species);
|
||||
CREATE INDEX idx_animals_status ON animals(status);
|
||||
CREATE INDEX idx_promotion_activities_status ON promotion_activities(status);
|
||||
CREATE INDEX idx_reward_records_user_id ON reward_records(user_id);
|
||||
CREATE INDEX idx_reward_records_activity_id ON reward_records(activity_id);
|
||||
@@ -1,92 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 启动结伴客Java后端服务脚本
|
||||
|
||||
# 启动顺序:Eureka Server -> 其他服务 -> Gateway
|
||||
|
||||
echo "开始启动结伴客Java后端服务..."
|
||||
|
||||
# 启动Eureka Server
|
||||
echo "正在启动Eureka Server..."
|
||||
cd eureka-server
|
||||
mvn spring-boot:run > eureka.log 2>&1 &
|
||||
EUREKA_PID=$!
|
||||
cd ..
|
||||
|
||||
sleep 10
|
||||
|
||||
# 启动Auth Service
|
||||
echo "正在启动Auth Service..."
|
||||
cd auth-service
|
||||
mvn spring-boot:run > auth.log 2>&1 &
|
||||
AUTH_PID=$!
|
||||
cd ..
|
||||
|
||||
sleep 5
|
||||
|
||||
# 启动User Service
|
||||
echo "正在启动User Service..."
|
||||
cd user-service
|
||||
mvn spring-boot:run > user.log 2>&1 &
|
||||
USER_PID=$!
|
||||
cd ..
|
||||
|
||||
sleep 5
|
||||
|
||||
# 启动Travel Service
|
||||
echo "正在启动Travel Service..."
|
||||
cd travel-service
|
||||
mvn spring-boot:run > travel.log 2>&1 &
|
||||
TRAVEL_PID=$!
|
||||
cd ..
|
||||
|
||||
sleep 5
|
||||
|
||||
# 启动Animal Service
|
||||
echo "正在启动Animal Service..."
|
||||
cd animal-service
|
||||
mvn spring-boot:run > animal.log 2>&1 &
|
||||
ANIMAL_PID=$!
|
||||
cd ..
|
||||
|
||||
sleep 5
|
||||
|
||||
# 启动Order Service
|
||||
echo "正在启动Order Service..."
|
||||
cd order-service
|
||||
mvn spring-boot:run > order.log 2>&1 &
|
||||
ORDER_PID=$!
|
||||
cd ..
|
||||
|
||||
sleep 5
|
||||
|
||||
# 启动Promotion Service
|
||||
echo "正在启动Promotion Service..."
|
||||
cd promotion-service
|
||||
mvn spring-boot:run > promotion.log 2>&1 &
|
||||
PROMOTION_PID=$!
|
||||
cd ..
|
||||
|
||||
sleep 5
|
||||
|
||||
# 启动Gateway Service
|
||||
echo "正在启动Gateway Service..."
|
||||
cd gateway-service
|
||||
mvn spring-boot:run > gateway.log 2>&1 &
|
||||
GATEWAY_PID=$!
|
||||
cd ..
|
||||
|
||||
echo "所有服务已启动!"
|
||||
echo "Eureka Server PID: $EUREKA_PID"
|
||||
echo "Auth Service PID: $AUTH_PID"
|
||||
echo "User Service PID: $USER_PID"
|
||||
echo "Travel Service PID: $TRAVEL_PID"
|
||||
echo "Animal Service PID: $ANIMAL_PID"
|
||||
echo "Order Service PID: $ORDER_PID"
|
||||
echo "Promotion Service PID: $PROMOTION_PID"
|
||||
echo "Gateway Service PID: $GATEWAY_PID"
|
||||
|
||||
echo "服务访问地址:"
|
||||
echo "Eureka Dashboard: http://localhost:8761"
|
||||
echo "API Gateway: http://localhost:8080"
|
||||
echo "API文档: http://localhost:8080/doc.html"
|
||||
@@ -1,29 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# 停止结伴客Java后端服务脚本
|
||||
|
||||
echo "开始停止结伴客Java后端服务..."
|
||||
|
||||
# 查找并停止所有相关的Java进程
|
||||
PIDS=$(ps aux | grep "com.jiebanke" | grep -v grep | awk '{print $2}')
|
||||
|
||||
if [ -z "$PIDS" ]; then
|
||||
echo "没有找到正在运行的结伴客服务"
|
||||
else
|
||||
echo "正在停止以下进程: $PIDS"
|
||||
kill $PIDS
|
||||
echo "服务已停止"
|
||||
fi
|
||||
|
||||
# 清理日志文件
|
||||
echo "清理日志文件..."
|
||||
rm -f eureka-server/eureka.log
|
||||
rm -f auth-service/auth.log
|
||||
rm -f user-service/user.log
|
||||
rm -f travel-service/travel.log
|
||||
rm -f animal-service/animal.log
|
||||
rm -f order-service/order.log
|
||||
rm -f promotion-service/promotion.log
|
||||
rm -f gateway-service/gateway.log
|
||||
|
||||
echo "清理完成"
|
||||
@@ -1,62 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,19 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user