我的第一次提交

master
zhuwenqiang 2023-11-12 17:38:51 +08:00
commit b4f95f8055
116 changed files with 5557 additions and 0 deletions

55
bwie-auth/pom.xml 100644
View File

@ -0,0 +1,55 @@
<?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.bwie</groupId>
<artifactId>bwie-1016</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>bwie-auth</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!-- common -->
<dependency>
<groupId>com.bwie</groupId>
<artifactId>bwie-common</artifactId>
</dependency>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,28 @@
package com.bwie.auth;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/16
*/
@SpringBootApplication
@EnableFeignClients
public class AuthApplication {
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class);
}
@Bean
public BCryptPasswordEncoder bCryptPasswordEncoder() {
return new BCryptPasswordEncoder();
}
}

View File

@ -0,0 +1,52 @@
package com.bwie.auth.consumer;
import com.bwie.common.constants.MQQueueNameConstants;
import com.bwie.common.domain.LoginLog;
import com.rabbitmq.client.Channel;
import lombok.extern.log4j.Log4j2;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.io.IOException;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/25
*/
@Component
@Log4j2
public class LoginLogQueueConsumer {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@RabbitListener(queuesToDeclare = {@Queue(MQQueueNameConstants.LOGIN_LOG_QUEUE_NAME)})
public void loginLogQueueConsumer(Message message, LoginLog loginLog, Channel channel) {
log.info("");
String messageId = message.getMessageProperties().getMessageId();
try {
Long add = redisTemplate.opsForSet().add(MQQueueNameConstants.LOGIN_LOG_QUEUE_NAME, messageId);
if (add == 1) {
// 正常消费 远程调用
// 手动 确认
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
log.info("");
}
} catch (Exception e) {
log.error("");
redisTemplate.opsForSet().remove(MQQueueNameConstants.LOGIN_LOG_QUEUE_NAME, messageId);
try {
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
} catch (IOException ex) {
log.error("");
}
}
}
}

View File

@ -0,0 +1,57 @@
package com.bwie.auth.consumer;
import com.bwie.common.constants.MQQueueNameConstants;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/31
*/
@Component
public class WebsiteStatsConsumer {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@RabbitListener(queuesToDeclare = {@Queue(MQQueueNameConstants.WEBSITE_STATS_QUEUE)})
public void websiteStatsConsumer(Message message, String msg, Channel channel) {
// 获取消息id
String messageId = message.getMessageProperties().getMessageId();
try {
Long count = redisTemplate.opsForSet().add("website_stats_queue", messageId);
if (count == 1) {
// 统计每日人次 访问次数 + 1
// 获取当前日期字符串 20231031
SimpleDateFormat format = new SimpleDateFormat("yyyyMMdd");
String dateStr = format.format(new Date());
// if (!redisTemplate.hasKey(dateStr)) {
// redis 自增 每次 + 1 重复登录不算
redisTemplate.opsForValue().increment(dateStr);
// }
// 消息确认
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
} catch (Exception e) {
redisTemplate.opsForSet().remove("website_stats_queue", messageId);
try {
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,86 @@
package com.bwie.auth.controller;
import com.alibaba.fastjson.JSONObject;
import com.bwie.auth.service.AuthService;
import com.bwie.common.domain.SysUser;
import com.bwie.common.domain.request.LoginRequest;
import com.bwie.common.domain.response.JwtResponse;
import com.bwie.common.result.Result;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/16
*/
@RestController
@Log4j2
public class AuthController {
@Autowired
private HttpServletRequest request;
@Autowired
private AuthService authService;
/**
*
*/
@PostMapping("send/code/{tel}")
public Result sendCode(@PathVariable String tel) {
log.info("功能名称发送短信验证码请求URI{},请求方式:{},请求参数:{}", request.getRequestURI(),
request.getMethod(), tel);
// 发送验证码
Result result = authService.sendCode(tel);
log.info("功能名称发送短信验证码请求URI{},请求方式:{},响应结果:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(result));
return result;
}
/**
*
*/
@PostMapping("login")
public Result<JwtResponse> login(@RequestBody LoginRequest loginRequest) {
log.info("功能名称短信验证码登录请求URI{},请求方式:{},请求参数:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(loginRequest));
// 登录
Result<JwtResponse> result = authService.login(loginRequest);
log.info("功能名称短信验证码登录请求URI{},请求方式:{},响应结果:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(result));
return result;
}
@PostMapping("login2")
public Result<JwtResponse> login2(@RequestBody LoginRequest loginRequest) {
log.info("功能名称短信验证码登录请求URI{},请求方式:{},请求参数:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(loginRequest));
// 登录
Result<JwtResponse> result = authService.login2(loginRequest);
log.info("功能名称短信验证码登录请求URI{},请求方式:{},响应结果:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(result));
return result;
}
/**
*
* @return
*/
@GetMapping("user/info")
public Result<SysUser> userInfo() {
log.info("功能名称查询用户信息请求URI{},请求方式:{}", request.getRequestURI(),
request.getMethod());
SysUser sysUser = authService.userInfo();
Result<SysUser> result = Result.success(sysUser);
log.info("功能名称查询用户信息请求URI{},请求方式:{},响应结果:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(result));
return result;
}
}

View File

@ -0,0 +1,24 @@
package com.bwie.auth.feign;
import com.bwie.common.domain.SysUser;
import com.bwie.common.result.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/16
*/
@FeignClient("bwie-system")
public interface SystemFeignService {
/**
*
*/
@GetMapping("sys/user/findByTel/{tel}")
public Result<SysUser> findByTel(@PathVariable String tel);
}

View File

@ -0,0 +1,37 @@
package com.bwie.auth.service;
import com.bwie.common.domain.SysUser;
import com.bwie.common.domain.request.LoginRequest;
import com.bwie.common.domain.response.JwtResponse;
import com.bwie.common.result.Result;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/16
*/
public interface AuthService {
/**
*
* @param tel
* @return
*/
Result sendCode(String tel);
/**
*
* @param loginRequest
* @return
*/
Result<JwtResponse> login(LoginRequest loginRequest);
/**
*
* @return
*/
SysUser userInfo();
Result<JwtResponse> login2(LoginRequest loginRequest);
}

View File

@ -0,0 +1,244 @@
package com.bwie.auth.service.impl;
import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.dysmsapi20170525.models.SendSmsResponseBody;
import com.bwie.auth.feign.SystemFeignService;
import com.bwie.auth.service.AuthService;
import com.bwie.common.constants.JwtConstants;
import com.bwie.common.constants.LoginConstants;
import com.bwie.common.constants.MQQueueNameConstants;
import com.bwie.common.constants.TokenConstants;
import com.bwie.common.domain.LoginLog;
import com.bwie.common.domain.SysUser;
import com.bwie.common.domain.dto.SendSmsDTO;
import com.bwie.common.domain.request.LoginRequest;
import com.bwie.common.domain.response.JwtResponse;
import com.bwie.common.result.Result;
import com.bwie.common.utils.JwtUtils;
import com.bwie.common.utils.StringUtils;
import com.bwie.common.utils.TelSmsUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/16
*/
@Service
public class AuthServiceImpl implements AuthService {
@Autowired
private SystemFeignService systemFeignService;
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private HttpServletRequest request;
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Override
public Result sendCode(String tel) {
// 验证手机号的格式
if (!validateTel(tel)) {
return Result.error("手机号格式不正确!");
}
// 验证码该手机是否属于系统用户 远程调用 springcloud openFeign (RPC remote producer call)
Result<SysUser> byTel = systemFeignService.findByTel(tel);
SysUser sysUser = byTel.getData();
if (sysUser == null) {
return Result.error("手机号不是系统用户!");
}
String code = "";
if (redisTemplate.hasKey(tel)) {
code = redisTemplate.opsForValue().get(tel);
} else {
// 生成验证码
code = RandomUtil.randomNumbers(4);
}
// 存储验证码
redisTemplate.opsForValue().set(tel, code, 5, TimeUnit.MINUTES);
SendSmsDTO sendSmsDTO = new SendSmsDTO();
sendSmsDTO.setMobile(tel);
sendSmsDTO.setKey("code");
sendSmsDTO.setValue(code);
// 发送消息到 短信队列
rabbitTemplate.convertAndSend("send_sms_queue", sendSmsDTO, message -> {
message.getMessageProperties().setMessageId(UUID.randomUUID().toString());
return message;
});
return Result.success();
}
@Override
public Result<JwtResponse> login(LoginRequest loginRequest) {
// 验证 参数
if (StringUtils.isAllBlank(loginRequest.getTel(), loginRequest.getCode())) {
addLoginLog(loginRequest.getTel(), LoginConstants.LOGIN_FAIL, "手机号或者验证码不能为空!");
return Result.error("手机号或者验证码不能为空!");
}
// 根据手机号查询
Result<SysUser> byTel = systemFeignService.findByTel(loginRequest.getTel());
SysUser sysUser = byTel.getData();
if (sysUser == null) {
addLoginLog(loginRequest.getTel(), LoginConstants.LOGIN_FAIL, "手机号不存在!");
return Result.error("手机号不存在!");
}
// 判断验证码
String code = redisTemplate.opsForValue().get(loginRequest.getTel());
if (StringUtils.isBlank(code)) {
addLoginLog(loginRequest.getTel(), LoginConstants.LOGIN_FAIL, "请先发送短信验证码!");
return Result.error("请先发送短信验证码!");
}
if (!code.equals(loginRequest.getCode())) {
addLoginLog(loginRequest.getTel(), LoginConstants.LOGIN_FAIL, "验证码错误!");
return Result.error("验证码错误!");
}
// 登录成功
addLoginLog(loginRequest.getTel(), LoginConstants.LOGIN_SUCCESS, "登录成功!");
// 使用 redis 统计人次
rabbitTemplate.convertAndSend(MQQueueNameConstants.WEBSITE_STATS_QUEUE, "1", message -> {
message.getMessageProperties().setMessageId(UUID.randomUUID().toString());
return message;
});
// 登录成功 生成 token
// 数据 将什么数据放入 token -> 根据token 解析出原来放入的数据 【对应相应的用户】
Map<String, Object> claims = new HashMap<>();
String userKey = UUID.randomUUID().toString().replaceAll("-", "");
claims.put(JwtConstants.USER_KEY, userKey);
String token = JwtUtils.createToken(claims);
// 存储用户的信息 30Min
// 设置用户的登录时间
sysUser.setLoginDate(new Date());
redisTemplate.opsForValue().set(TokenConstants.LOGIN_TOKEN_KEY + userKey, JSONObject.toJSONString(sysUser), 30, TimeUnit.MINUTES);
// 返回响应数据
JwtResponse jwtResponse = new JwtResponse();
jwtResponse.setToken(token);
jwtResponse.setExpireTime("30MIN");
return Result.success(jwtResponse);
}
/**
*
*/
private void addLoginLog(String username, int isOk, String promptInfo) {
LoginLog loginLog = new LoginLog();
loginLog.setLoginTime(new Date());
loginLog.setPromptInfo(promptInfo);
loginLog.setIsOk(isOk);
loginLog.setUsername(username);
rabbitTemplate.convertAndSend(MQQueueNameConstants.LOGIN_LOG_QUEUE_NAME, loginLog, message -> {
// 设置消息的id
message.getMessageProperties().setMessageId(UUID.randomUUID().toString());
return message;
});
}
@Override
public SysUser userInfo() {
String token = request.getHeader(TokenConstants.TOKEN);
String userKey = JwtUtils.getUserKey(token);
String userJson = redisTemplate.opsForValue().get(TokenConstants.LOGIN_TOKEN_KEY + userKey);
return JSONObject.parseObject(userJson, SysUser.class);
}
@Override
public Result<JwtResponse> login2(LoginRequest loginRequest) {
// 验证 参数
if (StringUtils.isAllBlank(loginRequest.getTel(), loginRequest.getPassword())) {
addLoginLog(loginRequest.getTel(), LoginConstants.LOGIN_FAIL, "手机号或者验证码不能为空!");
return Result.error("手机号或者验证码不能为空!");
}
// 根据手机号查询
Result<SysUser> byTel = systemFeignService.findByTel(loginRequest.getTel());
SysUser sysUser = byTel.getData();
if (sysUser == null) {
addLoginLog(loginRequest.getTel(), LoginConstants.LOGIN_FAIL, "手机号不存在!");
return Result.error("手机号不存在!");
}
// 判断验证码 第一个参数 明文 原始密码 用户输入的 第二个: 密码 数据查询的出来的 加密的
boolean matches = bCryptPasswordEncoder.matches(loginRequest.getPassword(), sysUser.getPassword());
if (!matches) {
return Result.error("密码错误!");
}
// 登录成功
addLoginLog(loginRequest.getTel(), LoginConstants.LOGIN_SUCCESS, "登录成功!");
// 登录成功 生成 token
// 数据 将什么数据放入 token -> 根据token 解析出原来放入的数据 【对应相应的用户】
Map<String, Object> claims = new HashMap<>();
String userKey = UUID.randomUUID().toString().replaceAll("-", "");
claims.put(JwtConstants.USER_KEY, userKey);
String token = JwtUtils.createToken(claims);
// 存储用户的信息 30Min
// 设置用户的登录时间
sysUser.setLoginDate(new Date());
redisTemplate.opsForValue().set(TokenConstants.LOGIN_TOKEN_KEY + userKey, JSONObject.toJSONString(sysUser), 30, TimeUnit.MINUTES);
// 返回响应数据
JwtResponse jwtResponse = new JwtResponse();
jwtResponse.setToken(token);
jwtResponse.setExpireTime("30MIN");
return Result.success(jwtResponse);
}
/**
*
*
* @param tel
* @return
*/
private boolean validateTel(String tel) {
// 定义正则表达式
Pattern compile =
Pattern.compile("^(?:(?:\\+|00)86)?1(?:(?:3[\\d])|(?:4[5-7|9])|(?:5[0-3|5-9])|(?:6[5-7])|(?:7[0-8])|(?:8[\\d])|(?:9[1|8|9]))\\d{8}$");
return compile.matcher(tel).matches();
}
public static void main(String[] args) {
String str = "{\n" +
" \"alg\": \"HS256\",\n" +
" \"typ\": \"JWT\"\n" +
"}";
// for (int i = 0; i < 10; i++) {
String js = Base64.encode(str);
System.out.println(js);
// }
String str2 = "ewogICJhbGciOiAiSFMyNTYiLAogICJ0eXAiOiAiSldUIgp9";
String encode = Base64.decodeStr(str2);
System.out.println(encode);
String s = UUID.randomUUID().toString();
String s2 = UUID.randomUUID().toString().replaceAll("-", "");
System.out.println(s);
System.out.println(s2);
}
}

View File

@ -0,0 +1,25 @@
server:
port: 9001
spring:
main:
allow-circular-references: true # 允许循环依赖
jackson: # json 序列化 和 返序列化 转换
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
application:
name: bwie-auth
profiles:
active: dev
# 配置nacos
cloud:
nacos:
discovery:
server-addr: 123.249.113.136:8848
config:
server-addr: 123.249.113.136:8848
namespace: a9b66e92-e507-47ba-9674-6f939f793aca
file-extension: yml
# 共享配置 application-dev.yml
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}

View File

@ -0,0 +1,35 @@
package com.bwie.test;
import com.alibaba.nacos.common.utils.MD5Utils;
import com.bwie.auth.AuthApplication;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/31
*/
@SpringBootTest(classes = AuthApplication.class)
public class PasswordEncoderTest {
@Autowired
private BCryptPasswordEncoder bCryptPasswordEncoder;
@Test
public void testPassEncode() {
String pwd = "123";
// 加密方法
for (int i = 0; i < 10; i++) {
// 加密方法
String encode = bCryptPasswordEncoder.encode(pwd);
System.out.println(encode);
}
// 匹配 方法
}
}

View File

@ -0,0 +1,96 @@
<?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.bwie</groupId>
<artifactId>bwie-1016</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>bwie-common</artifactId>
<dependencies>
<!-- bootstrap 启动器 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<!-- SpringCloud Alibaba Nacos -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- SpringCloud Alibaba Nacos Config -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- SpringCloud Alibaba Sentinel -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- 负载均衡-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
<!-- SpringCloud Openfeign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
</dependency>
<!-- Alibaba Fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
<!-- SpringBoot Boot Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Hibernate Validator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- Apache Lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<!-- lombok依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- hutool -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.3</version>
</dependency>
<!-- 阿里大鱼 -->
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>dysmsapi20170525</artifactId>
<version>2.0.1</version>
</dependency>
<!-- RabbitMQ -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,48 @@
package com.bwie.common.config;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* @ClassName:
* @Description: broker confirm
* @Author: zhuwenqiang
* @Date: 2023/10/23
*/
@Component
public class ConfirmCallbackConfig implements RabbitTemplate.ConfirmCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
*
*
*/
@PostConstruct
public void init() {
// 设置 rabbitTemplate 的消息发送到交换机确认
rabbitTemplate.setConfirmCallback(this);
}
/**
*
*
* @param correlationData correlation data for the callback.
* @param ack true for ack, false for nack true false
* @param cause An optional cause, for nack, when available, otherwise null.
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack) {
System.out.println("消息发送到broker成功");
} else {
System.out.println("消息发送到broker失败失败的原因是" + cause);
}
}
}

View File

@ -0,0 +1,16 @@
package com.bwie.common.config;
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 {
// 消息转换配置 SimpleMessageConverter String byte[] serializable
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
}

View File

@ -0,0 +1,40 @@
package com.bwie.common.config;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/23
*/
@Component
public class ReturnsCallbackConfig implements RabbitTemplate.ReturnsCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostConstruct
public void init() {
rabbitTemplate.setReturnsCallback(this);
}
/**
*
*
* @param returnedMessage the returned message and metadata.
*/
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
System.out.println("消息" + returnedMessage.getMessage().toString() +
"被交换机" + returnedMessage.getExchange() + "回退!" +
"退回原因为:" + returnedMessage.getReplyText());
// TODO 补偿 可以再发 做日志记录
}
}

View File

@ -0,0 +1,18 @@
package com.bwie.common.constants;
/**
* @description:
* @author DongZl
*/
public class Constants {
/**
*
*/
public static final Integer SUCCESS = 200;
public static final String SUCCESS_MSG = "操作成功";
/**
*
*/
public static final Integer ERROR = 500;
public static final String ERROR_MSG = "操作异常";
}

View File

@ -0,0 +1,29 @@
package com.bwie.common.constants;
/**
* @author DongZl
* @description: Jwt
*/
public class JwtConstants {
/**
* ID
*/
public static final String DETAILS_USER_ID = "user_id";
/**
*
*/
public static final String DETAILS_USERNAME = "username";
/**
*
*/
public static final String USER_KEY = "user_key";
/**
*
*/
public final static String SECRET = "abcdefghijklmnopqrstuvwxyz";
}

View File

@ -0,0 +1,21 @@
package com.bwie.common.constants;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/25
*/
public class LoginConstants {
/**
*
*/
public static final int LOGIN_SUCCESS = 1;
/**
*
*/
public static final int LOGIN_FAIL = 2;
}

View File

@ -0,0 +1,27 @@
package com.bwie.common.constants;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/25
*/
public class MQQueueNameConstants {
/**
*
*/
public static final String LOGIN_LOG_QUEUE_NAME = "login_log_queue_name";
/**
*
*/
public static final String ADD_GOODS_ES_QUEUE_NAME = "add_goods_es_queue_name";
/**
* 访
*/
public static final String WEBSITE_STATS_QUEUE = "website_stats_queue";
}

View File

@ -0,0 +1,21 @@
package com.bwie.common.constants;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/18
*/
public class RoleConstants {
/**
*
*/
public static final int ADMIN = 1;
/**
*
*/
public static final int ORDINARY_USERS = 2;
}

View File

@ -0,0 +1,24 @@
package com.bwie.common.constants;
/**
* @author DongZl
* @description:
*/
public class TokenConstants {
/**
* 30
*/
public final static long EXPIRATION = 30;
/**
* 120
*/
public final static long REFRESH_TIME = 120;
/**
*
*/
public final static String LOGIN_TOKEN_KEY = "login_tokens:";
/**
* token
*/
public static final String TOKEN = "token";
}

View File

@ -0,0 +1,46 @@
package com.bwie.common.domain;
import lombok.Data;
import java.util.Date;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/27
*/
@Data
public class Article {
/**
* id String
*/
private String id;
/**
*
*/
private String title;
/**
*
*/
private String author;
/**
*
*/
private String address;
/**
*
*/
private Double price;
/**
*
*/
private Date createTime;
}

View File

@ -0,0 +1,20 @@
package com.bwie.common.domain;
import lombok.Data;
import java.math.BigDecimal;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/18
*/
@Data
public class Goods {
private Long id;
private String goodsName;
private BigDecimal price;
private Long createBy;
private Long typeId;
}

View File

@ -0,0 +1,18 @@
package com.bwie.common.domain;
import lombok.Data;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/18
*/
@Data
public class GoodsType {
private Long id;
private String typeName;
}

View File

@ -0,0 +1,36 @@
package com.bwie.common.domain;
import lombok.Data;
import java.util.Date;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/25
*/
@Data
public class LoginLog {
/**
*
*/
private Long id;
/**
*
*/
private String username;
/**
*
*/
private Date loginTime;
/**
* 1- 2-
*/
private Integer isOk;
/**
*
*/
private String promptInfo;
}

View File

@ -0,0 +1,28 @@
package com.bwie.common.domain;
import lombok.Data;
import java.io.Serializable;
import java.security.SecureRandom;
import java.util.Date;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/16
*/
@Data
public class SysUser implements Serializable{
private Long id;
private String tel;
private String name;
private String username;
private String password;
private String avatar;
private Integer isLock;
private Date lockTime;
private Integer role;
private Date loginDate;
}

View File

@ -0,0 +1,38 @@
package com.bwie.common.domain.dto;
import lombok.Data;
import java.util.Date;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/28
*/
@Data
public class ArticleSearchDTO {
/**
*
*/
private String title;
/**
*
*/
private String author;
/**
*
*/
private Date beginDate;
private Date endDate;
/**
*
*/
private Integer pageNum = 1;
private Integer pageSize = 2;
}

View File

@ -0,0 +1,21 @@
package com.bwie.common.domain.dto;
import lombok.Data;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/18
*/
@Data
public class GoodsQueryDTO {
private String goodsName;
private Long createBy;
private Integer pageNum = 1;
private Integer pageSize = 2;
}

View File

@ -0,0 +1,20 @@
package com.bwie.common.domain.dto;
import lombok.Data;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/24
*/
@Data
public class SendSmsDTO {
private String mobile;
private String key;
private String value;
}

View File

@ -0,0 +1,28 @@
package com.bwie.common.domain.request;
import lombok.Data;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/17
*/
@Data
public class LoginRequest {
/**
*
*/
private String tel;
/**
*
*/
private String code;
private String username;
private String password;
}

View File

@ -0,0 +1,24 @@
package com.bwie.common.domain.response;
import lombok.Data;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/17
*/
@Data
public class JwtResponse {
/**
*
*/
private String token;
/**
*
*/
private String expireTime;
}

View File

@ -0,0 +1,24 @@
package com.bwie.common.domain.vo;
import lombok.Data;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/11/1
*/
@Data
public class CountPriceAggVO {
/**
* key price 1, 2, 5
*/
private String keyName;
/**
*
*/
private Long count;
}

View File

@ -0,0 +1,22 @@
package com.bwie.common.domain.vo;
import com.bwie.common.domain.Goods;
import lombok.Data;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/18
*/
@Data
public class GoodsVO extends Goods {
/**
*
*/
private String typeName;
private String username;
}

View File

@ -0,0 +1,34 @@
package com.bwie.common.result;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @author DongZl
* @description:
*/
@Data
public class PageResult<T> implements Serializable {
/**
*
*/
private long total;
/**
*
*/
private List<T> list;
public PageResult() {
}
public PageResult(long total, List<T> list) {
this.total = total;
this.list = list;
}
public static <T> PageResult<T> toPageResult(long total, List<T> list){
return new PageResult(total , list);
}
public static <T> Result<PageResult<T>> toResult(long total, List<T> list){
return Result.success(PageResult.toPageResult(total,list));
}
}

View File

@ -0,0 +1,76 @@
package com.bwie.common.result;
import com.bwie.common.constants.Constants;
import lombok.Data;
import java.io.Serializable;
/**
* @author DongZl
* @description:
*/
@Data
public class Result<T> implements Serializable {
private static final long serialVersionUID = 1L;
/**
*
*/
public static final int SUCCESS = Constants.SUCCESS;
/**
*
*/
public static final int FAIL = Constants.ERROR;
/**
*
*/
private int code;
/**
*
*/
private String msg;
/**
*
*/
private T data;
public static <T> Result<T> success() {
return restResult(null, SUCCESS, Constants.SUCCESS_MSG);
}
public static <T> Result<T> success(T data) {
return restResult(data, SUCCESS, Constants.SUCCESS_MSG);
}
public static <T> Result<T> success(T data, String msg) {
return restResult(data, SUCCESS, msg);
}
public static <T> Result<T> error() {
return restResult(null, FAIL, Constants.ERROR_MSG);
}
public static <T> Result<T> error(String msg) {
return restResult(null, FAIL, msg);
}
public static <T> Result<T> error(T data) {
return restResult(data, FAIL, Constants.ERROR_MSG);
}
public static <T> Result<T> error(T data, String msg) {
return restResult(data, FAIL, msg);
}
public static <T> Result<T> error(int code, String msg) {
return restResult(null, code, msg);
}
private static <T> Result<T> restResult(T data, int code, String msg) {
Result<T> apiResult = new Result<>();
apiResult.setCode(code);
apiResult.setData(data);
apiResult.setMsg(msg);
return apiResult;
}
}

View File

@ -0,0 +1,108 @@
package com.bwie.common.utils;
import com.bwie.common.constants.JwtConstants;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import java.util.Map;
/**
* @description: Jwt
* @author DongZl
*/
public class JwtUtils {
/**
*
*/
public static String secret = JwtConstants.SECRET;
/**
*
*
* @param claims
* @return
*/
public static String createToken(Map<String, Object> claims){
return Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS512, secret).compact();
}
/**
*
*
* @param token
* @return
*/
public static Claims parseToken(String token){
return Jwts.parser().setSigningKey(secret).parseClaimsJws(token).getBody();
}
/**
*
*
* @param token
* @return ID
*/
public static String getUserKey(String token){
Claims claims = parseToken(token);
return getValue(claims, JwtConstants.USER_KEY);
}
/**
*
*
* @param claims
* @return ID
*/
public static String getUserKey(Claims claims){
return getValue(claims, JwtConstants.USER_KEY);
}
/**
* ID
*
* @param token
* @return ID
*/
public static String getUserId(String token){
Claims claims = parseToken(token);
return getValue(claims, JwtConstants.DETAILS_USER_ID);
}
/**
* ID
*
* @param claims
* @return ID
*/
public static String getUserId(Claims claims){
return getValue(claims, JwtConstants.DETAILS_USER_ID);
}
/**
*
*
* @param token
* @return
*/
public static String getUserName(String token){
Claims claims = parseToken(token);
return getValue(claims, JwtConstants.DETAILS_USERNAME);
}
/**
*
*
* @param claims
* @return
*/
public static String getUserName(Claims claims){
return getValue(claims, JwtConstants.DETAILS_USERNAME);
}
/**
*
*
* @param claims
* @param key
* @return
*/
public static String getValue(Claims claims, String key){
Object obj = claims.get(key);
return obj == null ? "" : obj.toString();
}
}

View File

@ -0,0 +1,68 @@
package com.bwie.common.utils;
import org.springframework.util.AntPathMatcher;
import java.util.Collection;
import java.util.List;
/**
* @author DongZl
* @description:
*/
public class StringUtils extends org.apache.commons.lang3.StringUtils {
/**
* *
*
* @param object Object
* @return true false
*/
public static boolean isNull(Object object) {
return object == null;
}
/**
* * Collection ListSetQueue
*
* @param coll Collection
* @return true false
*/
public static boolean isEmpty(Collection<?> coll) {
return isNull(coll) || coll.isEmpty();
}
/**
*
*
* @param str
* @param strs
* @return
*/
public static boolean matches(String str, List<String> strs) {
if (isEmpty(str) || isEmpty(strs)) {
return false;
}
for (String pattern : strs) {
if (isMatch(pattern, str))
{
return true;
}
}
return false;
}
/**
* url:
* ? ;
* * ;
* ** ;
*
* @param pattern
* @param url url
* @return
*/
public static boolean isMatch(String pattern, String url) {
AntPathMatcher matcher = new AntPathMatcher();
return matcher.match(pattern, url);
}
}

View File

@ -0,0 +1,89 @@
package com.bwie.common.utils;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.aliyun.teaopenapi.models.Config;
import lombok.extern.log4j.Log4j2;
import java.util.Map;
/**
*
*/
@Log4j2
public class TelSmsUtils {
/**
* AccessKeyaccessKeySecretAPI访
*/
private static String accessKeyId = "LTAI5tMRrNoxsBPgb6bYZWTW";
private static String accessKeySecret = "r9LXZtt3ewEG2DHQ6DbAc65F0AFRA7";
private static String templateCode = "SMS10001";
/**
* 访
*/
private static String endpoint = "dysmsapi.aliyuncs.com";
/**
*
*/
private static String signName = "登录验证";
/**
*
*/
private static Client client;
static {
log.info("初始化短信服务开始");
long startTime = System.currentTimeMillis();
try {
client = initClient();
log.info("初始化短信成功:{}", signName);
} catch (Exception e) {
e.printStackTrace();
}
log.info("初始化短信服务结束:耗时:{}MS", (System.currentTimeMillis() - startTime));
}
/**
*
*
* @return
* @throws Exception
*/
private static Client initClient() throws Exception {
Config config = new Config()
// 您的AccessKey ID
.setAccessKeyId(accessKeyId)
// 您的AccessKey Secret
.setAccessKeySecret(accessKeySecret);
// 访问的域名
config.endpoint = endpoint;
return new Client(config);
}
/**
*
*
* @param tel
* @param sendDataMap
*/
public static String sendSms(String tel, Map<String, String> sendDataMap) {
SendSmsRequest sendSmsRequest = new SendSmsRequest()
.setPhoneNumbers(tel)
.setSignName(signName)
.setTemplateCode(templateCode)
.setTemplateParam(JSONObject.toJSONString(sendDataMap));
SendSmsResponse sendSmsResponse = null;
try {
log.info("发送短信验证码:消息内容是:【{}】", JSONObject.toJSONString(sendDataMap));
sendSmsResponse = client.sendSms(sendSmsRequest);
} catch (Exception e) {
log.error("短信发送异常,手机号:【{}】,短信内容:【{}】,异常信息:【{}】", tel, sendDataMap, e);
}
return JSONObject.toJSONString(sendSmsResponse.getBody());
}
}

View File

@ -0,0 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.bwie.common.config.RabbitmqConfig,\
com.bwie.common.config.ConfirmCallbackConfig,\
com.bwie.common.config.ReturnsCallbackConfig

View File

@ -0,0 +1,54 @@
<?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.bwie</groupId>
<artifactId>bwie-1016</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>bwie-gateway</artifactId>
<dependencies>
<!-- 公共模块 -->
<dependency>
<groupId>com.bwie</groupId>
<artifactId>bwie-common</artifactId>
</dependency>
<!-- 网关依赖 -->
<!-- SpringCloud Gateway -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- SpringCloud Alibaba Sentinel Gateway -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>
<!-- 引入阿里巴巴sentinel限流 依赖-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,19 @@
package com.bwie.gateway;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/17
*/
@SpringBootApplication
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class);
}
}

View File

@ -0,0 +1,35 @@
package com.bwie.gateway.config;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.extern.log4j.Log4j2;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/19
*/
@Configuration
@ConfigurationProperties(prefix = "ignore")
@Data
@Log4j2
public class IgnoreWhitesConfig {
private List<String> whites = new ArrayList<>();
public void setWhites(List<String> whites) {
log.info("加载系统白名单请求:{}", JSONObject.toJSON(whites));
this.whites = whites;
}
}

View File

@ -0,0 +1,104 @@
package com.bwie.gateway.filters;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.bwie.common.constants.TokenConstants;
import com.bwie.common.domain.SysUser;
import com.bwie.common.utils.JwtUtils;
import com.bwie.common.utils.StringUtils;
import com.bwie.gateway.config.IgnoreWhitesConfig;
import com.bwie.gateway.utils.GatewayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.*;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* @ClassName:
* @Description: token
* @Author: zhuwenqiang
* @Date: 2023/10/19
*/
@Component
public class AuthFilter implements GlobalFilter, Ordered {
@Autowired
private IgnoreWhitesConfig ignoreWhitesConfig;
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
* token
* <p>
*
*
* @param exchange
* @param chain
* @return
*/
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
// 获取系统白名单请求
List<String> whites = ignoreWhitesConfig.getWhites();
// 判断 当前 请求是否是 白名单的请求
// 获取当前请求的 URI
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
if (StringUtils.matches(path, whites)) {
// 白名单请求 放行
return chain.filter(exchange);
}
// 验证 token 非空
String token = request.getHeaders().getFirst(TokenConstants.TOKEN);
if (StringUtils.isBlank(token)) {
// 提示错误信息
return GatewayUtils.errorResponse(exchange, "token不能为空");
}
// 合法性验证
String userKey = null;
try {
userKey = JwtUtils.getUserKey(token);
} catch (Exception e) {
return GatewayUtils.errorResponse(exchange, "token不合法");
}
// token 有效性验证
// 获取 userKey
if (!redisTemplate.hasKey(TokenConstants.LOGIN_TOKEN_KEY + userKey)) {
// 提示错误信息
return GatewayUtils.errorResponse(exchange, "token过期");
}
// 续期 登录在10分钟以内自动续期
// 获取用户的登录时间
String userJSON = redisTemplate.opsForValue().get(TokenConstants.LOGIN_TOKEN_KEY + userKey);
SysUser sysUser = JSONObject.parseObject(userJSON, SysUser.class);
Date loginDate = sysUser.getLoginDate();
long min = DateUtil.between(loginDate, new Date(), DateUnit.MINUTE);
if (min <= 10) {
redisTemplate.expire(TokenConstants.LOGIN_TOKEN_KEY + userKey, 30, TimeUnit.MINUTES);
}
// 放行
return chain.filter(exchange);
}
/**
*
*
* @return
*/
@Override
public int getOrder() {
return 0;
}
}

View File

@ -0,0 +1,29 @@
package com.bwie.gateway.filters;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.*;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/19
*/
//@Component
public class LogFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return null;
}
@Override
public int getOrder() {
return 1;
}
}

View File

@ -0,0 +1,102 @@
package com.bwie.gateway.utils;
import com.alibaba.fastjson.JSONObject;
import com.bwie.common.result.Result;
import com.bwie.common.utils.StringUtils;
import lombok.extern.log4j.Log4j2;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/**
* @author DongZl
* @description:
*/
@Log4j2
public class GatewayUtils {
/**
*
*
* @param mutate
* @param key
* @param value
*/
public static void addHeader(ServerHttpRequest.Builder mutate, String key, Object value) {
if (StringUtils.isEmpty(key)) {
log.warn("添加请求头参数键不可以为空");
return;
}
if (value == null) {
log.warn("添加请求头参数:[{}]值为空", key);
return;
}
String valueStr = value.toString();
mutate.header(key, valueStr);
log.info("添加请求头参数成功 - 键:[{}] , 值:[{}]", key, value);
}
/**
*
*
* @param mutate
* @param key
*/
public static void removeHeader(ServerHttpRequest.Builder mutate, String key) {
if (StringUtils.isEmpty(key)) {
log.warn("删除请求头参数键不可以为空");
return;
}
mutate.headers(httpHeaders -> httpHeaders.remove(key)).build();
log.info("删除请求头参数 - 键:[{}]", key);
}
/**
*
*
* @param exchange
* @param msg
* @return
*/
public static Mono<Void> errorResponse(ServerWebExchange exchange, String msg, HttpStatus httpStatus) {
ServerHttpResponse response = exchange.getResponse();
//设置HTTP响应头状态
response.setStatusCode(httpStatus);
//设置HTTP响应头文本格式
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, "application/json");
//定义响应内容
Result<?> result = Result.error(msg);
String resultJson = JSONObject.toJSONString(result);
log.error("[鉴权异常处理]请求路径:[{}],异常信息:[{}],响应结果:[{}]", exchange.getRequest().getPath(), msg, resultJson);
DataBuffer dataBuffer = response.bufferFactory().wrap(resultJson.getBytes());
//进行响应
return response.writeWith(Mono.just(dataBuffer));
}
/**
*
*
* @param exchange
* @param msg
* @return
*/
public static Mono<Void> errorResponse(ServerWebExchange exchange, String msg) {
ServerHttpResponse response = exchange.getResponse();
//设置HTTP响应头状态
response.setStatusCode(HttpStatus.OK);
//设置HTTP响应头文本格式
response.getHeaders().add(HttpHeaders.CONTENT_TYPE, "application/json");
//定义响应内容
Result<?> result = Result.error(msg);
String resultJson = JSONObject.toJSONString(result);
log.error("[鉴权异常处理]请求路径:[{}],异常信息:[{}],响应结果:[{}]", exchange.getRequest().getPath(), msg, resultJson);
DataBuffer dataBuffer = response.bufferFactory().wrap(resultJson.getBytes());
//进行响应
return response.writeWith(Mono.just(dataBuffer));
}
}

View File

@ -0,0 +1,30 @@
# Tomcat
server:
port: 18080
# Spring
spring:
application:
# 应用名称
name: bwie-gateway
profiles:
# 环境配置
active: dev
main:
# 允许使用循环引用
allow-circular-references: true
# 允许定义相同的bean对象 去覆盖原有的
allow-bean-definition-overriding: true
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: 123.249.113.136:8848
config:
# 配置中心地址
server-addr: 123.249.113.136:8848
# 配置文件格式
file-extension: yml
namespace: a9b66e92-e507-47ba-9674-6f939f793aca
# 共享配置
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}

View File

@ -0,0 +1,30 @@
<?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.bwie</groupId>
<artifactId>bwie-modules</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>bwie-es</artifactId>
<dependencies>
<dependency>
<groupId>com.bwie</groupId>
<artifactId>bwie-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- es -->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,23 @@
package com.bwie.es;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.scheduling.annotation.EnableScheduling;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/27
*/
@SpringBootApplication
@EnableFeignClients
@EnableScheduling
public class ESApplication {
public static void main(String[] args) {
SpringApplication.run(ESApplication.class);
}
}

View File

@ -0,0 +1,35 @@
package com.bwie.es.config;
import lombok.Data;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @ClassName:
* @Description: RestHighLevelClient
* @Author: zhuwenqiang
* @Date: 2023/10/27
*/
@Configuration
@ConfigurationProperties(prefix = "es")
@Data
public class InitRestHighLevelClient {
private String host;
private int port;
private String scheme;
@Bean
public RestHighLevelClient init() {
return new RestHighLevelClient(
RestClient.builder(new HttpHost(host, port, scheme))
);
}
}

View File

@ -0,0 +1,68 @@
package com.bwie.es.consumer;
import com.alibaba.fastjson.JSONObject;
import com.bwie.common.constants.MQQueueNameConstants;
import com.bwie.common.domain.vo.GoodsVO;
import com.bwie.es.service.GoodsService;
import com.rabbitmq.client.Channel;
import lombok.extern.log4j.Log4j2;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/30
*/
@Component
@Log4j2
public class AddGoodsConsumer {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private GoodsService goodsService;
@RabbitListener(queuesToDeclare = {@Queue(MQQueueNameConstants.ADD_GOODS_ES_QUEUE_NAME)})
public void addGoodsConsumer(Message message, GoodsVO goodsVO, Channel channel) {
log.info("消息队列:{},接收到消息,消息的内容是:{},开始消费...",
MQQueueNameConstants.ADD_GOODS_ES_QUEUE_NAME, JSONObject.toJSONString(goodsVO));
// 获取 messageId
String messageId = message.getMessageProperties().getMessageId();
try {
long count = redisTemplate.opsForSet().add(MQQueueNameConstants.ADD_GOODS_ES_QUEUE_NAME, messageId);
if (count == 1) {
// 正常消费
List<GoodsVO> list = Arrays.asList(goodsVO);
goodsService.batchAdd(list);
// 确认
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
log.info("消息队列:{},接收到消息,消息的内容是:{},消费成功...",
MQQueueNameConstants.ADD_GOODS_ES_QUEUE_NAME, JSONObject.toJSONString(goodsVO));
}
} catch (Exception e) {
log.error("消息队列:{},接收到消息,消息的内容是:{},消费消息异常,异常信息:{}",
MQQueueNameConstants.ADD_GOODS_ES_QUEUE_NAME, JSONObject.toJSONString(goodsVO), e);
redisTemplate.opsForSet().remove(MQQueueNameConstants.ADD_GOODS_ES_QUEUE_NAME, messageId);
try {
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
} catch (IOException ex) {
log.error("消息队列:{},接收到消息,消息的内容是:{},消息回退异常,异常信息:{}",
MQQueueNameConstants.ADD_GOODS_ES_QUEUE_NAME, JSONObject.toJSONString(goodsVO), ex);
}
}
}
}

View File

@ -0,0 +1,157 @@
package com.bwie.es.cotroller;
import com.alibaba.fastjson.JSONObject;
import com.bwie.common.domain.Article;
import com.bwie.common.domain.dto.ArticleSearchDTO;
import com.bwie.common.domain.vo.CountPriceAggVO;
import com.bwie.common.result.PageResult;
import com.bwie.common.result.Result;
import com.bwie.es.service.ArticleService;
import com.bwie.es.service.GoodsService;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/27
*/
@RestController
@RequestMapping("es/article")
@Log4j2
public class ArticleController {
@Autowired
private HttpServletRequest request;
@Autowired
private ArticleService articleService;
/**
*
*/
@PostMapping
public Result add(@RequestBody Article article) {
log.info("功能名称添加文章到ES请求URI{},请求方式:{},请求参数:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(article));
articleService.add(article);
Result<Object> result = Result.success();
log.info("功能名称添加文章到ES请求URI{},请求方式:{},响应结果:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(result));
return result;
}
/**
*
*/
@PostMapping("batchAdd")
public Result batchAdd(@RequestBody List<Article> articles) {
log.info("功能名称批量添加文章到ES请求URI{},请求方式:{},请求参数:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(articles));
articleService.batchAdd(articles);
Result<Object> result = Result.success();
log.info("功能名称批量添加文章到ES请求URI{},请求方式:{},响应结果:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(result));
return result;
}
/**
*
*/
@PutMapping
public Result update(@RequestBody Article article) {
log.info("功能名称修改文章到ES请求URI{},请求方式:{},请求参数:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(article));
articleService.update(article);
Result<Object> result = Result.success();
log.info("功能名称修改文章到ES请求URI{},请求方式:{},响应结果:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(result));
return result;
}
/**
*
*/
@DeleteMapping("delete/{id}")
public Result delete(@PathVariable String id) {
log.info("功能名称删除文章请求URI{},请求方式:{},请求参数:{}", request.getRequestURI(),
request.getMethod(), id);
articleService.delete(id);
Result<Object> result = Result.success();
log.info("功能名称删除文章请求URI{},请求方式:{},响应结果:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(result));
return result;
}
/**
*
*/
@GetMapping("findById/{id}")
public Result<Article> findById(@PathVariable String id) {
log.info("功能名称主键查询文章请求URI{},请求方式:{},请求参数:{}", request.getRequestURI(),
request.getMethod(), id);
Article article = articleService.findById(id);
Result<Article> result = Result.success(article);
log.info("功能名称主键查询文章请求URI{},请求方式:{},响应结果:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(result));
return result;
}
/**
*
*/
@PostMapping("search")
public Result<PageResult<Article>> search(@RequestBody ArticleSearchDTO articleSearchDTO) {
log.info("功能名称文章搜索请求URI{},请求方式:{},请求参数:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(articleSearchDTO));
Result<PageResult<Article>> result = articleService.search(articleSearchDTO);
log.info("功能名称文章搜索请求URI{},请求方式:{},响应结果:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(result));
return result;
}
@Autowired
private GoodsService goodsService;
/**
*
*/
@GetMapping("maxPrice")
public Result<Double> maxPrice() {
log.info("功能名称查询商品最大价格请求URI{},请求方式:{}", request.getRequestURI(),
request.getMethod());
Double maxPrice = goodsService.maxPrice();
Result<Double> result = Result.success(maxPrice);
log.info("功能名称查询商品最大价格请求URI{},请求方式:{},响应结果:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(result));
return result;
}
/**
* DTO
* VO
* BO Business Object
*
* @return
*/
@GetMapping("countPrice")
public Result<List<CountPriceAggVO>> countPrice() {
log.info("功能名称查询每种价格商品的数量请求URI{},请求方式:{}", request.getRequestURI(),
request.getMethod());
List<CountPriceAggVO> countPrices = goodsService.countPrice();
Result<List<CountPriceAggVO>> result = Result.success(countPrices);
log.info("功能名称查询每种价格商品的数量请求URI{},请求方式:{},响应结果:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(result));
return result;
}
}

View File

@ -0,0 +1,26 @@
package com.bwie.es.remote;
import com.bwie.common.domain.vo.GoodsVO;
import com.bwie.common.result.Result;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import java.util.List;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/30
*/
@FeignClient("bwie-goods")
public interface GoodsFeignService {
/**
*
* @return
*/
@GetMapping("findAll")
public Result<List<GoodsVO>> findAll();
}

View File

@ -0,0 +1,54 @@
package com.bwie.es.service;
import com.bwie.common.domain.Article;
import com.bwie.common.domain.dto.ArticleSearchDTO;
import com.bwie.common.result.PageResult;
import com.bwie.common.result.Result;
import java.util.List;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/28
*/
public interface ArticleService {
/**
* ES
* @param article
*/
void add(Article article);
/**
* ES
* @param articles
*/
void batchAdd(List<Article> articles);
/**
* ES
* @param article
*/
void update(Article article);
/**
*
* @param id
*/
void delete(String id);
/**
*
* @param id
* @return
*/
Article findById(String id);
/**
*
* @param articleSearchDTO
* @return
*/
Result<PageResult<Article>> search(ArticleSearchDTO articleSearchDTO);
}

View File

@ -0,0 +1,23 @@
package com.bwie.es.service;
import com.bwie.common.domain.vo.CountPriceAggVO;
import com.bwie.common.domain.vo.GoodsVO;
import java.util.List;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/30
*/
public interface GoodsService {
void batchAdd(List<GoodsVO> goods);
Double maxPrice();
List<CountPriceAggVO> countPrice();
}

View File

@ -0,0 +1,274 @@
package com.bwie.es.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.bwie.common.constants.TokenConstants;
import com.bwie.common.domain.Article;
import com.bwie.common.domain.SysUser;
import com.bwie.common.domain.dto.ArticleSearchDTO;
import com.bwie.common.result.PageResult;
import com.bwie.common.result.Result;
import com.bwie.common.utils.JwtUtils;
import com.bwie.common.utils.StringUtils;
import com.bwie.es.service.ArticleService;
import lombok.extern.log4j.Log4j2;
import org.bouncycastle.cert.ocsp.Req;
import org.elasticsearch.action.ActionListener;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.naming.directory.SearchResult;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.*;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/28
*/
@Service
@Log4j2
public class ArticleServiceImpl implements ArticleService {
/**
*
*/
private static final String INDEX_NAME = "article";
@Autowired
private RestHighLevelClient restHighLevelClient;
@Autowired
private HttpServletRequest request;
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Override
public void add(Article article) {
try {
// 创建 IndexRequest对象
IndexRequest indexRequest = new IndexRequest(INDEX_NAME);
// 设置文档 id 可选
indexRequest.id(UUID.randomUUID().toString());
// 设置添加时间
article.setCreateTime(new Date());
// 设置添加的数据【JSON格式】
indexRequest.source(JSONObject.toJSONString(article), XContentType.JSON);
// 同步执行
restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
// 异步执行
// restHighLevelClient.indexAsync(indexRequest, RequestOptions.DEFAULT, new ActionListener<IndexResponse>() {
// /**
// * 执行成功
// * @param indexResponse
// */
// @Override
// public void onResponse(IndexResponse indexResponse) {
//
// }
//
// /**
// * 执行失败
// * @param e
// */
// @Override
// public void onFailure(Exception e) {
//
// }
// });
} catch (IOException e) {
log.error("添加文章异常,请求参数:{},异常信息:{}", JSONObject.toJSONString(article), e.getMessage());
}
}
@Override
public void batchAdd(List<Article> articles) {
try {
// 创建 BulkRequest 对象
BulkRequest bulkRequest = new BulkRequest();
// 遍历 articles 创建 IndexRequest对象
articles.forEach(article -> {
// 设置创建时间
article.setCreateTime(new Date());
bulkRequest.add(
new IndexRequest(INDEX_NAME)
.id(UUID.randomUUID().toString())
.source(JSONObject.toJSONString(article), XContentType.JSON));
});
// 执行发送请求
restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
} catch (Exception e) {
log.error("批量添加文章异常,请求参数:{},异常信息:{}", JSONObject.toJSONString(articles), e.getMessage());
}
}
@Override
public void update(Article article) {
try {
// 创建 UpdateRequest 对象
UpdateRequest updateRequest = new UpdateRequest(INDEX_NAME, article.getId());
// 设置修改的数据
updateRequest.doc(JSONObject.toJSONString(article), XContentType.JSON);
// 执行修改请求
restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
} catch (Exception e) {
log.error("修改文章异常,请求参数:{},异常信息:{}", JSONObject.toJSONString(article), e.getMessage());
}
}
@Override
public void delete(String id) {
try {
// 创建 DeleteRequest
DeleteRequest deleteRequest = new DeleteRequest(INDEX_NAME, id);
// 执行删除请求
restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
} catch (Exception e) {
log.error("删除文章,请求参数:{},异常信息:{}", id, e.getMessage());
}
}
@Override
public Article findById(String id) {
try {
// 创建 GetRequest 对象
GetRequest getRequest = new GetRequest(INDEX_NAME, id);
// 执行查询操作
GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
// 处理响应结果
if (getResponse.isExists()) {
// 获取查询到的结果
String sourceAsString = getResponse.getSourceAsString();
// 反序列化
Article article = JSONObject.parseObject(sourceAsString, Article.class);
// 设置 id
article.setId(getResponse.getId());
return article;
}
} catch (Exception e) {
log.error("主键查询文章异常,请求参数:{},异常信息:{}", id, e.getMessage());
}
return null;
}
@Override
public Result<PageResult<Article>> search(ArticleSearchDTO articleSearchDTO) {
// 定义 总记录数
long total = 0;
// 当前页数据
List<Article> articles = new ArrayList<>();
try {
// 创建SearchRequest 对象
SearchRequest searchRequest = new SearchRequest(INDEX_NAME);
// 构建 搜索条件 对象 所有的搜索条件 都需要给它
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 构建条件拼接对象
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
// 拼接查询条件
if (!StringUtils.isBlank(articleSearchDTO.getTitle())) {
boolQueryBuilder.must(QueryBuilders.matchQuery("title", articleSearchDTO.getTitle()));
}
if (!StringUtils.isEmpty(articleSearchDTO.getAuthor())) {
boolQueryBuilder.must(QueryBuilders.matchQuery("author", articleSearchDTO.getAuthor()));
}
// 区间
if (articleSearchDTO.getBeginDate() != null) {
boolQueryBuilder.must(QueryBuilders.rangeQuery("createTime").gte(articleSearchDTO.getBeginDate().getTime()));
}
if (articleSearchDTO.getEndDate() != null) {
boolQueryBuilder.must(QueryBuilders.rangeQuery("createTime").lte(articleSearchDTO.getEndDate().getTime()));
}
// 判断当前登录用户的角色
Integer role = userInfo().getRole();
if (role == 2) {
boolQueryBuilder.must(QueryBuilders.matchQuery("createBy", userInfo().getId()));
}
// 将查询条件 给 searchSourceBuilder
searchSourceBuilder.query(boolQueryBuilder);
// 分页
searchSourceBuilder.from((articleSearchDTO.getPageNum() - 1) * articleSearchDTO.getPageSize());
searchSourceBuilder.size(articleSearchDTO.getPageSize());
// 排序
searchSourceBuilder.sort("createTime", SortOrder.DESC);
// 高亮 ?? preTags 前面标签 postTags 后面标签 <span style=\"color:red;\">ddd</span> 华为<span style=\"color:red;\">手机</span>遥遥领先
searchSourceBuilder.highlighter(
new HighlightBuilder().field("title").preTags("<span style=\"color:red;\">").postTags("</span>"));
// 将 查询条件对象 给 searchRequest
searchRequest.source(searchSourceBuilder);
// 执行查询操作
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 获取查询的命中
SearchHits searchHits = searchResponse.getHits();
// 获取总记录数
total = searchHits.getTotalHits().value;
// 获取当前页数据
SearchHit[] hits = searchHits.getHits();
// 遍历数组
for (SearchHit hit : hits) {
// 获取 source
String sourceAsString = hit.getSourceAsString();
// 反序列化
Article article = JSONObject.parseObject(sourceAsString, Article.class);
// 替换高亮字段
// 获取高亮 map 集合
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if (highlightFields != null && highlightFields.size() > 0) {
HighlightField highlightField = highlightFields.get("title");
if (highlightField != null) {
// 获取 高亮 碎片
Text[] fragments = highlightField.getFragments();
// 拼接 华为手机遥遥领先 -> 华为<span style=\"color:red;\">手机</span>遥遥领先
StringBuilder sb = new StringBuilder();
for (Text fragment : fragments) {
sb.append(fragment);
}
// 替换
article.setTitle(sb.toString());
}
}
// 设置 id
article.setId(hit.getId());
// 添加到结果集
articles.add(article);
}
} catch (Exception e) {
log.error("文章搜索异常,请求参数:{},异常信息:{}", JSONObject.toJSONString(articleSearchDTO), e.getMessage());
}
// 返回结果
return PageResult.toResult(total, articles);
}
private SysUser userInfo() {
String token = request.getHeader(TokenConstants.TOKEN);
String userKey = JwtUtils.getUserKey(token);
String useJson = redisTemplate.opsForValue().get(TokenConstants.LOGIN_TOKEN_KEY + userKey);
return JSONObject.parseObject(useJson, SysUser.class);
}
}

View File

@ -0,0 +1,137 @@
package com.bwie.es.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.bwie.common.domain.vo.CountPriceAggVO;
import com.bwie.common.domain.vo.GoodsVO;
import com.bwie.es.service.GoodsService;
import org.bouncycastle.cert.ocsp.Req;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.Avg;
import org.elasticsearch.search.aggregations.metrics.Max;
import org.elasticsearch.search.aggregations.metrics.MaxAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/30
*/
@Service
public class GoodsServiceImpl implements GoodsService {
/**
*
*/
private static final String INDEX_NAME = "goods";
@Autowired
private RestHighLevelClient restHighLevelClient;
@Override
public void batchAdd(List<GoodsVO> goods) {
try {
// 分批次同步 每次取出 100
// 每次取出100条数据
int batchSize = 100;
for (int i = 0; i < goods.size(); i += batchSize) {
BulkRequest bulkRequest = new BulkRequest();
List<GoodsVO> batch = goods.subList(i, Math.min(goods.size(), i + batchSize));
batch.forEach(good -> {
bulkRequest.add(
new IndexRequest(INDEX_NAME)
.id(good.getId() + "")
.source(JSONObject.toJSONString(good), XContentType.JSON));
});
restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
}
} catch (Exception e) {
}
}
@Override
public Double maxPrice() {
Double maxPrice = 0.00;
try {
// 创建 搜索请求对象
SearchRequest searchRequest = new SearchRequest("goods");
// 构建 SearchSourceBuilder
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 构建 集合查询对象 max("聚合函数的名称") 名称随意 field("列名") 对那一列进行聚合
// 将聚合查询的条件 给 SearchSourceBuilder
searchSourceBuilder.aggregation(AggregationBuilders.avg("avg_price").field("price"));
// 将 searchSourceBuilder 给 searchRequest
searchRequest.source(searchSourceBuilder);
// 小执行查询
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 处理结果集
Avg avg = searchResponse.getAggregations().get("avg_price");
maxPrice = avg.getValue();
} catch (Exception e) {
}
return maxPrice;
}
@Override
public List<CountPriceAggVO> countPrice() {
List<CountPriceAggVO> priceAggVOList = new ArrayList<>();
try {
SearchRequest searchRequest = new SearchRequest("goods");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("count_price").field("price");
searchSourceBuilder.aggregation(aggregationBuilder);
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
Terms terms = searchResponse.getAggregations().get("count_price");
List<? extends Terms.Bucket> buckets = terms.getBuckets();
CountPriceAggVO countPriceAggVO = null;
for (Terms.Bucket bucket : buckets) {
String key = bucket.getKeyAsString();
long docCount = bucket.getDocCount();
countPriceAggVO = new CountPriceAggVO();
countPriceAggVO.setKeyName(key);
countPriceAggVO.setCount(docCount);
priceAggVOList.add(countPriceAggVO);
}
} catch (Exception e) {
}
return priceAggVOList;
}
public static void main(String[] args) {
// 假设你有一个List集合
List<String> dataList = new ArrayList<>();
for (int i = 0; i < 500; i++) {
dataList.add("Data " + i);
}
// 每次取出100条数据
int batchSize = 100;
for (int i = 0; i < dataList.size(); i += batchSize) {
List<String> batch = dataList.subList(i, Math.min(dataList.size(), i + batchSize));
// 在这里处理batch例如打印出来
System.out.println(batch);
}
}
}

View File

@ -0,0 +1,48 @@
package com.bwie.es.sync;
import com.bwie.common.domain.vo.GoodsVO;
import com.bwie.common.result.Result;
import com.bwie.es.remote.GoodsFeignService;
import com.bwie.es.service.GoodsService;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/30
*/
@Component
@Log4j2
public class SyncGoods {
@Autowired
private GoodsFeignService goodsFeignService;
@Autowired
private GoodsService goodsService;
/**
* es
*/
// @Scheduled(cron = "0 0/1 * * * ?")
public void syncGoods() {
// 查询 商品的数据
Result<List<GoodsVO>> all = goodsFeignService.findAll();
List<GoodsVO> data = all.getData();
// 同步
if (!CollectionUtils.isEmpty(data)) {
log.info("同步商品信息,查询到{}条商品信息,开始同步...", data.size());
long s = System.currentTimeMillis();
goodsService.batchAdd(data);
log.info("同步商品信息结束,用时{}毫秒...", (System.currentTimeMillis() - s));
}
}
}

View File

@ -0,0 +1,50 @@
package com.bwie.es.sync;
import com.bwie.common.domain.vo.GoodsVO;
import com.bwie.common.result.Result;
import com.bwie.es.remote.GoodsFeignService;
import com.bwie.es.service.GoodsService;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/30
*/
//@Component
@Log4j2
public class SyncGoods2 implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
}
// @Autowired
// private GoodsFeignService goodsFeignService;
//
// @Autowired
// private GoodsService goodsService;
//
// @Override
// public void run(ApplicationArguments args) throws Exception {
// // 查询 商品的数据
// Result<List<GoodsVO>> all = goodsFeignService.findAll();
// List<GoodsVO> data = all.getData();
// // 同步
// if (!CollectionUtils.isEmpty(data)) {
// log.info("同步商品信息,查询到{}条商品信息,开始同步...", data.size());
// long s = System.currentTimeMillis();
// goodsService.batchAdd(data);
// log.info("同步商品信息结束,用时{}毫秒...", (System.currentTimeMillis() - s));
// }
// }
}

View File

@ -0,0 +1,34 @@
# Tomcat
server:
port: 9007
# Spring
spring:
main:
allow-circular-references: true # 允许循环依赖
jackson: # json 序列化 和 返序列化 转换
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
application:
# 应用名称
name: bwie-es
profiles:
# 环境配置
active: dev
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: 123.249.113.136:8848
config:
# 配置中心地址
server-addr: 123.249.113.136:8848
namespace: a9b66e92-e507-47ba-9674-6f939f793aca
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}
es:
host: 123.249.113.136
port: 9200
scheme: http

View File

@ -0,0 +1,50 @@
<?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.bwie</groupId>
<artifactId>bwie-modules</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>bwie-goods</artifactId>
<dependencies>
<!-- 系统公共 依赖 -->
<dependency>
<groupId>com.bwie</groupId>
<artifactId>bwie-common</artifactId>
</dependency>
<!-- SpringBoot Web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
<!-- Mysql Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Mybatis 依赖配置 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!-- Pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.1</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,19 @@
package com.bwie.goods;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/18
*/
@SpringBootApplication
public class GoodsApplication {
public static void main(String[] args) {
SpringApplication.run(GoodsApplication.class);
}
}

View File

@ -0,0 +1,64 @@
package com.bwie.goods.controller;
import com.alibaba.fastjson.JSONObject;
import com.bwie.common.domain.Goods;
import com.bwie.common.domain.dto.GoodsQueryDTO;
import com.bwie.common.domain.vo.GoodsVO;
import com.bwie.common.result.PageResult;
import com.bwie.common.result.Result;
import com.bwie.goods.service.GoodsService;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/18
*/
@RestController
@Log4j2
public class GoodsController {
@Autowired
private HttpServletRequest request;
@Autowired
private GoodsService goodsService;
/**
*
* @param goodsQueryDTO fastjson alibaba goggle Gson Jackson
* @return
*/
@PostMapping("list")
public Result<PageResult<GoodsVO>> list(@RequestBody GoodsQueryDTO goodsQueryDTO) {
log.info("功能名称查询商品列表请求URI{},请求方式:{},请求参数:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(goodsQueryDTO));
Result<PageResult<GoodsVO>> result = goodsService.list(goodsQueryDTO);
log.info("功能名称查询商品列表请求URI{},请求方式:{},响应结果:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(result));
return result;
}
/**
*
* @return
*/
@GetMapping("findAll")
public Result<List<GoodsVO>> findAll() {
return Result.success(goodsService.findAll());
}
@PostMapping
public Result add(@RequestBody Goods goods) {
goodsService.add(goods);
return Result.success();
}
}

View File

@ -0,0 +1,32 @@
package com.bwie.goods.mapper;
import com.bwie.common.domain.Goods;
import com.bwie.common.domain.dto.GoodsQueryDTO;
import com.bwie.common.domain.vo.GoodsVO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/18
*/
@Mapper
public interface GoodsMapper {
/**
*
* @param goodsQueryDTO
* @return
*/
List<GoodsVO> list(GoodsQueryDTO goodsQueryDTO);
List<GoodsVO> findAll();
void add(Goods goods);
GoodsVO detail(Long id);
}

View File

@ -0,0 +1,30 @@
package com.bwie.goods.service;
import com.bwie.common.domain.Goods;
import com.bwie.common.domain.dto.GoodsQueryDTO;
import com.bwie.common.domain.vo.GoodsVO;
import com.bwie.common.result.PageResult;
import com.bwie.common.result.Result;
import java.util.List;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/18
*/
public interface GoodsService {
/**
*
* @param goodsQueryDTO
* @return
*/
Result<PageResult<GoodsVO>> list(GoodsQueryDTO goodsQueryDTO);
List<GoodsVO> findAll();
void add(Goods goods);
GoodsVO detail(Long id);
}

View File

@ -0,0 +1,99 @@
package com.bwie.goods.service.impl;
import com.alibaba.fastjson.JSONObject;
import com.bwie.common.constants.MQQueueNameConstants;
import com.bwie.common.constants.RoleConstants;
import com.bwie.common.constants.TokenConstants;
import com.bwie.common.domain.Goods;
import com.bwie.common.domain.SysUser;
import com.bwie.common.domain.dto.GoodsQueryDTO;
import com.bwie.common.domain.vo.GoodsVO;
import com.bwie.common.result.PageResult;
import com.bwie.common.result.Result;
import com.bwie.common.utils.JwtUtils;
import com.bwie.goods.mapper.GoodsMapper;
import com.bwie.goods.service.GoodsService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.UUID;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/18
*/
@Service
public class GoodsServiceImpl implements GoodsService {
@Autowired
private GoodsMapper goodsMapper;
@Autowired
private HttpServletRequest request;
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private RabbitTemplate rabbitTemplate;
@Override
public Result<PageResult<GoodsVO>> list(GoodsQueryDTO goodsQueryDTO) {
PageHelper.startPage(goodsQueryDTO.getPageNum(), goodsQueryDTO.getPageSize());
// 普通用户展示自己管理的仓库信息,管理员登录所有仓库的信息
// 区分角色
SysUser sysUser = userInfo();
if (sysUser.getRole() == RoleConstants.ORDINARY_USERS) {
goodsQueryDTO.setCreateBy(sysUser.getId());
}
List<GoodsVO> goodsList = goodsMapper.list(goodsQueryDTO);
PageInfo<GoodsVO> pageInfo = new PageInfo<>(goodsList);
return PageResult.toResult(pageInfo.getTotal(), goodsList);
}
@Override
public List<GoodsVO> findAll() {
return goodsMapper.findAll();
}
@Override
public void add(Goods goods) {
goodsMapper.add(goods);
// 查询
GoodsVO goodsVO = this.detail(goods.getId());
// 异步 同步
rabbitTemplate.convertAndSend(MQQueueNameConstants.ADD_GOODS_ES_QUEUE_NAME, goodsVO, message -> {
message.getMessageProperties().setMessageId(UUID.randomUUID().toString());
return message;
});
}
@Override
public GoodsVO detail(Long id) {
return goodsMapper.detail(id);
}
/**
*
*/
private SysUser userInfo() {
// 获取 请求头中的 token
String token = request.getHeader(TokenConstants.TOKEN);
// 获取 userKey
String userKey = JwtUtils.getUserKey(token);
// 获取redis中的用户的信息 -》 key
String userJson = redisTemplate.opsForValue().get(TokenConstants.LOGIN_TOKEN_KEY + userKey);
// 反序列化
return JSONObject.parseObject(userJson, SysUser.class);
}
}

View File

@ -0,0 +1,29 @@
# Tomcat
server:
port: 9003
# Spring
spring:
main:
allow-circular-references: true # 允许循环依赖
jackson: # json 序列化 和 返序列化 转换
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
application:
# 应用名称
name: bwie-goods
profiles:
# 环境配置
active: dev
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: 123.249.113.136:8848
config:
# 配置中心地址
server-addr: 123.249.113.136:8848
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bwie.goods.mapper.GoodsMapper">
<insert id="add" useGeneratedKeys="true" keyProperty="id">
INSERT INTO tb_goods(goods_name, price, create_by, type_id) VALUES (#{goodsName}, #{price}, #{createBy}, #{typeId})
</insert>
<select id="list" resultType="com.bwie.common.domain.vo.GoodsVO">
select g.*, t.type_name, u.username from tb_goods g inner join tb_goods_type t on g.type_id = t.id
inner join tb_user u on g.create_by = u.id
<where>
<if test="goodsName!=null and goodsName!=''">
and g.goods_name like concat('%',#{goodsName},'%')
</if>
<if test="createBy!=null">
and g.create_by = #{createBy}
</if>
</where>
</select>
<select id="findAll" resultType="com.bwie.common.domain.vo.GoodsVO">
select g.*, t.type_name, u.username from tb_goods g inner join tb_goods_type t on g.type_id = t.id
inner join tb_user u on g.create_by = u.id
</select>
<select id="detail" resultType="com.bwie.common.domain.vo.GoodsVO">
select g.*, t.type_name, u.username from tb_goods g inner join tb_goods_type t on g.type_id = t.id
inner join tb_user u on g.create_by = u.id where g.id = #{id}
</select>
</mapper>

View File

@ -0,0 +1,30 @@
<?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.bwie</groupId>
<artifactId>bwie-modules</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>bwie-kafka</artifactId>
<dependencies>
<dependency>
<groupId>com.bwie</groupId>
<artifactId>bwie-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- kafka -->
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,19 @@
package com.bwie.kafka;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/11/2
*/
@SpringBootApplication
public class KafkaApplication {
public static void main(String[] args) {
SpringApplication.run(KafkaApplication.class);
}
}

View File

@ -0,0 +1,108 @@
package com.bwie.kafka.config;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.config.KafkaListenerContainerFactory;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
import org.springframework.kafka.listener.ConcurrentMessageListenerContainer;
import org.springframework.kafka.listener.ContainerProperties;
import org.springframework.kafka.support.serializer.JsonDeserializer;
import java.util.HashMap;
import java.util.Map;
/**
* @author
* @date 2022/10/31 18:05
* kafkaymlyml
*/
@SpringBootConfiguration
public class KafkaConsumerConfig {
@Value("${spring.kafka.consumer.bootstrap-servers}")
private String bootstrapServers;
@Value("${spring.kafka.consumer.group-id}")
private String groupId;
@Value("${spring.kafka.consumer.enable-auto-commit}")
private boolean enableAutoCommit;
@Value("${spring.kafka.properties.session.timeout.ms}")
private String sessionTimeout;
@Value("${spring.kafka.properties.max.poll.interval.ms}")
private String maxPollIntervalTime;
@Value("${spring.kafka.consumer.max-poll-records}")
private String maxPollRecords;
@Value("${spring.kafka.consumer.auto-offset-reset}")
private String autoOffsetReset;
@Value("${spring.kafka.listener.concurrency}")
private Integer concurrency;
@Value("${spring.kafka.listener.missing-topics-fatal}")
private boolean missingTopicsFatal;
@Value("${spring.kafka.listener.poll-timeout}")
private long pollTimeout;
@Bean
public Map<String, Object> consumerConfigs() {
Map<String, Object> propsMap = new HashMap<>(16);
propsMap.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
propsMap.put(ConsumerConfig.GROUP_ID_CONFIG, groupId);
//是否自动提交偏移量默认值是true为了避免出现重复数据和数据丢失可以把它设置为false然后手动提交偏移量
propsMap.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, enableAutoCommit);
//自动提交的时间间隔,自动提交开启时生效
propsMap.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "2000");
//该属性指定了消费者在读取一个没有偏移量的分区或者偏移量无效的情况下该作何处理:
//earliest当各分区下有已提交的offset时从提交的offset开始消费无提交的offset时从头开始消费分区的记录
//latest当各分区下有已提交的offset时从提交的offset开始消费无提交的offset时消费新产生的该分区下的数据在消费者启动之后生成的记录
//none当各分区都存在已提交的offset时从提交的offset开始消费只要有一个分区不存在已提交的offset则抛出异常
propsMap.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, autoOffsetReset);
//两次poll之间的最大间隔默认值为5分钟。如果超过这个间隔会触发reBalance
propsMap.put(ConsumerConfig.MAX_POLL_INTERVAL_MS_CONFIG, maxPollIntervalTime);
//这个参数定义了poll方法最多可以拉取多少条消息默认值为500。如果在拉取消息的时候新消息不足500条那有多少返回多少如果超过500条每次只返回500。
//这个默认值在有些场景下太大有些场景很难保证能够在5min内处理完500条消息
//如果消费者无法在5分钟内处理完500条消息的话就会触发reBalance,
//然后这批消息会被分配到另一个消费者中,还是会处理不完,这样这批消息就永远也处理不完。
//要避免出现上述问题提前评估好处理一条消息最长需要多少时间然后覆盖默认的max.poll.records参数
//注需要开启BatchListener批量监听才会生效如果不开启BatchListener则不会出现reBalance情况
propsMap.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, maxPollRecords);
//当broker多久没有收到consumer的心跳请求后就触发reBalance默认值是10s
propsMap.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, sessionTimeout);
//序列化建议使用Json这种序列化方式可以无需额外配置传输实体类
propsMap.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
propsMap.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, JsonDeserializer.class);
return propsMap;
}
@Bean
public ConsumerFactory<Object, Object> consumerFactory() {
// 配置消费者的 Json 反序列化的可信赖包,反序列化实体类需要
try (JsonDeserializer<Object> deserializer = new JsonDeserializer<>()) {
deserializer.trustedPackages("*");
return new DefaultKafkaConsumerFactory<>(consumerConfigs(), new JsonDeserializer<>(), deserializer);
}
}
@Bean
public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<Object, Object>> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<Object, Object> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(consumerFactory());
//在侦听器容器中运行的线程数,一般设置为 机器数*分区数
factory.setConcurrency(concurrency);
//消费监听接口监听的主题不存在时默认会报错所以设置为false忽略错误
factory.setMissingTopicsFatal(missingTopicsFatal);
// 自动提交关闭,需要设置手动消息确认
factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL_IMMEDIATE);
factory.getContainerProperties().setPollTimeout(pollTimeout);
// 设置为批量监听需要用List接收
// factory.setBatchListener(true);
return factory;
}
}

View File

@ -0,0 +1,84 @@
package com.bwie.kafka.config;
import org.apache.kafka.clients.producer.ProducerConfig;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.kafka.core.DefaultKafkaProducerFactory;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.core.ProducerFactory;
import org.springframework.kafka.transaction.KafkaTransactionManager;
import org.springframework.kafka.support.serializer.JsonSerializer;
import java.util.HashMap;
import java.util.Map;
/**
* KafkaTemplate
*/
@SpringBootConfiguration
public class KafkaProviderConfig {
@Value("${spring.kafka.producer.bootstrap-servers}")
private String bootstrapServers;
@Value("${spring.kafka.producer.transaction-id-prefix}")
private String transactionIdPrefix;
@Value("${spring.kafka.producer.acks}")
private String acks;
@Value("${spring.kafka.producer.retries}")
private String retries;
@Value("${spring.kafka.producer.batch-size}")
private String batchSize;
@Value("${spring.kafka.producer.buffer-memory}")
private String bufferMemory;
@Bean
public Map<String, Object> producerConfigs() {
Map<String, Object> props = new HashMap<>(16);
props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapServers);
//acks=0 生产者在成功写入消息之前不会等待任何来自服务器的响应。
//acks=1 只要集群的首领节点收到消息,生产者就会收到一个来自服务器成功响应。
//acks=all :只有当所有参与复制的节点全部收到消息时,生产者才会收到一个来自服务器的成功响应。
//开启事务必须设为all
props.put(ProducerConfig.ACKS_CONFIG, acks);
//发生错误后消息重发的次数开启事务必须大于0
props.put(ProducerConfig.RETRIES_CONFIG, retries);
//当多个消息发送到相同分区时,生产者会将消息打包到一起,以减少请求交互. 而不是一条条发送
//批次的大小可以通过batch.size 参数设置.默认是16KB
//较小的批次大小有可能降低吞吐量批次大小为0则完全禁用批处理
//比如说kafka里的消息5秒钟Batch才凑满了16KB才能发送出去。那这些消息的延迟就是5秒钟
//实测batchSize这个参数没有用
props.put(ProducerConfig.BATCH_SIZE_CONFIG, batchSize);
//有的时刻消息比较少,过了很久,比如5min也没有凑够16KB,这样延时就很大,所以需要一个参数. 再设置一个时间,到了这个时间,
//即使数据没达到16KB,也将这个批次发送出去
props.put(ProducerConfig.LINGER_MS_CONFIG, "5000");
//生产者内存缓冲区的大小
props.put(ProducerConfig.BUFFER_MEMORY_CONFIG, bufferMemory);
//反序列化,和生产者的序列化方式对应
props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
return props;
}
@Bean
public ProducerFactory<Object, Object> producerFactory() {
DefaultKafkaProducerFactory<Object, Object> factory = new DefaultKafkaProducerFactory<>(producerConfigs());
//开启事务,会导致 LINGER_MS_CONFIG 配置失效
factory.setTransactionIdPrefix(transactionIdPrefix);
return factory;
}
@Bean
public KafkaTransactionManager<Object, Object> kafkaTransactionManager(ProducerFactory<Object, Object> producerFactory) {
return new KafkaTransactionManager<>(producerFactory);
}
/**
* kafkaTemplate
* @return
*/
@Bean
public KafkaTemplate<Object, Object> kafkaTemplate() {
return new KafkaTemplate<>(producerFactory());
}
}

View File

@ -0,0 +1,38 @@
package com.bwie.kafka.config;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.clients.producer.RecordMetadata;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.ProducerListener;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* @author
* @date 2022/10/31 15:41
* kafka
*/
@Component
public class KafkaSendResultHandler implements ProducerListener<Object, Object> {
@Autowired
private KafkaTemplate<Object, Object> kafkaTemplate;
@PostConstruct
public void init() {
this.kafkaTemplate.setProducerListener(this);
}
@Override
public void onSuccess(ProducerRecord producerRecord, RecordMetadata recordMetadata) {
System.out.println("消息发送成功:" + producerRecord.toString());
}
@Override
public void onError(ProducerRecord producerRecord, @Nullable RecordMetadata recordMetadata, Exception exception) {
System.out.println("消息发送失败:" + producerRecord.toString() + exception.getMessage());
}
}

View File

@ -0,0 +1,34 @@
package com.bwie.kafka.config;
import org.apache.kafka.clients.consumer.Consumer;
import org.springframework.kafka.listener.KafkaListenerErrorHandler;
import org.springframework.kafka.listener.ListenerExecutionFailedException;
import org.springframework.lang.NonNull;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;
/**
* @author
* @date 2022/10/31 15:27
*
*/
@Component
public class MyKafkaListenerErrorHandler implements KafkaListenerErrorHandler {
@Override
@NonNull
public Object handleError(@NonNull Message<?> message, @NonNull ListenerExecutionFailedException exception) {
return new Object();
}
@Override
@NonNull
public Object handleError(@NonNull Message<?> message, @NonNull ListenerExecutionFailedException exception,
Consumer<?, ?> consumer) {
System.out.println("消息详情:" + message);
System.out.println("异常信息::" + exception);
System.out.println("消费者详情::" + consumer.groupMetadata());
System.out.println("监听主题::" + consumer.listTopics());
return KafkaListenerErrorHandler.super.handleError(message, exception, consumer);
}
}

View File

@ -0,0 +1,40 @@
package com.bwie.kafka.consumer;
import lombok.extern.log4j.Log4j2;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.kafka.support.Acknowledgment;
import org.springframework.stereotype.Component;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/11/2
*/
@Component
@Log4j2
public class HelloWorldKafkaConsumer {
// @KafkaListener(topics = { "hw-topic" })
// public void helloWorldKafkaConsumer(ConsumerRecord<String, String> record) {
// String key = record.key();
// String value = record.value();
// log.info("消息的消费者接收到消息消息的key{}value{}", key, value);
// }
@KafkaListener(topics = {"topic1", "topic2"},
containerFactory = "kafkaListenerContainerFactory",
errorHandler = "myKafkaListenerErrorHandler")
public void helloWorldKafkaConsumer(ConsumerRecord<String, String> record,
Acknowledgment acknowledgment) {
String key = record.key();
String value = record.value();
log.info("消息的消费者接收到消息消息的key{}value{}", key, value);
// 手动确认
acknowledgment.acknowledge();
}
}

View File

@ -0,0 +1,46 @@
package com.bwie.kafka.consumer;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;
import org.apache.kafka.common.serialization.StringDeserializer;
import java.time.Duration;
import java.util.Collections;
import java.util.Properties;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/11/2
*/
public class KafkaConsumerQuickStart {
public static void main(String[] args) {
// 创建 properties 对象 配置 kafka消费者的配置信息
Properties properties = new Properties();
properties.setProperty(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "123.249.113.136:9092");
// 设置 键值的反序列化方式
properties.setProperty(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
properties.setProperty(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class.getName());
// 配置 分组 ***
properties.setProperty(ConsumerConfig.GROUP_ID_CONFIG, "bw2");
// 创建 kafka 消息消费者对象
KafkaConsumer<String, String> kafkaConsumer = new KafkaConsumer<String, String>(properties);
// 订阅主题
kafkaConsumer.subscribe(Collections.singleton("bwie-topic"));
while (true) {
// 拉取消息
ConsumerRecords<String, String> records = kafkaConsumer.poll(Duration.ofMillis(2000));
// 遍历
records.forEach(record -> {
String key = record.key();
String value = record.value();
System.out.println("消息者消息成功消息的key" + key + "value" + value);
});
}
}
}

View File

@ -0,0 +1,63 @@
package com.bwie.kafka.controller;
import org.apache.kafka.clients.producer.ProducerRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.kafka.support.KafkaHeaders;
import org.springframework.kafka.support.SendResult;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.Map;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/11/2
*/
@RestController
@RequestMapping("kafka")
public class HelloWorldKafkaController {
// @Autowired
// private KafkaTemplate<String, String> kafkaTemplate;
//
// @GetMapping("send/message")
// public String sendMessage() {
// // 发送消息
// kafkaTemplate.send("hw-topic", "hello", "kafka");
//// listenableFuture.addCallback(result -> {
//// }, ex -> {
//// });
// return "ok";
// }
@Autowired
private KafkaTemplate<Object, Object> kafkaTemplate;
@GetMapping("send/message")
@Transactional
public String sendMessage() {
// ProducerRecord<Object, Object> producerRecord = new ProducerRecord<Object, Object>("topic1", "hello", "kafka");
kafkaTemplate.send("topic1", "hello", "kafka");
//使用 Message发送消息
Map<String, Object> map = new HashMap<>();
map.put(KafkaHeaders.TOPIC, "topic1");
map.put(KafkaHeaders.PARTITION_ID, 0);
map.put(KafkaHeaders.MESSAGE_KEY, "0");
GenericMessage<Object> message = new GenericMessage<>("use Message to send message", new MessageHeaders(map));
kafkaTemplate.send(message);
return "ok";
}
}

View File

@ -0,0 +1,69 @@
package com.bwie.kafka.producer;
import org.apache.kafka.clients.producer.*;
import org.apache.kafka.common.serialization.StringSerializer;
import java.util.Properties;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/11/2
*/
public class KafkaProducerQuickStart {
/**
*
*
* 1 : 1
*
* 1 : N
*
*
* @param args
*/
public static void main(String[] args) {
// 发送消息 Kafka
// 用来配置 kafka消息生产者对象的配置信息
Properties properties = new Properties();
// 配置 host
properties.setProperty(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "123.249.113.136:9092");
// 配置 键值的序列化方式
properties.setProperty(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
properties.setProperty(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
// 配置消息的确认
properties.setProperty(ProducerConfig.ACKS_CONFIG, "all");
// 设置重试次数
properties.setProperty(ProducerConfig.RETRIES_CONFIG, "3");
// 消息压缩
properties.setProperty(ProducerConfig.COMPRESSION_TYPE_CONFIG, "gzip");
// 创建消息生产者对象
KafkaProducer<String, String> kafkaProducer = new KafkaProducer<String, String>(properties);
// 发送消息
// 创建 消息记录
ProducerRecord<String, String> producerRecord = new ProducerRecord<String, String>("bwie-topic", "key", "hello world");
// kafkaProducer.send(producerRecord);
// 异步发送
kafkaProducer.send(producerRecord, (metadata, exception) -> {
if (exception == null) {
// 获取消息发送的分区
int partition = metadata.partition();
// 消息的偏移量
long offset = metadata.offset();
// 主题
String topic = metadata.topic();
System.out.println("发送成功,消息的分区:" + partition + ",消息的偏移量:" + offset + ",主题:" + topic);
} else {
System.out.println("发送失败,异常信息:" + exception);
}
});
// 关闭 kafkaProducer
kafkaProducer.close();
}
}

View File

@ -0,0 +1,102 @@
# Tomcat
server:
port: 9009
# Spring
spring:
kafka:
producer:
# Kafka服务器
bootstrap-servers: 123.249.113.136:9092
# 开启事务,必须在开启了事务的方法中发送,否则报错
transaction-id-prefix: kafkaTx-
# 发生错误后消息重发的次数开启事务必须设置大于0。
retries: 3
# acks=0 生产者在成功写入消息之前不会等待任何来自服务器的响应。
# acks=1 只要集群的首领节点收到消息,生产者就会收到一个来自服务器成功响应。
# acks=all :只有当所有参与复制的节点全部收到消息时,生产者才会收到一个来自服务器的成功响应。
# 开启事务时必须设置为all
acks: all
# 当有多个消息需要被发送到同一个分区时,生产者会把它们放在同一个批次里。该参数指定了一个批次可以使用的内存大小,按照字节数计算。
batch-size: 16384
# 生产者内存缓冲区的大小。
buffer-memory: 1024000
# 键的序列化方式
key-serializer: org.springframework.kafka.support.serializer.JsonSerializer
# 值的序列化方式建议使用Json这种序列化方式可以无需额外配置传输实体类
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
consumer:
# Kafka服务器
bootstrap-servers: 123.249.113.136:9092
group-id: firstGroup
# 自动提交的时间间隔 在spring boot 2.X 版本中这里采用的是值的类型为Duration 需要符合特定的格式如1S,1M,2H,5D
#auto-commit-interval: 2s
# 该属性指定了消费者在读取一个没有偏移量的分区或者偏移量无效的情况下该作何处理:
# earliest当各分区下有已提交的offset时从提交的offset开始消费无提交的offset时从头开始消费分区的记录
# latest当各分区下有已提交的offset时从提交的offset开始消费无提交的offset时消费新产生的该分区下的数据在消费者启动之后生成的记录
# none当各分区都存在已提交的offset时从提交的offset开始消费只要有一个分区不存在已提交的offset则抛出异常
auto-offset-reset: latest
# 是否自动提交偏移量默认值是true为了避免出现重复数据和数据丢失可以把它设置为false然后手动提交偏移量
enable-auto-commit: false
# 键的反序列化方式
#key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
key-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
# 值的反序列化方式建议使用Json这种序列化方式可以无需额外配置传输实体类
value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
# 配置消费者的 Json 反序列化的可信赖包,反序列化实体类需要
properties:
spring:
json:
trusted:
packages: "*"
# 这个参数定义了poll方法最多可以拉取多少条消息默认值为500。如果在拉取消息的时候新消息不足500条那有多少返回多少如果超过500条每次只返回500。
# 这个默认值在有些场景下太大有些场景很难保证能够在5min内处理完500条消息
# 如果消费者无法在5分钟内处理完500条消息的话就会触发reBalance,
# 然后这批消息会被分配到另一个消费者中,还是会处理不完,这样这批消息就永远也处理不完。
# 要避免出现上述问题提前评估好处理一条消息最长需要多少时间然后覆盖默认的max.poll.records参数
# 注需要开启BatchListener批量监听才会生效如果不开启BatchListener则不会出现reBalance情况
max-poll-records: 3
properties:
# 两次poll之间的最大间隔默认值为5分钟。如果超过这个间隔会触发reBalance
max:
poll:
interval:
ms: 600000
# 当broker多久没有收到consumer的心跳请求后就触发reBalance默认值是10s
session:
timeout:
ms: 10000
listener:
# 在侦听器容器中运行的线程数,一般设置为 机器数*分区数
concurrency: 4
# 自动提交关闭,需要设置手动消息确认
ack-mode: manual_immediate
# 消费监听接口监听的主题不存在时默认会报错所以设置为false忽略错误
missing-topics-fatal: false
# 两次poll之间的最大间隔默认值为5分钟。如果超过这个间隔会触发reBalance
poll-timeout: 600000
main:
allow-circular-references: true # 允许循环依赖
jackson: # json 序列化 和 返序列化 转换
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
application:
# 应用名称
name: bwie-kafka
profiles:
# 环境配置
active: dev
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: 123.249.113.136:8848
config:
# 配置中心地址
server-addr: 123.249.113.136:8848
namespace: a9b66e92-e507-47ba-9674-6f939f793aca
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}

View File

@ -0,0 +1,35 @@
<?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.bwie</groupId>
<artifactId>bwie-modules</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>bwie-rabbitmq</artifactId>
<dependencies>
<dependency>
<groupId>com.bwie</groupId>
<artifactId>bwie-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- rabbitMQ 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!-- jackson -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,32 @@
package com.bwie.mq;
import com.rabbitmq.client.AMQP;
import org.springframework.amqp.core.Queue;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/20
*/
@SpringBootApplication
public class RabbitMQApplication {
public static void main(String[] args) {
SpringApplication.run(RabbitMQApplication.class);
}
// /**
// * 初始化队列
// * @return
// */
// @Bean
// public Queue initQueue() {
// return new Queue("hello_world_queue", true);
// }
}

View File

@ -0,0 +1,48 @@
package com.bwie.mq.config;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* @ClassName:
* @Description: broker confirm
* @Author: zhuwenqiang
* @Date: 2023/10/23
*/
@Component
public class ConfirmCallbackConfig implements RabbitTemplate.ConfirmCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
*
*
*/
@PostConstruct
public void init() {
// 设置 rabbitTemplate 的消息发送到交换机确认
rabbitTemplate.setConfirmCallback(this);
}
/**
*
*
* @param correlationData correlation data for the callback.
* @param ack true for ack, false for nack true false
* @param cause An optional cause, for nack, when available, otherwise null.
*/
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack) {
System.out.println("消息发送到broker成功");
} else {
System.out.println("消息发送到broker失败失败的原因是" + cause);
}
}
}

View File

@ -0,0 +1,15 @@
package com.bwie.mq.config;
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 {
// 消息转换配置 SimpleMessageConverter String byte[] serializable
@Bean
public MessageConverter jsonMessageConverter() {
return new Jackson2JsonMessageConverter();
}
}

View File

@ -0,0 +1,40 @@
package com.bwie.mq.config;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/23
*/
@Component
public class ReturnsCallbackConfig implements RabbitTemplate.ReturnsCallback {
@Autowired
private RabbitTemplate rabbitTemplate;
@PostConstruct
public void init() {
rabbitTemplate.setReturnsCallback(this);
}
/**
*
*
* @param returnedMessage the returned message and metadata.
*/
@Override
public void returnedMessage(ReturnedMessage returnedMessage) {
System.out.println("消息" + returnedMessage.getMessage().toString() +
"被交换机" + returnedMessage.getExchange() + "回退!" +
"退回原因为:" + returnedMessage.getReplyText());
// TODO 补偿 可以再发 做日志记录
}
}

View File

@ -0,0 +1,27 @@
package com.bwie.mq.consumer;
import lombok.extern.log4j.Log4j2;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/20
*/
@Component
@Log4j2
public class HelloWorldConsumer {
/**
*
*/
// @RabbitListener(queues = { "hello_world_queue" })
@RabbitListener(queuesToDeclare = {@Queue("hello_world_queue")})
public void helloWorldConsumer(String message) {
log.info("hello_world_queue队列消费者接收到了消息消息内容是{},消费完毕!", message);
}
}

View File

@ -0,0 +1,69 @@
package com.bwie.mq.consumer;
import com.alibaba.fastjson.JSONObject;
import com.bwie.common.domain.SysUser;
import com.rabbitmq.client.Channel;
import lombok.extern.log4j.Log4j2;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.io.IOException;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/21
*/
@Component
@Log4j2
public class MessageConverterConsumer {
@Autowired
private RedisTemplate<String, String> redisTemplate;
/**
*
* @param message
* @param sysUser
* @param channel
*/
@RabbitListener(queuesToDeclare = {@Queue(name = "test_message_converter_queue")})
@Transactional // 开启事务管理,确保消息处理成功或回滚。
public void messageConverterConsumer(Message message, SysUser sysUser, Channel channel) {
// 获取消息的 messageId
String messageId = message.getMessageProperties().getMessageId();
try {
// 如果添加成功返回 1 失败 0
Long count = redisTemplate.opsForSet().add("test_message_converter_queue", messageId);
if (count == 1) {
// 正常消费 执行业务逻辑
log.info("test_message_converter_queue队列消费者1接收到了消息消息内容是{},消费完毕!", sysUser);
// 执行业务逻辑的时候 出现了异常
// 手动确认
// 第一个参数 : 消息投递序号
// 第二个参数 : 是否批量确认 true false 如果值是true 表示 将 当前所有消息包括 当前序号的消息全部确认 false 只确认这一条消息
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}
} catch (IOException e) {
e.printStackTrace();
redisTemplate.opsForSet().remove("test_message_converter_queue", messageId);
// 回退消息
// 第一个参数 : 消息投递序号
// 第二个参数 : 是否批量确认 true false 如果值是true 表示 将 当前所有消息包括 当前序号的消息全部确认 false 只确认这一条消息
// 第三个参数 是否回退到原来的队列 true false 消息丢弃
// channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);
try {
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
} catch (IOException ex) {
e.printStackTrace();
}
}
}
}

View File

@ -0,0 +1,37 @@
package com.bwie.mq.consumer;
import lombok.extern.log4j.Log4j2;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/20
*/
@Component
@Log4j2
public class PublishSubscribeConsumer {
/**
* 1
* @param message
*/
@RabbitListener(bindings = {@QueueBinding(value = @Queue("pb_sb_queue"),
exchange = @Exchange(value = "publish_subscribe_exchange", type = ExchangeTypes.FANOUT))})
public void publishSubscribeConsumer(String message) {
log.info("pb_sb_queue队列消费者1接收到了消息消息内容是{},消费完毕!", message);
}
@RabbitListener(bindings = {@QueueBinding(value = @Queue("pb_sb_queue2"),
exchange = @Exchange(value = "publish_subscribe_exchange", type = ExchangeTypes.FANOUT))})
public void publishSubscribeConsumer2(String message) {
log.info("pb_sb_queue队列消费者2接收到了消息消息内容是{},消费完毕!", message);
}
}

View File

@ -0,0 +1,42 @@
package com.bwie.mq.consumer;
import lombok.extern.log4j.Log4j2;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/20
*/
@Component
@Log4j2
public class RoutingConsumer {
@RabbitListener(bindings = {@QueueBinding(value = @Queue(name = "routing_queue"),
exchange = @Exchange(name = "routing_exchange", type = ExchangeTypes.DIRECT),
key = { "aa", "bb", "error" })})
public void routingConsumer(String message) {
log.info("routing_queue队列消费者1接收到了消息消息内容是{},消费完毕!", message);
}
@RabbitListener(bindings = {@QueueBinding(value = @Queue(name = "routing_queue2"),
exchange = @Exchange(name = "routing_exchange", type = ExchangeTypes.DIRECT),
key = { "bb", "error" })})
public void routingConsumer2(String message) {
log.info("routing_queue队列消费者2接收到了消息消息内容是{},消费完毕!", message);
}
@RabbitListener(bindings = {@QueueBinding(value = @Queue(name = "routing_queue3"),
exchange = @Exchange(name = "routing_exchange", type = ExchangeTypes.DIRECT),
key = { "aac", "cc", "error" })})
public void routingConsumer3(String message) {
log.info("routing_queue队列消费者3接收到了消息消息内容是{},消费完毕!", message);
}
}

View File

@ -0,0 +1,34 @@
package com.bwie.mq.consumer;
import lombok.extern.log4j.Log4j2;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/21
*/
@Component
@Log4j2
public class TopicConsumer {
@RabbitListener(bindings = { @QueueBinding(value = @Queue(name = "topic_queue"),
exchange = @Exchange(name = "topic_exchange", type = ExchangeTypes.TOPIC),
key = { "*.name.*", "age.#" })})
public void topicConsumer(String message) {
log.info("topic_queue队列消费者1接收到了消息消息内容是{},消费完毕!", message);
}
@RabbitListener(bindings = { @QueueBinding(value = @Queue(name = "topic_queue2"),
exchange = @Exchange(name = "topic_exchange", type = ExchangeTypes.TOPIC),
key = { "user.name", "user.name.age" })})
public void topicConsumer2(String message) {
log.info("topic_queue2队列消费者2接收到了消息消息内容是{},消费完毕!", message);
}
}

View File

@ -0,0 +1,31 @@
package com.bwie.mq.consumer;
import lombok.extern.java.Log;
import lombok.extern.log4j.Log4j2;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/20
*/
@Component
@Log4j2
public class WorkQueuesConsumer {
@RabbitListener(queuesToDeclare = {@Queue("work_queues_queue")})
public void workQueuesConsumer(String message) throws InterruptedException {
Thread.sleep(200);
log.info("work_queues_queue队列消费者1接收到了消息消息内容是{},消费完毕!", message);
}
@RabbitListener(queuesToDeclare = {@Queue("work_queues_queue")})
public void workQueuesConsumer2(String message) throws InterruptedException {
Thread.sleep(400);
log.info("work_queues_queue队列消费2者接收到了消息消息内容是{},消费完毕!", message);
}
}

View File

@ -0,0 +1,111 @@
package com.bwie.mq.producer;
import com.alibaba.fastjson.JSONObject;
import com.bwie.common.domain.SysUser;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.UUID;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/20
*/
@RestController
@RequestMapping("mq")
public class RabbitMQController {
@Autowired
private RabbitTemplate rabbitTemplate;
/**
* hello world
*/
@GetMapping("hello/world")
public String helloWorld() {
// routingKey: 队列名称
// object : 消息的内容
rabbitTemplate.convertAndSend("hello_world_queue", "hello world rabbit!");
return "ok";
}
/**
* work queues
*/
@GetMapping("work/queues")
public String workQueues() {
// routingKey: 队列名称
// object : 消息的内容
for (int i = 1; i <= 10; i++) {
rabbitTemplate.convertAndSend("work_queues_queue", "hello world work!"+i);
}
return "ok";
}
/**
* publish/subscribe
* fanout
*/
@GetMapping("publish/subscribe")
public String publishSubScribe() {
// 第一个参数: exchange: 交换机的名称
// 第二个参数: 交换机与队列的绑定的规则 如果是发布订阅模式则 routingKey写成空字符串只要绑定就行不要写规则
// 第三个参数: 消息的内容
rabbitTemplate.convertAndSend("publish_subscribe_exchange", "", "publish subscribe!");
return "ok";
}
/**
* routing
* direct
*/
@GetMapping("routing")
public String routing() {
// 第一个参数: exchange: 交换机的名称
// 第二个参数: 绑定规则 名字随意 字符串
// 第三个参数: 消息的内容
rabbitTemplate.convertAndSend("routing_exchange", "aa", "publish subscribe!");
return "ok";
}
/**
* topic
* topic
*/
@GetMapping("topic")
public String topic() {
// 第一个参数: exchange: 交换机的名称
// 第二个参数: 绑定规则 名字随意 字符串 多个单词组成 以.
// 第三个参数: 消息的内容
rabbitTemplate.convertAndSend("topic_exchange", "age.name.sex", "topic !");
return "ok";
}
/**
* hello world
*/
@GetMapping("hello/world2")
public String helloWorld2() {
// routingKey: 队列名称
// object : 消息的内容
SysUser sysUser = new SysUser();
sysUser.setName("张三");
sysUser.setPassword("123");
rabbitTemplate.convertAndSend("test_message_converter_queue", sysUser, message -> {
// 设置消息的 messageId
message.getMessageProperties().setMessageId(UUID.randomUUID().toString());
return message;
});
return "ok";
}
}

View File

@ -0,0 +1,45 @@
# Tomcat
server:
port: 9004
# Spring
spring:
rabbitmq:
host: 123.249.113.136
port: 5672
username: guest
password: guest
virtual-host: /
listener:
simple:
prefetch: 1 # 每次取出来一条消息,消费完毕之后取下一条消费
acknowledge-mode: manual # 手动确认消费
retry: # 重试
enabled: true
# 发送确认配置 开启发送确认
publisher-confirm-type: correlated # 消息发送到broker确认
publisher-returns: true # 消息发送到队列确认
main:
allow-circular-references: true # 允许循环依赖
jackson: # json 序列化 和 返序列化 转换
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
application:
# 应用名称
name: bwie-rabbitmq
profiles:
# 环境配置
active: dev
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: 123.249.113.136:8848
config:
# 配置中心地址
server-addr: 123.249.113.136:8848
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}

View File

@ -0,0 +1,79 @@
1、概念
a、MQ的使用场景
(1)、异步
(2)、解耦
(3)、流量削峰
b、市面上常见的MQ各自的特点、并且如何选择
(1)、ActiveMQ
比较老牌、由apache开发的吞吐量万级时效性 ms, 停止维护 慢慢淘汰
(2)、RabbitMQ
erlang 语言开发的 并发较好 吞吐量万级 时效性 us 可用性高 主从 支持语言较多 社区活跃度高
(3)、RocketMQ
Java 语言开发 吞吐量 10W级别 时效性 ms 可用性非常高 集群 消息可靠性高 可以做到消息的0丢失
互联网金融相关的业务
(4)、Kafka
Scala 和 Java 语言开发 吞吐量 100W级别 时效性 ms 可用性非常高 集群
互联网公司产生大量业务数量收集的场景 日志收集分析
c、RabbitMQ 交换机 队列 virtualhost channel
(1)、交换机 消息先发送给交换机交换机在将消息发送给与其绑定的、并且满足routingKey规则 没有存储消息的能力
一条消息 可以被多个消费者同时消费 ,如果没有交换机【只有一个队列】,则消息只能被一个消费者消费
a、发布订阅模式 fanout
b、路由模型 direct
c、主题模型 topic rabbitMQ 自带的
(2)、 消息的缓冲区 可以存储消息的
(3)、 virtualhost 类似于网络上的 namespace 命名空间 隔离作用
每一个用户都可以创建自己的交换机|队列 通过不同的 virtualhost 来做隔离相互不影响
张三: 创建名字为 a 的队列 , 李四 也可以创建名字为 a 的队列
(4)、 channel 轻量级的connection对象 较少tcp connection 的连接
2、springboot工程中 需要 集成 RabbitMQ [amqp] 高级消息队列协议
a 引入依赖
<de>
spring-boot-starter-amqp
b 配置
默认值 默认连接的本机
spring:
rabbitmq:
host:
port: 5672
username: guest
password: guest
virtual-host: /
c 注入 RabbitTemplate
d 发送消息
rabbitTemplate.convertAndSend()
e 监听队列 消息消息
@RabbitListener(queues = {"队列名称",".."})
自己构建队列 配置类 new Queue("队列名称", true)
@RabbitListener(queuesToDeclare = { @Queue(name = "队列名称") })
队列模式:
1 hello world
一个消息生产者 一个队列 一个消费者
2 work queues
一个消息生产者 一个队列 多个消费者 默认公平消费 配置多劳多得
rabbitTemplate.convertAndSend("队列名称", "消息内容")
spring:
rabbitmq:
host:
port: 5672
username: guest
password: guest
virtual-host: /
listener:
simple:
prefetch: 1
3 发布订阅 publish|subscribe
一个消息生产者 交换机 多个队列 多个消费者
routingKey 不需要写规则 只要绑定就行
rabbitTemplate.convertAndSend("队列名称", "", "消息内容")
@RabbitListener(bindings={ @QueueBindings(value=@Queue(name=""), exchange=@Exchange(name="", type="类型")) })

View File

@ -0,0 +1,25 @@
<?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.bwie</groupId>
<artifactId>bwie-modules</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>bwie-sms</artifactId>
<dependencies>
<dependency>
<groupId>com.bwie</groupId>
<artifactId>bwie-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,19 @@
package com.bwie.sms;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/25
*/
@SpringBootApplication
public class SmsApplication {
public static void main(String[] args) {
SpringApplication.run(SmsApplication.class);
}
}

View File

@ -0,0 +1,77 @@
package com.bwie.sms.consumer;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson.JSONObject;
import com.aliyun.dysmsapi20170525.models.SendSmsResponseBody;
import com.bwie.common.domain.dto.SendSmsDTO;
import com.bwie.common.utils.TelSmsUtils;
import com.rabbitmq.client.Channel;
import lombok.extern.log4j.Log4j2;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/24
*/
@Component
@Log4j2
public class SendSmsQueueConsumer {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@RabbitListener(queuesToDeclare = {@Queue("send_sms_queue")})
public void sendSmsQueueConsumer(Message message, SendSmsDTO sendSmsDTO, Channel channel) {
log.info("短信队列:{},接收到消息:{},开始消费...", "send_sms_queue", JSONObject.toJSONString(sendSmsDTO));
long s = System.currentTimeMillis();
// 获取消息 id
String messageId = message.getMessageProperties().getMessageId();
try {
// 添加消息id到 redis的set 集合中
Long count = redisTemplate.opsForSet().add("send_sms_queue", messageId);
if (count == 1) {
String jsonStr = TelSmsUtils.sendSms(sendSmsDTO.getMobile(), new HashMap<String, String>() {{
put(sendSmsDTO.getKey(), sendSmsDTO.getValue());
}});
// 反序列化
SendSmsResponseBody sendSmsResponseBody = JSONObject.parseObject(jsonStr, SendSmsResponseBody.class);
if (!"OK".equals(sendSmsResponseBody.getCode())) {
TelSmsUtils.sendSms(sendSmsDTO.getMobile(), new HashMap<String, String>() {{
put(sendSmsDTO.getKey(), sendSmsDTO.getValue());
}});
}
// 手动确认
channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
log.info("短信队列:{},接收到消息:{},消费完成,耗时:{}毫秒!", "send_sms_queue", JSONObject.toJSONString(sendSmsDTO), (System.currentTimeMillis() - s));
}
} catch (Exception e) {
log.error("短信队列:{},接收到消息:{},消费消息异常,异常信息:{}", "send_sms_queue", JSONObject.toJSONString(sendSmsDTO), e.getMessage());
// 移除 添加到 redis set 集合中的 messageId
redisTemplate.opsForSet().remove("send_sms_queue", messageId);
// 消息回退到原来的队列
try {
channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
} catch (IOException ex) {
log.error("短信队列:{},接收到消息:{},消费回退异常,异常信息:{}", "send_sms_queue", JSONObject.toJSONString(sendSmsDTO), e.getMessage());
}
}
}
public static void main(String[] args) {
System.out.println("\"aaaa\"");
}
}

View File

@ -0,0 +1,30 @@
# Tomcat
server:
port: 9006
# Spring
spring:
main:
allow-circular-references: true # 允许循环依赖
jackson: # json 序列化 和 返序列化 转换
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
application:
# 应用名称
name: bwie-sms
profiles:
# 环境配置
active: dev
cloud:
nacos:
discovery:
# 服务注册地址
server-addr: 123.249.113.136:8848
config:
# 配置中心地址
server-addr: 123.249.113.136:8848
namespace: a9b66e92-e507-47ba-9674-6f939f793aca
# 配置文件格式
file-extension: yml
# 共享配置
shared-configs:
- application-${spring.profiles.active}.${spring.cloud.nacos.config.file-extension}

View File

@ -0,0 +1,50 @@
<?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.bwie</groupId>
<artifactId>bwie-modules</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>bwie-system</artifactId>
<dependencies>
<!-- 系统公共 依赖 -->
<dependency>
<groupId>com.bwie</groupId>
<artifactId>bwie-common</artifactId>
</dependency>
<!-- SpringBoot Web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.2.8</version>
</dependency>
<!-- Mysql Connector -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- Mybatis 依赖配置 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<!-- Pagehelper -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.4.1</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,19 @@
package com.bwie.system;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/17
*/
@SpringBootApplication
public class SystemApplication {
public static void main(String[] args) {
SpringApplication.run(SystemApplication.class);
}
}

View File

@ -0,0 +1,16 @@
package com.bwie.system.controller;
import org.springframework.web.bind.annotation.RestController;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/25
*/
@RestController
public class LoginLogController {
}

View File

@ -0,0 +1,48 @@
package com.bwie.system.controller;
import com.alibaba.fastjson.JSONObject;
import com.bwie.common.domain.SysUser;
import com.bwie.common.result.Result;
import com.bwie.system.service.SysUserService;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/16
*/
@RestController
@RequestMapping("sys/user")
@Log4j2
public class SysUserController {
@Autowired
private HttpServletRequest request;
@Autowired
private SysUserService sysUserService;
/**
*
*/
@GetMapping("findByTel/{tel}")
public Result<SysUser> findByTel(@PathVariable String tel) {
log.info("功能名称根据手机号查询系统用户请求URI{},请求方式:{},请求参数:{}", request.getRequestURI(),
request.getMethod(), tel);
SysUser sysUser = sysUserService.findByTel(tel);
Result<SysUser> result = Result.success(sysUser);
log.info("功能名称根据手机号查询系统用户请求URI{},请求方式:{},响应结果:{}", request.getRequestURI(),
request.getMethod(), JSONObject.toJSONString(result));
return result;
}
}

View File

@ -0,0 +1,21 @@
package com.bwie.system.mapper;
import com.bwie.common.domain.SysUser;
import org.apache.ibatis.annotations.Mapper;
/**
* @ClassName:
* @Description:
* @Author: zhuwenqiang
* @Date: 2023/10/16
*/
@Mapper
public interface SysUserMapper {
/**
*
* @param tel
* @return
*/
SysUser findByTel(String tel);
}

Some files were not shown because too many files have changed in this diff Show More