feat(新增): 购物车功能搭建

Signed-off-by: bunny <1319900154@qq.com>
This commit is contained in:
bunny 2024-03-28 14:07:57 +08:00
parent 70dd7c63f1
commit 77d4019dc7
27 changed files with 355 additions and 64 deletions

View File

@ -14,6 +14,7 @@
<module name="common-service" />
<module name="service-product" />
<module name="service-user" />
<module name="service-cart" />
<module name="spzx-manager" />
<module name="service-user-client" />
<module name="common-log" />
@ -26,6 +27,7 @@
<module name="common-log" options="-parameters" />
<module name="common-service" options="-parameters" />
<module name="common-util" options="-parameters" />
<module name="service-cart" options="-parameters" />
<module name="service-order" options="-parameters" />
<module name="service-product" options="-parameters" />
<module name="service-product-client" options="-parameters" />

View File

@ -14,6 +14,7 @@
<file url="file://$PROJECT_DIR$/spzx-service-client/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/spzx-service-client/src/main/resources" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/spzx-service-client/untitled/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/spzx-service/service-cart/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/spzx-service/service-order/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/spzx-service/service-product/src/main/java" charset="UTF-8" />
<file url="file://$PROJECT_DIR$/spzx-service/service-user/src/main/java" charset="UTF-8" />

View File

@ -28,7 +28,7 @@ public class LogAspect {
SysOperLog sysOperLog = new SysOperLog();
LogUtil.beforeHandleLog(sysLog, joinPoint, sysOperLog);
Object proceed = null;
try {
proceed = joinPoint.proceed();
// 执行业务方法

View File

@ -29,11 +29,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--支持配置属性类yml文件中可以提示配置项-->
<dependency>
<groupId>org.springframework.boot</groupId>
@ -59,5 +54,12 @@
<artifactId>jetty-util</artifactId>
<version>9.3.7.v20160115</version>
</dependency>
<!-- openfeign依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,17 @@
package com.atguigu.anno;
import com.atguigu.config.WebMvcConfiguration;
import com.atguigu.interceptor.UserLoginAuthInterceptor;
import org.springframework.context.annotation.Import;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.TYPE)
@Import(value = {UserLoginAuthInterceptor.class, WebMvcConfiguration.class})
public @interface EnableUserWebMvcConfiguration {
}

View File

@ -3,14 +3,18 @@ package com.atguigu.config;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info;
import lombok.extern.slf4j.Slf4j;
import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@Slf4j
public class Knife4jConfig {
@Bean
public OpenAPI customOpenAPI() {
log.info("尚品甑选API接口文档");
// 设定作者
return new OpenAPI().info(new Info().title("尚品甑选API接口文档")
.version("1.0").description("尚品甑选API接口文档").contact(new Contact().name("bunny")));
@ -18,16 +22,22 @@ public class Knife4jConfig {
@Bean
public GroupedOpenApi adminApi() {
log.info("admin请求接口");
return GroupedOpenApi.builder().group("admin请求接口").pathsToMatch("/admin/**").build();// 接口请求路径规则
}
@Bean
public GroupedOpenApi productApi() {
log.info("商品管理接口");
return GroupedOpenApi.builder().group("商品管理接口").pathsToMatch("/api/product/**").build(); // 接口请求路径规则
}
@Bean
public GroupedOpenApi userApi() {
log.info("用户接口");
return GroupedOpenApi.builder().group("用户接口").pathsToMatch("/api/user/**").build();
}
}

View File

@ -1,28 +1,30 @@
package com.atguigu.config;
import com.atguigu.interceptor.LoginAuthInterceptor;
import com.atguigu.interceptor.UserLoginAuthInterceptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Component
@Slf4j
public class WebMvcConfiguration extends WebMvcConfigurationSupport {
public class WebMvcConfiguration implements WebMvcConfigurer {
@Autowired
private LoginAuthInterceptor loginAuthInterceptor;
@Autowired
private UserLoginAuthInterceptor userLoginAuthInterceptor;
/**
* * 解决跨域
*
* @param registry 跨域注册表
*/
protected void addCorsMappings(CorsRegistry registry) {
@Override
public void addCorsMappings(CorsRegistry registry) {
log.info("WebMvcConfiguration===>开始跨域注册表...");
registry.addMapping("/admin/**")// 添加路径规则
registry.addMapping("/**")// 添加路径规则
.allowCredentials(true)// 是否允许在跨域的情况下传递Cookie
.allowedOriginPatterns("*")// 允许请求来源的域规则
.allowedMethods("*").allowedHeaders("*");// 允许所有的请求头
@ -33,11 +35,17 @@ public class WebMvcConfiguration extends WebMvcConfigurationSupport {
*
* @param registry InterceptorRegistry
*/
protected void addInterceptors(InterceptorRegistry registry) {
@Override
public void addInterceptors(InterceptorRegistry registry) {
log.info("WebMvcConfiguration===>开始注册自定义拦截器...");
String[] loginAuthInterceptors = {"/admin/system/index/login", "/admin/system/index/generateValidateCode", "/admin/product/category/exportData"};
// 需要拦截的
registry.addInterceptor(loginAuthInterceptor).addPathPatterns("/admin/**")
.excludePathPatterns("/admin/system/index/login", "/admin/system/index/generateValidateCode", "/admin/product/category/exportData");
.excludePathPatterns(loginAuthInterceptors);
String[] userLoginAuthInterceptors = {};
registry.addInterceptor(userLoginAuthInterceptor).addPathPatterns("/api/**")
.excludePathPatterns(userLoginAuthInterceptors);
}
}

View File

@ -65,8 +65,6 @@ public class LoginAuthInterceptor implements HandlerInterceptor {
*/
@Override
public void afterCompletion(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler, @Nullable Exception ex) throws Exception {
log.info("LoginAuthInterceptor===>请求完成后删除ThreadLocal");
BaseContext.removeSysUser();
}
}

View File

@ -0,0 +1,47 @@
package com.atguigu.interceptor;
import com.alibaba.fastjson.JSON;
import com.atguigu.constant.MessageConstant;
import com.atguigu.context.BaseContext;
import com.atguigu.spzx.model.entity.user.UserInfo;
import com.atguigu.utils.EmptyUtil;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
@Configuration
@Slf4j
public class UserLoginAuthInterceptor implements HandlerInterceptor {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private EmptyUtil emptyUtil;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
log.info("UserLoginAuthInterceptor===>preHandle拦截请求前");
// 判断token是否为空
String token = request.getHeader("token");
emptyUtil.isEmpty(token, MessageConstant.TOKEN_IS_EMPTY);
String userJSON = (String) redisTemplate.opsForValue().get(token);
BaseContext.setUserInfo(JSON.parseObject(userJSON, UserInfo.class));
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
BaseContext.removeUserInfo();
}
}

View File

@ -36,5 +36,10 @@
<artifactId>minio</artifactId>
<version>8.5.9</version>
</dependency>
<!-- redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -20,7 +20,7 @@
<dependencies>
<dependency>
<groupId>com.atguigu</groupId>
<artifactId>spzx-model</artifactId>
<artifactId>common-service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- gateway -->

View File

@ -3,8 +3,10 @@ package com.atguigu.gateway;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan("com.atguigu")
@Slf4j
public class GatewayApplication {
public static void main(String[] args) {

View File

@ -1,37 +0,0 @@
package com.atguigu.gateway.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
@Configuration
@Slf4j
public class CorsConfig {
/**
* * 解决跨域
*/
@Bean
protected CorsWebFilter addCorsMappings() {
log.info("WebMvcConfiguration===>开始跨域注册表...");
// 当allowCredentials设置为true时allowedOrigins属性不能使用通配符"*"而是需要显式列出允许的源或使用allowedOriginPatterns
CorsConfiguration config = new CorsConfiguration();
// 设置跨域请求地址
// config.addAllowedOrigin("*");
config.addAllowedMethod("*");
config.addAllowedHeader("*");
// 使用allowedOriginPatterns属性在Spring Framework 5.3及更高版本中
// 您可以使用allowedOriginPatterns属性它允许您使用正则表达式模式来匹配允许的源这样您可以更灵活地配置允许的源例如
// 使用"https?://example.com"来匹配http://example.com和https://example.com两个源
config.addAllowedOriginPattern("*");
config.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/**", config);
return new CorsWebFilter(source);
}
}

View File

@ -0,0 +1,75 @@
package com.atguigu.gateway.filter;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.atguigu.constant.MessageConstant;
import com.atguigu.spzx.model.entity.user.UserInfo;
import com.atguigu.spzx.model.vo.result.Result;
import com.atguigu.spzx.model.vo.result.ResultCodeEnum;
import com.atguigu.utils.EmptyUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
import java.util.List;
@Configuration
public class AuthGlobalFilter implements GlobalFilter, Ordered {
private final AntPathMatcher antPathMatcher = new AntPathMatcher();
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private EmptyUtil emptyUtil;
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest request = exchange.getRequest();
String path = request.getURI().getPath();
// 判断请求路径
if (antPathMatcher.match("/api/**/auth/**", path)) {
UserInfo userInfo = getUserinfo(request);
emptyUtil.isEmpty(userInfo, MessageConstant.USER_DOES_NOT_EXIST);
ServerHttpResponse response = exchange.getResponse();
return out(exchange.getResponse());
}
return chain.filter(exchange);
}
private UserInfo getUserinfo(ServerHttpRequest request) {
List<String> tokenList = request.getHeaders().get("token");
emptyUtil.isEmpty(tokenList, MessageConstant.TOKEN_IS_EMPTY);
String token = tokenList.get(0);
if (StringUtils.hasText(token)) {
String userJson = (String) redisTemplate.opsForValue().get(token);
emptyUtil.isEmpty(userJson, MessageConstant.TOKEN_IS_EMPTY);
return JSON.parseObject(userJson, UserInfo.class);
}
return null;
}
@Override
public int getOrder() {
return 0;
}
private Mono<Void> out(ServerHttpResponse response) {
Result<ResultCodeEnum> result = Result.build(null, ResultCodeEnum.LOGIN_AUTH);
byte[] bits = JSONObject.toJSONString(result).getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = response.bufferFactory().wrap(bits);
// 指定编码否则在浏览器中会中文乱码
response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
return response.writeWith(Mono.just(buffer));
}
}

View File

@ -4,3 +4,7 @@ bunny:
discovery:
namespace: spzx
redis:
host: 106.15.251.123
port: 6379
database: 2

View File

@ -47,6 +47,11 @@ spring:
uri: lb://service-order
predicates:
- Path=/*/order/**
data:
redis:
host: ${bunny.redis.host}
port: ${bunny.redis.port}
database: ${bunny.redis.database}
logging:
level:

View File

@ -15,6 +15,7 @@
<modules>
<module>service-user</module>
<module>service-order</module>
<module>service-cart</module>
</modules>
<properties>
@ -27,11 +28,6 @@
<artifactId>common-service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- redis的起步依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- mybatis的起步依赖 -->
<dependency>

View File

@ -0,0 +1,28 @@
<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.atguigu</groupId>
<artifactId>spzx-service</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>service-cart</artifactId>
<packaging>jar</packaging>
<name>service-cart</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,13 @@
package com.atguigu.cart;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
// 排除数据库的自动化配置Cart微服务不需要访问数据库
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class CartApplication {
public static void main(String[] args) {
SpringApplication.run(CartApplication.class, args);
}
}

View File

@ -0,0 +1,10 @@
bunny:
redis:
host: 106.15.251.123
port: 6379
database: 2
nacos:
server-addr: z-bunny.cn:8848
discovery:
namespace: spzx

View File

@ -0,0 +1,29 @@
server:
port: 8513
spring:
profiles:
active: dev
application:
name: service-cart
cloud:
nacos:
discovery:
namespace: ${bunny.nacos.discovery.namespace}
server-addr: ${bunny.nacos.server-addr}
data:
redis:
host: ${bunny.redis.host}
port: ${bunny.redis.port}
database: ${bunny.redis.database}
logging:
level:
com.atguigu.spzx.user.mapper: debug
com.atguigu.spzx.user.controller: info
com.atguigu.spzx.user.service: info
pattern:
dateformat: HH:mm:ss:SSS
file:
path: "logs/${spring.application.name}"

View File

@ -0,0 +1,16 @@
-----------------▄██-█▄---------
-----------------███▄██▄--------
-----------------███████--------
-----------------▀███████-------
-------------------██████▄▄-----
-------------------█████████▄---
-------------------██████▄████--
-------▄███████████████████████-
-----▄███████████████████████▀--
---▄██████████████████████------
---███████████████████████------
---███████████████████████------
-▄▄██████████████████████▀------
-█████████████████▀█████--------
-▀██████████████▀▀-▀█████▄------
-------▀▀▀▀▀▀▀▀▀------▀▀▀▀------

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<contextName>logback</contextName>
<!-- 日志的输出目录 -->
<property name="log.path" value="logs//service-user//logs"/>
<!--控制台日志格式:彩色日志-->
<!-- magenta:洋红 -->
<!-- boldMagenta:粗红-->
<!-- cyan:青色 -->
<!-- white:白色 -->
<!-- magenta:洋红 -->
<property name="CONSOLE_LOG_PATTERN"
value="%yellow(%date{yyyy-MM-dd HH:mm:ss}) %highlight([%-5level]) %green(%logger) %msg%n"/>
<!--文件日志格式-->
<property name="FILE_LOG_PATTERN" value="%date{yyyy-MM-dd HH:mm:ss} [%-5level] %thread %file:%line %logger %msg%n"/>
<!--编码-->
<property name="ENCODING" value="UTF-8"/>
<!-- 控制台日志 -->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!-- 临界值过滤器 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level>
</filter>
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
<charset>${ENCODING}</charset>
</encoder>
</appender>
<!-- 文件日志 -->
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>${log.path}//log.log</file>
<append>true</append>
<encoder>
<pattern>%date{yyyy-MM-dd HH:mm:ss} %msg%n</pattern>
<charset>${ENCODING}</charset>
</encoder>
</appender>
<!-- 开发环境 -->
<springProfile name="dev">
<!-- com.atguigu日志记录器业务程序INFO级别 -->
<logger name="com.atguigu" level="INFO"/>
<!-- 根日志记录器INFO级别 -->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="FILE"/>
</root>
</springProfile>
</configuration>

View File

@ -33,7 +33,7 @@ public class SmsServiceImpl implements SmsService {
UserInfo userInfo = userInfoMapper.selectByUsername(phone);
emptyUtil.isNotEmpty(isExist, MessageConstant.MESSAGE_CODE_NOT_PASS);
emptyUtil.isNotEmpty(userInfo, MessageConstant.USER_DOES_IS_EXIST);
// 1. 生成验证码
String code = RandomStringUtils.randomNumeric(4);
// 2. 生成的验证码放到Redis中设置过期时间

View File

@ -3,6 +3,7 @@ package com.atguigu.user.service.impl;
import com.alibaba.fastjson.JSON;
import com.atguigu.constant.MessageConstant;
import com.atguigu.constant.UserConstant;
import com.atguigu.context.BaseContext;
import com.atguigu.exception.BunnyException;
import com.atguigu.spzx.model.dto.h5.UserLoginDto;
import com.atguigu.spzx.model.dto.h5.UserRegisterDto;
@ -107,10 +108,12 @@ public class UserInfoServiceImpl implements UserInfoService {
*/
@Override
public UserInfoVo getCurrentUserInfo(String token) {
Object tokenObject = redisTemplate.opsForValue().get(token);
emptyUtil.isEmpty(tokenObject, MessageConstant.TOKEN_IS_EMPTY);
UserInfo userInfo = JSON.parseObject((String) tokenObject, UserInfo.class);
// Object tokenObject = redisTemplate.opsForValue().get(token);
// emptyUtil.isEmpty(tokenObject, MessageConstant.TOKEN_IS_EMPTY);
//
// UserInfo userInfo = JSON.parseObject((String) tokenObject, UserInfo.class);
UserInfo userInfo = BaseContext.getUserInfo();
UserInfoVo userInfoVo = new UserInfoVo();
BeanUtils.copyProperties(userInfo, userInfoVo);