Merge pull request '1.7' (#8) from cyj into main

main
pvtfxms7o 2 months ago
commit ae91627af9

@ -1,4 +1,4 @@
# 编写原因
# 编写原因
写在前面,很多加入我们群里的人,都会问我们源码在哪里,现在仔细回答一下

@ -1,6 +1,6 @@
## 商品分组
#### 商城应用
####商城应用
在mall4j精选商城首页中可以看到有`每日上新`、`商城热卖`、`更多商品`等标签栏,在每一栏位中用来展示特定的商品列表,如下图:。

@ -2,7 +2,7 @@
在看具体的数据库实体设计之前,我们先一起了解下**电商的名词定义**
## 1.1 名词定义
##1.1 名词定义
参考 [《产品 SKU 是什么意思?与之相关的还有哪些?》](https://www.zhihu.com/question/19841574) 整理。

@ -2,7 +2,7 @@
## PageAdapter
##PageAdapter
使用分页时,前端传入的数据统一格式为`current`当前页,`size`每页大小。而我们在数据库中要将这两个数据变更为从第几行到第几行,所以我们需要简单的适配一下:

@ -1,6 +1,6 @@
在小程序登陆的时候,在`MiniAppAuthenticationProvider`中我们看到这样一行代码
```java
``java
yamiUserDetailsService.insertUserIfNecessary(appConnect);
```

@ -2,7 +2,7 @@
网上有很多说解决xss攻击的方法有很多都是和前端有关而实际上在后台这最后一个防御当中是最为重要的。
在mall4j这个项目里面,使用了一个过滤器 `XssFilter`
在mall4这个项目里面使用了一个过滤器 `XssFilter`
```
public class XssFilter implements Filter {

@ -7,7 +7,7 @@
- 多图片上传:`src\components\mul-pic-upload`
- 文件上传:`src\components\file-upload`
上述这些文件上传,都是基于`el-upload`进封装
上述这些文件上传,都是基于`el-upload`进封装

@ -1,6 +1,6 @@
## 权限控制
#### 前端权限控制
### 前端权限控制
在商城运营时,我们可能是多个人员共同操作我们的系统,但是每个操作人员所具备的权限应该不同,权限的不同主要表现在两个部分,即导航菜单的查看权限和页面增删改操作按钮的操作权限。我们的把页面导航菜单查看权限和页面操作按钮统一存储在菜单数据库表中,菜单类型页面资源的类型。类型包括目录 、菜单 、按钮。

@ -1,4 +1,4 @@
## 后台异常处理
# 后台异常处理
在开发过程中,不可避免的是需要处理各种异常,异常处理方法随处可见,所以代码中就会出现大量的`try {...} catch {...} finally {...}` 代码块,不仅会造成大量的冗余代码,而且还影响代码的可读性,所以对异常统一处理非常有必要。为此,我们定义了一个统一的异常类`YamiShopBindException` 与异常管理类 `DefaultExceptionHandlerConfig`

@ -2,7 +2,7 @@
利用`spring`框架中`aop`,我们可以实现业务代码与系统级服务进行解耦,例如日志记录、事务及其他安全业务等,可以使得我们的工程更加容易维护、优雅。如何在系统中添加相应的日志呢?
##### 添加依赖
#### 添加依赖
```
<dependency>

@ -1,4 +1,4 @@
我们后使用`spring` 为我们提供好的统一校验的工具`spring-boot-starter-validation`对请求进行校验。
我们后使用`spring` 为我们提供好的统一校验的工具`spring-boot-starter-validation`对请求进行校验。
```xml
<dependency>

@ -1,4 +1,4 @@
## 通用分页表格实现
# 通用分页表格实现
前端基于VUE的轻量级表格插件 `avue`
后端分页组件使用Mybatis分页插件 `MybatisPlus`

@ -2,7 +2,7 @@
~~~
yami-shops
├── mall4m -- 小程序代码
├──mall4m -- 小程序代码
├── mall4v -- 后台vue代码
├── yami-shop-admin -- 后台vue接口工程[8085]
├── yami-shop-api -- 前端(小程序)接口工程[8086]

@ -1,4 +1,4 @@
里整理了一些经常会被问到的问题:
里整理了一些经常会被问到的问题:
1. 为什么vue打包之后或者修改url之后无法登录
你用chrome按f12看看console提示的信息如`Access-Control-Allow-Origin` 那就是跨域了再看看network的请求方法是不是`options`但是返回不是200这也是跨域了。

@ -13,7 +13,7 @@
我们先来看下是如何获取商品信息的
```java
``java
@PostMapping("/info")
@Operation(summary = "获取用户购物车信息" , description = "获取用户购物车信息,参数为用户选中的活动项数组,以购物车id为key")
public ServerResponseEntity<List<ShopCartDto>> info(@RequestBody Map<Long, ShopCartParam> basketIdShopCartParamMap) {

@ -7,7 +7,7 @@
## 第一步:
# 第一步:
1. 用户点击“立即购买”或“购物车-结算”进入到“确认订单”页面相关url`/p/order/confirm`

@ -8,7 +8,7 @@
我们返回确认订单的接口,看到这样一行代码:
```java
``java
@Operation(summary = "结算,生成订单信息" , description = "传入下单所需要的参数进行下单")
public ServerResponseEntity<ShopCartOrderMergerDto> confirm(@Valid @RequestBody OrderParam orderParam) {
orderService.putConfirmOrderCache(userId,shopCartOrderMergerDto);

@ -1,6 +1,6 @@
> 我们的支付时不允许在订单的支付接口传订单金额的,所以我们采用了订单号进行支付的形式
## 支付
# 支付
我们来到`PayController` ,这里就是统一支付的接口,当然这里的统一支付采用的是模拟支付。

@ -1,3 +1,3 @@
这里只有几点说明:
1. 这里写的是接口设计如果你整个接口的接口文档只需要启动api这个项目然后访问 http://localhost:8086/doc.html
1 这里写的是接口设计如果你整个接口的接口文档只需要启动api这个项目然后访问 http://localhost:8086/doc.html

@ -3,6 +3,6 @@
安装JDK,如果没有java-17-openjdk-devel就没有javac命令
```bash
yum install java-17-openjdk java-17-openjdk-devel
yu install java-17-openjdk java-17-openjdk-devel
```

@ -14,7 +14,7 @@ docker-compose version 1.17.1, build 6d101fb
Linux 系统请使用以下介绍的方法安装。
## 安装方法一:二进制包
# 安装方法一:二进制包
在 Linux 上的也安装十分简单,从 [官方 GitHub Release](https://github.com/docker/compose/releases) 处直接下载编译好的二进制文件即可。

@ -1,4 +1,4 @@
## 安装 Docker
# 安装 Docker
从 2017 年 3 月开始 docker 在原来的基础上分为两个分支版本: Docker CE 和 Docker EE。
Docker CE 即社区免费版Docker EE 即企业版,强调安全,但需付费使用。

@ -9,7 +9,7 @@ docker pull [OPTIONS] NAME[:TAG|@DIGEST]
```
体的选项可以通过 docker pull --help 命令看到,这里我们说一下镜像名称的格式。
体的选项可以通过 docker pull --help 命令看到,这里我们说一下镜像名称的格式。
- Docker 镜像仓库地址:地址的格式一般是 <域名/IP>[:端口号]。默认地址是 Docker Hub。
- 仓库名:如之前所说,这里的仓库名是两段式名称,即 <用户名>/<软件名>。对于 Docker Hub如果不给出用户名则默认为 library也就是官方镜像。

@ -4,7 +4,7 @@
从 Docker 镜像仓库获取镜像的命令是 `docker pull`。其命令格式为:
```
# docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
docker pull [OPTIONS] NAME[:TAG|@DIGEST]
```

@ -2,7 +2,7 @@
**如果无法理解我们所编写的 `Dockerfile`强烈的不推荐使用docker进行生产环境部署**
*如果无法理解我们所编写的 `Dockerfile`强烈的不推荐使用docker进行生产环境部署**
0. 将整个项目上传到centos中进入到项目根目录
1. 安装 `docker` (参考《docker centos 安装》)

@ -1,6 +1,6 @@
安装maven的前提是安装jdk参考《linux jdk安装》
```bash
``bash
// 使用配置工具配置第三方epel源仓库
yum-config-manager --add-repo http://repos.fedorapeople.org/repos/dchen/apache-maven/epel-apache-maven.repo
yum-config-manager --enable epel-apache-maven

@ -2,7 +2,7 @@
Nginx官方提供了Yum源
## 1、安装nginx
# 1、安装nginx
```shell
yum install -y nginx

@ -1,6 +1,6 @@
本文为大家介绍了*CentOS* 7 64位 安装 *MySQL5.7* 的详细步骤
## 1、配置YUM源
# 1、配置YUM源
在[MySQL]官网中下载YUM源rpm安装包http://dev.mysql.com/downloads/repo/yum/

@ -1,7 +1,7 @@
## 安装redis
```
#安装tcl redis需要
安装tcl redis需要
wget http://downloads.sourceforge.net/tcl/tcl8.6.8-src.tar.gz
tar xzvf tcl8.6.8-src.tar.gz -C /usr/local/
cd /usr/local/tcl8.6.8/unix/

@ -1,6 +1,6 @@
## 安装jdk
JDK,如果没有java-17-openjdk-devel就没有javac命令
安JDK,如果没有java-17-openjdk-devel就没有javac命令
```bash
yum install java-17-openjdk java-17-openjdk-devel

@ -8,6 +8,7 @@
*
*/
// 该类所属的包名表明其处于管理端相关的包下作为整个Web应用的启动类所在的位置
package com.yami.shop.admin;
import org.springframework.boot.SpringApplication;
@ -20,31 +21,45 @@ import org.springframework.context.annotation.ComponentScan;
/**
* @author lgh
* Spring BootWeb
* Spring BootWeb
* Spring Boot使
* ServletWAR
*/
// @SpringBootApplication是一个组合注解相当于同时使用了@Configuration、@EnableAutoConfiguration和@ComponentScan这三个注解
// 下面分别对其作用进行详细解释:
// @Configuration注解表明这个类是一个Java配置类可用于定义Spring容器中的Bean以及配置相关的信息类似于传统的XML配置文件的作用。
// @EnableAutoConfiguration注解开启Spring Boot的自动配置功能它会根据项目中添加的依赖以及一些默认的配置规则自动配置Spring应用的各种组件
// 例如自动配置数据源、Web相关组件等极大地减少了手动配置的工作量。
// @ComponentScan注解用于指定Spring要扫描的基础包路径在这里表示会扫描com.yami.shop及其子包下的所有组件比如使用@Component、@Service、@Repository、@Controller等注解标注的类
// 找到这些组件后会将它们纳入Spring容器进行管理使得它们能够在应用中被自动注入和使用。
@SpringBootApplication
// @SpringBootApplication注解是一个组合注解相当于同时使用了@Configuration、@EnableAutoConfiguration和@ComponentScan
// 它表明这个类是一个Spring Boot应用的配置类并且开启自动配置以及组件扫描功能。
// 通过@ComponentScan注解指定Spring要扫描的基础包路径为com.yami.shop及其子包确保应用中的各种组件能被正确扫描并加载到Spring容器中进行管理。
@ComponentScan("com.yami.shop")
// @ComponentScan注解用于指定Spring要扫描的基础包路径在这里表示会扫描com.yami.shop及其子包下的所有组件比如@Component、@Service、@Repository等标注的类
// 以便将这些组件纳入Spring容器进行管理。
// @EnableCaching注解用于开启Spring的缓存功能当应用中需要使用缓存来提高性能比如缓存数据库查询结果、方法返回值等情况时启用此注解后
// 可以通过在相应的方法上使用缓存相关注解(如@Cacheable、@CachePut、@CacheEvict等来配置缓存策略。在这里表示应用开启了缓存功能后续可按需配置具体的缓存细节。
@EnableCaching
public class WebApplication extends SpringBootServletInitializer {
/**
* JavaSpring BootWebApplication.classargs
* Spring BootWebWeb
* Java
* SpringApplicationrunSpring BootWebApplication.classargs
* Spring Boot
* @ComponentScanWebWeb
*
* @param args
*/
public static void main(String[] args) {
SpringApplication.run(WebApplication.class, args);
}
/**
* WARServletTomcatSpring
* SpringApplicationBuilderWebApplication.class便
* WARServletTomcatJettySpring
* SpringApplicationBuilderWebApplication.class
* ServletSpring
*
*/
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(WebApplication.class);
}
}

@ -16,16 +16,76 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* AdminBeanConfigSpringBean便Spring使
* BeanID
*
* @author lanhai
*/
@Configuration
// @Configuration注解表明这个类是一个配置类Spring会扫描到这个类并根据其中定义的Bean配置方法来创建和管理相应的Bean对象。
@AllArgsConstructor
// @AllArgsConstructor注解会为这个类生成一个包含所有参数的构造函数方便依赖注入操作在这里用于注入AdminConfig实例。
public class AdminBeanConfig {
// 通过构造函数注入AdminConfig实例AdminConfig应该是包含了相关配置信息的类此处用于获取创建Snowflake实例所需的参数。
private final AdminConfig adminConfig;
/**
* snowflakeSpringSnowflakeBean
* SnowflakeID
* AdminConfigworkerIddatacenterIdSnowflakeID
*
* @return SnowflakeSpringID使
*/
@Bean
public Snowflake snowflake() {
return new Snowflake(adminConfig.getWorkerId(), adminConfig.getDatacenterId());
}
}
java
/*
* Copyright (c) 2018 - 2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
*
*
*
*/
package com.yami.shop.admin.config;
import cn.hutool.core.lang.Snowflake;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* AdminBeanConfigSpringBean便Spring使
* BeanID
*
* @author lanhai
*/
@Configuration
// @Configuration注解表明这个类是一个配置类Spring会扫描到这个类并根据其中定义的Bean配置方法来创建和管理相应的Bean对象
@AllArgsConstructor
// @AllArgsConstructor注解会为这个类生成一个包含所有参数的构造函数方便依赖注入操作在这里用于注入AdminConfig实例
public class AdminBeanConfig {
// 通过构造函数注入AdminConfig实例AdminConfig应该是包含了相关配置信息的类此处用于获取创建Snowflake实例所需的参数
private final AdminConfig adminConfig;
/**
* snowflakeSpringSnowflakeBean
* SnowflakeID
* AdminConfigworkerIddatacenterIdSnowflakeID
*
* @return SnowflakeSpringID使
*/
@Bean
public Snowflake snowflake() {
return new Snowflake(adminConfig.getWorkerId(), adminConfig.getDatacenterId());
return new Snowflake(adminConfig.getWorkerId(), adminConfig.getDatacenterId());
}
}
}

@ -1,5 +1,8 @@
```java
/*
* Copyright (c) 2018-2999 广 All rights reserved.
* Copyright (c) 2018 - 2999 广 All rights reserved.
*
* https://www.mall4j.com/
*
@ -15,25 +18,44 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
/**
*
* AdminConfig
* Spring便使
*
* @author lgh
*/
@Data
@Component
/**
* @ComponentSpringSpring
* 使
*/
@PropertySource("classpath:admin.properties")
/**
* @PropertySourceadmin.properties
*/
@ConfigurationProperties(prefix = "admin")
/**
* @ConfigurationProperties"admin"
*/
@Data
/**
* @DataLombokGetterSetter
* toStringequalshashCode便访
*/
public class AdminConfig {
/**
* ID
*/
private Integer datacenterId;
/**
* ID
*/
private Integer workerId;
/**
* ID
* ID使Snowflake
* ID
*/
private Integer datacenterId;
/**
* ID
* ID
* ID
*/
private Integer workerId;
}
```

@ -18,26 +18,39 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Swagger使
* SwaggerSwagger
* 使Swagger
* @author LGH
*/
@Configuration
public class SwaggerConfiguration {
@Bean
public GroupedOpenApi baseRestApi() {
return GroupedOpenApi.builder()
.group("接口文档")
.packagesToScan("com.yami").build();
}
/**
* Rest API便Swagger
* "接口文档""com.yami"
*
* @return GroupedOpenApi
*/
@Bean
public GroupedOpenApi baseRestApi() {
return GroupedOpenApi.builder()
.group("接口文档")
.packagesToScan("com.yami")
.build();
}
@Bean
public OpenAPI springShopOpenApi() {
return new OpenAPI()
.info(new Info().title("Mall4j接口文档")
.description("Mall4j接口文档openapi3.0 接口,用于前端对接")
.version("v0.0.1")
.license(new License().name("使用请遵守AGPL3.0授权协议").url("https://www.mall4j.com")));
}
}
/**
* Spring ShopOpenAPI使
* Swagger便使
* @return OpenAPI
*/
@Bean
public OpenAPI springShopOpenApi() {
return new OpenAPI()
.info(new Info().title("Mall4j接口文档")
.description("Mall4j接口文档openapi3.0 接口,用于前端对接")
.version("v0.0.1")
.license(new License().name("使用请遵守AGPL3.0授权协议").url("https://www.mall4j.com")));
}
}

@ -7,6 +7,7 @@
*
*
*/
package com.yami.shop.admin.config;
import com.xxl.job.core.executor.impl.XxlJobSpringExecutor;
@ -19,47 +20,77 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* xxl-job config
* xxl-job
* spring quartzquartzxxl-job
* xxl-jobxxl-job
* xxl-jobxxl-job-adminxxl-job
* xxl-jobXXL-JOBXXL-JOB
*
*
* 使SpringQuartz
* 使QuartzQuartz
* XXL-JOBXXL-JOB
* XXL-JOBXXL-JOB
*
* 使XXL-JOB
* 1. XXL-JOBxxl-job-admin
* 2. xxl-job-admin
* 3. XXL-JOB
*
* @author FrozenWatermelon
* @date 2021/1/18
*/
@Configuration
public class XxlJobConfig {
// 创建一个日志记录器用于记录与XXL-JOB配置相关的日志信息方便后续排查问题以及了解配置初始化等过程的情况。
private final Logger logger = LoggerFactory.getLogger(XxlJobConfig.class);
// 通过@Value注解从配置文件中读取xxl-job.admin.addresses属性值该值代表了XXL-JOB管理端的地址用于执行器与管理端进行通信连接。
@Value("${xxl-job.admin.addresses}")
private String adminAddresses;
// 通过@Value注解从配置文件中读取xxl-job.accessToken属性值该值作为访问令牌用于在执行器与XXL-JOB管理端通信时进行身份验证等安全相关操作。
@Value("${xxl-job.accessToken}")
private String accessToken;
// 通过@Value注解从配置文件中读取xxl-job.logPath属性值该值指定了定时任务执行日志的存储路径方便后续查看任务执行情况以及排查可能出现的问题。
@Value("${xxl-job.logPath}")
private String logPath;
// 通过@Value注解从配置文件中读取server.port属性值即当前项目服务所监听的端口号后续可能会基于此端口号来确定执行器相关的端口等信息这里是做了端口偏移使用
@Value("${server.port}")
private int port;
// 注入InetUtils对象该对象是Spring Cloud提供的一个工具类用于处理网络相关的操作在这里主要用于获取合适的IP地址
// 特别是在多网卡、容器内部署等复杂网络环境下能够帮助准确获取到执行器要绑定的IP地址避免IP地址获取不准确导致的通信问题。
@Autowired
private InetUtils inetUtils;
// 以下是配置XXL-JOB执行器的方法目前被注释掉了原因在类的开头注释部分已经详细说明了。
// 如果要启用XXL-JOB执行定时任务需要取消这段代码的注释并确保已经正确配置好了XXL-JOB相关环境。
// @Bean注解表示该方法会创建一个Spring管理的Bean对象在这里就是创建一个XxlJobSpringExecutor类型的Bean
// 该对象是XXL-JOB在Spring环境下的执行器实现类负责与XXL-JOB管理端交互以及执行具体的定时任务等操作。
// @Bean
// public XxlJobSpringExecutor xxlJobExecutor() {
//
// // 记录一条日志信息表示开始进行XXL-JOB配置初始化方便在启动项目时查看相关配置是否正常加载等情况。
// logger.info(">>>>>>>>>>> xxl-job config init.");
// // 创建一个XxlJobSpringExecutor实例用于后续配置并返回作为Spring管理的Bean对象。
// XxlJobSpringExecutor xxlJobSpringExecutor = new XxlJobSpringExecutor();
// // 设置XXL-JOB管理端的地址让执行器知道要与哪个管理端进行通信连接这个地址就是从配置文件中读取到的adminAddresses的值。
// xxlJobSpringExecutor.setAdminAddresses(adminAddresses);
// // 设置应用名称,这里固定设置为"mall4j"在XXL-JOB管理端可以通过这个名称来区分不同的执行器所属的应用方便管理和监控。
// xxlJobSpringExecutor.setAppname("mall4j");
// // 针对多网卡、容器内部署等情况,可借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP
// // 针对多网卡、容器内部署等情况,借助 "spring-cloud-commons" 提供的 "InetUtils" 组件灵活定制注册IP
// // 通过调用InetUtils的findFirstNonLoopbackAddress方法获取到第一个非回环地址即有效的网络IP地址并设置为执行器绑定的IP地址
// // 确保执行器能够在复杂网络环境下正确地与XXL-JOB管理端进行通信。
// xxlJobSpringExecutor.setIp(inetUtils.findFirstNonLoopbackAddress().getHostAddress());
// // 设置执行器监听的端口号这里采用了项目服务端口号port加上1000的方式来确定避免端口冲突等问题同时也方便统一管理端口分配。
// xxlJobSpringExecutor.setPort(port + 1000);
// // 设置访问令牌用于在执行器与XXL-JOB管理端通信时进行身份验证等安全相关操作这个值就是从配置文件中读取到的accessToken的值。
// xxlJobSpringExecutor.setAccessToken(accessToken);
// // 设置定时任务执行日志的存储路径方便后续查看任务执行情况以及排查可能出现的问题这个值就是从配置文件中读取到的logPath的值。
// xxlJobSpringExecutor.setLogPath(logPath);
// // 设置日志保留天数这里设置为3天表示定时任务执行日志只会保留最近3天的记录超过这个天数的旧日志会被自动清理
// // 这样可以避免日志文件过多占用磁盘空间,同时也能保证在一定时间范围内可以查看历史任务执行情况。
// xxlJobSpringExecutor.setLogRetentionDays(3);
// return xxlJobSpringExecutor;
// }
}
}

@ -7,6 +7,7 @@
*
*
*/
package com.yami.shop.admin.controller;
import cn.hutool.core.util.StrUtil;
@ -43,6 +44,8 @@ import java.util.Set;
import java.util.stream.Collectors;
/**
* token
*
* @author FrozenWatermelon
* @date 2020/6/30
*/
@ -50,51 +53,72 @@ import java.util.stream.Collectors;
@Tag(name = "登录")
public class AdminLoginController {
// 注入TokenStore用于存储和获取用户登录相关的token信息比如生成token、从存储中获取token相关数据等操作。
@Autowired
private TokenStore tokenStore;
// 注入SysUserService用于与系统用户相关的数据库操作比如根据用户名查询用户信息等。
@Autowired
private SysUserService sysUserService;
// 注入SysMenuService用于与系统菜单相关的数据库操作例如查询系统菜单列表等此处主要用于获取权限相关信息。
@Autowired
private SysMenuService sysMenuService;
// 注入PasswordCheckManager用于对用户输入的密码进行合法性、安全性等方面的检查比如验证密码是否符合规则、是否在一定时间内多次输错被限制登录等。
@Autowired
private PasswordCheckManager passwordCheckManager;
// 注入CaptchaService用于处理验证码相关的操作比如验证验证码是否正确、是否过期等。
@Autowired
private CaptchaService captchaService;
// 注入PasswordManager用于对密码进行解密等相关操作比如将前端传来的加密密码进行解密以便后续进行密码验证。
@Autowired
private PasswordManager passwordManager;
/**
* token
*
* @param captchaAuthenticationDTO @Valid
* @return ServerResponseEntitytoken
*/
@PostMapping("/adminLogin")
@Operation(summary = "账号密码 + 验证码登录(用于后台登录)" , description = "通过账号/手机号/用户名密码登录")
@Operation(summary = "账号密码 + 验证码登录(用于后台登录)", description = "通过账号/手机号/用户名密码登录")
public ServerResponseEntity<?> login(
@Valid @RequestBody CaptchaAuthenticationDTO captchaAuthenticationDTO) {
// 登陆后台登录需要再校验一遍验证码
// 创建CaptchaVO对象用于传递给验证码服务进行验证将前端传来的验证码验证信息设置进去。
CaptchaVO captchaVO = new CaptchaVO();
captchaVO.setCaptchaVerification(captchaAuthenticationDTO.getCaptchaVerification());
// 调用验证码服务的验证方法传入CaptchaVO对象获取验证结果响应模型。
ResponseModel response = captchaService.verification(captchaVO);
// 如果验证不成功,即验证码有误或者已过期,返回相应的错误提示信息给前端。
if (!response.isSuccess()) {
return ServerResponseEntity.showFailMsg("验证码有误或已过期");
}
// 根据用户名从数据库中查询系统用户信息,如果未查询到用户,说明账号不存在,抛出相应的异常提示账号或密码不正确。
SysUser sysUser = sysUserService.getByUserName(captchaAuthenticationDTO.getUserName());
if (sysUser == null) {
throw new YamiShopBindException("账号或密码不正确");
}
// 半小时内密码输入错误十次已限制登录30分钟
// 先对前端传来的密码进行解密操作,以便后续与数据库中存储的密码进行比对验证。
String decryptPassword = passwordManager.decryptPassword(captchaAuthenticationDTO.getPassWord());
passwordCheckManager.checkPassword(SysTypeEnum.ADMIN,captchaAuthenticationDTO.getUserName(), decryptPassword, sysUser.getPassword());
// 调用密码检查管理器,传入系统类型、用户名、解密后的密码以及数据库中存储的用户密码,进行密码验证,若不符合规则会抛出相应异常。
passwordCheckManager.checkPassword(SysTypeEnum.ADMIN, captchaAuthenticationDTO.getUserName(), decryptPassword, sysUser.getPassword());
// 不是店铺超级管理员,并且是禁用状态,无法登录
if (Objects.equals(sysUser.getStatus(),0)) {
if (Objects.equals(sysUser.getStatus(), 0)) {
// 若用户状态为禁用这里假设状态0表示禁用抛出异常提示未找到此用户信息此处实际意思应该是用户不可用
// 未找到此用户信息
throw new YamiShopBindException("未找到此用户信息");
}
// 创建用于存储在token中的用户信息对象用于后续生成token以及传递给前端展示相关用户信息。
UserInfoInTokenBO userInfoInToken = new UserInfoInTokenBO();
userInfoInToken.setUserId(String.valueOf(sysUser.getUserId()));
userInfoInToken.setSysType(SysTypeEnum.ADMIN.value());
@ -102,23 +126,35 @@ public class AdminLoginController {
userInfoInToken.setPerms(getUserPermissions(sysUser.getUserId()));
userInfoInToken.setNickName(sysUser.getUsername());
userInfoInToken.setShopId(sysUser.getShopId());
// 存储token返回vo
// 调用token存储服务将用户信息存储并生成token相关信息获取包含token等详细信息的TokenInfoVO对象然后返回登录成功的响应结果给前端包含了token信息。
TokenInfoVO tokenInfoVO = tokenStore.storeAndGetVo(userInfoInToken);
return ServerResponseEntity.success(tokenInfoVO);
}
/**
* IDID
*
*
* @param userId ID
* @return
*/
private Set<String> getUserPermissions(Long userId) {
List<String> permsList;
//系统管理员,拥有最高权限
if(userId == Constant.SUPER_ADMIN_ID){
// 系统管理员,拥有最高权限
if (userId == Constant.SUPER_ADMIN_ID) {
// 如果是系统超级管理员,查询所有的系统菜单列表,获取每个菜单对应的权限字符串,组成权限列表。
List<SysMenu> menuList = sysMenuService.list(Wrappers.emptyWrapper());
permsList = menuList.stream().map(SysMenu::getPerms).collect(Collectors.toList());
}else{
} else {
// 如果不是系统超级管理员,调用用户服务的方法查询该用户的所有权限信息,得到权限列表。
permsList = sysUserService.queryAllPerms(userId);
}
return permsList.stream().flatMap((perms)->{
// 将权限列表中的每个权限字符串进行分割(假设权限字符串之间以逗号分隔),然后扁平化处理,去除重复的权限,最终收集为一个权限集合返回。
return permsList.stream().flatMap((perms) -> {
if (StrUtil.isBlank(perms)) {
return null;
}
@ -126,4 +162,4 @@ public class AdminLoginController {
}
).collect(Collectors.toSet());
}
}
}

@ -27,38 +27,55 @@ import java.util.List;
import java.util.Objects;
/**
* AreaControllerArea
* ID
* @author lgh on 2018/10/26.
*/
@RestController
@RequestMapping("/admin/area")
public class AreaController {
// 自动注入AreaService用于调用与地区相关的业务逻辑方法
@Autowired
private AreaService areaService;
/**
*
*
* AreaPageParam
* AreaServicepageServerResponseEntity
* @param area
* @param page
* @return ServerResponseEntityIPage<Area>
*/
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('admin:area:page')")
public ServerResponseEntity<IPage<Area>> page(Area area,PageParam<Area> page) {
public ServerResponseEntity<IPage<Area>> page(Area area, PageParam<Area> page) {
IPage<Area> sysUserPage = areaService.page(page, new LambdaQueryWrapper<Area>());
return ServerResponseEntity.success(sysUserPage);
}
/**
*
*
* Area
* AreaServicelist使LambdaQueryWrapper
* ServerResponseEntity
* @param area
* @return ServerResponseEntityList<Area>
*/
@GetMapping("/list")
@PreAuthorize("@pms.hasPermission('admin:area:list')")
public ServerResponseEntity<List<Area>> list(Area area) {
List<Area> areas = areaService.list(new LambdaQueryWrapper<Area>()
.like(area.getAreaName() != null, Area::getAreaName, area.getAreaName()));
.like(area.getAreaName()!= null, Area::getAreaName, area.getAreaName()));
return ServerResponseEntity.success(areas);
}
/**
* id
* id
* AreaServicelistByPidID
* ServerResponseEntity
* @param pid ID
* @return ServerResponseEntityList<Area>
*/
@GetMapping("/listByPid")
public ServerResponseEntity<List<Area>> listByPid(Long pid) {
@ -67,7 +84,11 @@ public class AreaController {
}
/**
*
* ID
* IDAreaServicegetById
* ServerResponseEntity
* @param id ID
* @return ServerResponseEntityArea
*/
@GetMapping("/info/{id}")
@PreAuthorize("@pms.hasPermission('admin:area:info')")
@ -77,12 +98,17 @@ public class AreaController {
}
/**
*
*
* ID
* AreaServiceremoveAreaCacheByParentIdID
* AreaServicesaveServerResponseEntity
* @param area @Valid
* @return ServerResponseEntityVoid
*/
@PostMapping
@PreAuthorize("@pms.hasPermission('admin:area:save')")
public ServerResponseEntity<Void> save(@Valid @RequestBody Area area) {
if (area.getParentId() != null) {
if (area.getParentId()!= null) {
Area parentArea = areaService.getById(area.getParentId());
area.setLevel(parentArea.getLevel() + 1);
areaService.removeAreaCacheByParentId(area.getParentId());
@ -92,17 +118,23 @@ public class AreaController {
}
/**
*
*
* IDareaDb
*
* hasSameName
* AreaServiceupdateByIdIDServerResponseEntity
* @param area @Valid
* @return ServerResponseEntityVoid
*/
@PutMapping
@PreAuthorize("@pms.hasPermission('admin:area:update')")
public ServerResponseEntity<Void> update(@Valid @RequestBody Area area) {
Area areaDb = areaService.getById(area.getAreaId());
// 判断当前省市区级别如果是1级、2级则不能修改级别不能修改成别人的下级
if(Objects.equals(areaDb.getLevel(), AreaLevelEnum.FIRST_LEVEL.value()) && !Objects.equals(area.getLevel(),AreaLevelEnum.FIRST_LEVEL.value())){
if (Objects.equals(areaDb.getLevel(), AreaLevelEnum.FIRST_LEVEL.value()) &&!Objects.equals(area.getLevel(), AreaLevelEnum.FIRST_LEVEL.value())) {
throw new YamiShopBindException("不能改变一级行政地区的级别");
}
if(Objects.equals(areaDb.getLevel(),AreaLevelEnum.SECOND_LEVEL.value()) && !Objects.equals(area.getLevel(),AreaLevelEnum.SECOND_LEVEL.value())){
if (Objects.equals(areaDb.getLevel(), AreaLevelEnum.SECOND_LEVEL.value()) &&!Objects.equals(area.getLevel(), AreaLevelEnum.SECOND_LEVEL.value())) {
throw new YamiShopBindException("不能改变二级行政地区的级别");
}
hasSameName(area);
@ -112,7 +144,12 @@ public class AreaController {
}
/**
*
*
* IDAreaServicegetById
* AreaServiceremoveByIdID
* ServerResponseEntity
* @param id ID
* @return ServerResponseEntityVoid
*/
@DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('admin:area:delete')")
@ -123,14 +160,20 @@ public class AreaController {
return ServerResponseEntity.success();
}
/**
*
* LambdaQueryWrapperIDID
* AreaServicecount0
* @param area
*/
private void hasSameName(Area area) {
long count = areaService.count(new LambdaQueryWrapper<Area>()
.eq(Area::getParentId, area.getParentId())
.eq(Area::getAreaName, area.getAreaName())
.ne(Objects.nonNull(area.getAreaId()) && !Objects.equals(area.getAreaId(), 0L), Area::getAreaId, area.getAreaId())
.eq(Area::getParentId, area.getParentId())
.eq(Area::getAreaName, area.getAreaName())
.ne(Objects.nonNull(area.getAreaId()) &&!Objects.equals(area.getAreaId(), 0L), Area::getAreaId, area.getAreaId())
);
if (count > 0) {
throw new YamiShopBindException("该地区已存在");
}
}
}
}

@ -26,73 +26,103 @@ import jakarta.validation.Valid;
import java.util.Objects;
/**
*
*
*
*
* @author lgh
*/
@RestController
@RequestMapping("/admin/attribute")
public class AttributeController {
// 注入ProdPropService用于与商品属性相关的业务逻辑处理例如查询、保存、更新、删除商品属性及其相关值等操作。
@Autowired
private ProdPropService prodPropService;
/**
*
*/
/**
* ProdPropPageParam
*
* @PreAuthorize"admin:attribute:page"访
*/
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('admin:attribute:page')")
public ServerResponseEntity<IPage<ProdProp>> page(ProdProp prodProp,PageParam<ProdProp> page){
prodProp.setRule(ProdPropRule.ATTRIBUTE.value());
prodProp.setShopId(SecurityUtils.getSysUser().getShopId());
IPage<ProdProp> prodPropPage = prodPropService.pagePropAndValue(prodProp,page);
return ServerResponseEntity.success(prodPropPage);
}
@PreAuthorize("@pms.hasPermission('admin:attribute:page')")
public ServerResponseEntity<IPage<ProdProp>> page(ProdProp prodProp, PageParam<ProdProp> page) {
// 设置商品属性的规则为属性类型这里假设ProdPropRule.ATTRIBUTE表示属性类型用于后续业务逻辑中区分不同类型的商品属性规则。
prodProp.setRule(ProdPropRule.ATTRIBUTE.value());
// 设置商品属性所属的店铺ID通过SecurityUtils工具类获取当前登录用户所属的店铺ID确保查询的是当前店铺下的商品属性信息。
prodProp.setShopId(SecurityUtils.getSysUser().getShopId());
// 调用ProdPropService的pagePropAndValue方法传入设置好的商品属性对象和分页参数获取分页后的商品属性信息结果集。
IPage<ProdProp> prodPropPage = prodPropService.pagePropAndValue(prodProp, page);
// 将查询到的分页商品属性信息封装在成功的响应实体中返回给前端。
return ServerResponseEntity.success(prodPropPage);
}
/**
*
*/
@GetMapping("/info/{id}")
@PreAuthorize("@pms.hasPermission('admin:attribute:info')")
public ServerResponseEntity<ProdProp> info(@PathVariable("id") Long id){
ProdProp prodProp = prodPropService.getById(id);
return ServerResponseEntity.success(prodProp);
}
* IDID
* @PreAuthorize"admin:attribute:info"访
*/
@GetMapping("/info/{id}")
@PreAuthorize("@pms.hasPermission('admin:attribute:info')")
public ServerResponseEntity<ProdProp> info(@PathVariable("id") Long id) {
// 调用ProdPropService的getById方法根据传入的商品属性ID从数据库中获取对应的商品属性对象。
ProdProp prodProp = prodPropService.getById(id);
// 将获取到的商品属性对象封装在成功的响应实体中返回给前端。
return ServerResponseEntity.success(prodProp);
}
/**
*
*/
@PostMapping
@PreAuthorize("@pms.hasPermission('admin:attribute:save')")
public ServerResponseEntity<Void> save(@Valid ProdProp prodProp){
prodProp.setRule(ProdPropRule.ATTRIBUTE.value());
prodProp.setShopId(SecurityUtils.getSysUser().getShopId());
prodPropService.saveProdPropAndValues(prodProp);
return ServerResponseEntity.success();
}
/**
* @ValidProdProp
* ID
* @PreAuthorize"admin:attribute:save"访
*/
@PostMapping
@PreAuthorize("@pms.hasPermission('admin:attribute:save')")
public ServerResponseEntity<Void> save(@Valid ProdProp prodProp) {
// 设置商品属性的规则为属性类型这里假设ProdPropRule.ATTRIBUTE表示属性类型用于后续业务逻辑中区分不同类型的商品属性规则。
prodProp.setRule(ProdPropRule.ATTRIBUTE.value());
// 设置商品属性所属的店铺ID通过SecurityUtils工具类获取当前登录用户所属的店铺ID确保保存的是当前店铺下的商品属性信息。
prodProp.setShopId(SecurityUtils.getSysUser().getShopId());
// 调用ProdPropService的saveProdPropAndValues方法将设置好的商品属性对象及其相关值保存到数据库中。
prodPropService.saveProdPropAndValues(prodProp);
// 返回表示操作成功的响应实体由于这里只是执行保存操作无需返回具体数据所以返回的是Void类型的成功响应。
return ServerResponseEntity.success();
}
/**
*
*/
@PutMapping
@PreAuthorize("@pms.hasPermission('admin:attribute:update')")
public ServerResponseEntity<Void> update(@Valid ProdProp prodProp){
ProdProp dbProdProp = prodPropService.getById(prodProp.getPropId());
if (!Objects.equals(dbProdProp.getShopId(), SecurityUtils.getSysUser().getShopId())) {
throw new YamiShopBindException("没有权限获取该商品规格信息");
}
prodProp.setRule(ProdPropRule.ATTRIBUTE.value());
prodProp.setShopId(SecurityUtils.getSysUser().getShopId());
prodPropService.updateProdPropAndValues(prodProp);
return ServerResponseEntity.success();
}
/**
* @ValidProdProp
* IDID
* ID
* @PreAuthorize"admin:attribute:update"访
*/
@PutMapping
@PreAuthorize("@pms.hasPermission('admin:attribute:update')")
public ServerResponseEntity<Void> update(@Valid ProdProp prodProp) {
// 根据传入的商品属性对象中的ID调用ProdPropService的getById方法从数据库中获取对应的商品属性对象用于后续权限判断等操作。
ProdProp dbProdProp = prodPropService.getById(prodProp.getPropId());
// 判断数据库中查询到的商品属性所属的店铺ID与当前登录用户所属的店铺ID是否一致若不一致则说明当前用户没有权限修改该商品属性信息抛出相应异常。
if (!Objects.equals(dbProdProp.getShopId(), SecurityUtils.getSysUser().getShopId())) {
throw new YamiShopBindException("没有权限获取该商品规格信息");
}
// 设置商品属性的规则为属性类型这里假设ProdPropRule.ATTRIBUTE表示属性类型用于后续业务逻辑中区分不同类型的商品属性规则。
prodProp.setRule(ProdPropRule.ATTRIBUTE.value());
// 设置商品属性所属的店铺ID通过SecurityUtils工具类获取当前登录用户所属的店铺ID确保更新的是当前店铺下的商品属性信息。
prodProp.setShopId(SecurityUtils.getSysUser().getShopId());
// 调用ProdPropService的updateProdPropAndValues方法将设置好的商品属性对象及其相关值更新到数据库中。
prodPropService.updateProdPropAndValues(prodProp);
// 返回表示操作成功的响应实体由于这里只是执行更新操作无需返回具体数据所以返回的是Void类型的成功响应。
return ServerResponseEntity.success();
}
/**
*
*/
@DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('admin:attribute:delete')")
public ServerResponseEntity<Void> delete(@PathVariable Long id){
prodPropService.deleteProdPropAndValues(id,ProdPropRule.ATTRIBUTE.value(),SecurityUtils.getSysUser().getShopId());
return ServerResponseEntity.success();
}
}
/**
* ID
* @PreAuthorize"admin:attribute:delete"访
*/
@DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('admin:attribute:delete')")
public ServerResponseEntity<Void> delete(@PathVariable Long id) {
// 调用ProdPropService的deleteProdPropAndValues方法传入商品属性ID、属性规则这里指定为属性类型以及当前登录用户所属的店铺ID执行删除操作。
prodPropService.deleteProdPropAndValues(id, ProdPropRule.ATTRIBUTE.value(), SecurityUtils.getSysUser().getShopId());
// 返回表示操作成功的响应实体由于这里只是执行删除操作无需返回具体数据所以返回的是Void类型的成功响应。
return ServerResponseEntity.success();
}
}

@ -25,33 +25,44 @@ import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
import java.util.Objects;
/**
*
*
*
* @author lgh
*/
@RestController
@RequestMapping("/admin/brand")
public class BrandController {
// 自动注入BrandService用于调用与品牌相关的业务逻辑方法
@Autowired
private BrandService brandService;
/**
*
*
* BrandPageParam
* BrandServicepageLambdaQueryWrapper
* ServerResponseEntity
* @param brand
* @param page
* @return ServerResponseEntityIPage<Brand>
*/
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('admin:brand:page')")
public ServerResponseEntity<IPage<Brand>> page(Brand brand,PageParam<Brand> page) {
public ServerResponseEntity<IPage<Brand>> page(Brand brand, PageParam<Brand> page) {
IPage<Brand> brands = brandService.page(page,
new LambdaQueryWrapper<Brand>()
.like(StrUtil.isNotBlank(brand.getBrandName()), Brand::getBrandName, brand.getBrandName()).orderByAsc(Brand::getFirstChar));
.like(StrUtil.isNotBlank(brand.getBrandName()), Brand::getBrandName, brand.getBrandName())
.orderByAsc(Brand::getFirstChar));
return ServerResponseEntity.success(brands);
}
/**
*
* ID
* IDBrandServicegetById
* ServerResponseEntity
* @param id ID
* @return ServerResponseEntityBrand
*/
@GetMapping("/info/{id}")
@PreAuthorize("@pms.hasPermission('admin:brand:info')")
@ -61,13 +72,18 @@ public class BrandController {
}
/**
*
*
* BrandServicegetByBrandName
* BrandServicesave
* ServerResponseEntity
* @param brand @Valid
* @return ServerResponseEntityVoid
*/
@PostMapping
@PreAuthorize("@pms.hasPermission('admin:brand:save')")
public ServerResponseEntity<Void> save(@Valid Brand brand) {
Brand dbBrand = brandService.getByBrandName(brand.getBrandName());
if (dbBrand != null) {
if (dbBrand!= null) {
throw new YamiShopBindException("该品牌名称已存在");
}
brandService.save(brand);
@ -75,13 +91,18 @@ public class BrandController {
}
/**
*
*
* BrandServicegetByBrandName
* IDBrandServiceupdateById
* ServerResponseEntity
* @param brand @Valid
* @return ServerResponseEntityVoid
*/
@PutMapping
@PreAuthorize("@pms.hasPermission('admin:brand:update')")
public ServerResponseEntity<Void> update(@Valid Brand brand) {
Brand dbBrand = brandService.getByBrandName(brand.getBrandName());
if (dbBrand != null && !Objects.equals(dbBrand.getBrandId(), brand.getBrandId())) {
if (dbBrand!= null &&!Objects.equals(dbBrand.getBrandId(), brand.getBrandId())) {
throw new YamiShopBindException("该品牌名称已存在");
}
brandService.updateById(brand);
@ -89,7 +110,11 @@ public class BrandController {
}
/**
*
*
* IDBrandServicedeleteByBrand
* ServerResponseEntity
* @param id ID
* @return ServerResponseEntityVoid
*/
@DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('admin:brand:delete')")
@ -97,5 +122,4 @@ public class BrandController {
brandService.deleteByBrand(id);
return ServerResponseEntity.success();
}
}
}

@ -25,10 +25,10 @@ import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
*
*
*
*
* @author lgh
*
*/
@ -36,110 +36,154 @@ import java.util.Objects;
@RequestMapping("/prod/category")
public class CategoryController {
@Autowired
private CategoryService categoryService;
/**
*
* @return
*/
@GetMapping("/table")
@PreAuthorize("@pms.hasPermission('prod:category:page')")
public ServerResponseEntity<List<Category>> table(){
List<Category> categoryMenuList = categoryService.tableCategory(SecurityUtils.getSysUser().getShopId());
return ServerResponseEntity.success(categoryMenuList);
}
/**
*
*/
@GetMapping("/info/{categoryId}")
public ServerResponseEntity<Category> info(@PathVariable("categoryId") Long categoryId){
Category category = categoryService.getById(categoryId);
return ServerResponseEntity.success(category);
}
/**
*
*/
@SysLog("保存分类")
@PostMapping
@PreAuthorize("@pms.hasPermission('prod:category:save')")
public ServerResponseEntity<Void> save(@RequestBody Category category){
category.setShopId(SecurityUtils.getSysUser().getShopId());
category.setRecTime(new Date());
Category categoryName = categoryService.getOne(new LambdaQueryWrapper<Category>().eq(Category::getCategoryName,category.getCategoryName())
.eq(Category::getShopId,category.getShopId()));
if(Objects.nonNull(categoryName)){
throw new YamiShopBindException("类目名称已存在!");
}
categoryService.saveCategory(category);
return ServerResponseEntity.success();
}
/**
*
*/
@SysLog("更新分类")
@PutMapping
@PreAuthorize("@pms.hasPermission('prod:category:update')")
public ServerResponseEntity<String> update(@RequestBody Category category){
category.setShopId(SecurityUtils.getSysUser().getShopId());
if (Objects.equals(category.getParentId(),category.getCategoryId())) {
return ServerResponseEntity.showFailMsg("分类的上级不能是自己本身");
}
Category categoryName = categoryService.getOne(new LambdaQueryWrapper<Category>().eq(Category::getCategoryName,category.getCategoryName())
.eq(Category::getShopId,category.getShopId()).ne(Category::getCategoryId,category.getCategoryId()));
if(categoryName != null){
throw new YamiShopBindException("类目名称已存在!");
}
Category categoryDb = categoryService.getById(category.getCategoryId());
// 如果从下线改成正常,则需要判断上级的状态
if (Objects.equals(categoryDb.getStatus(),0) && Objects.equals(category.getStatus(),1) && !Objects.equals(category.getParentId(),0L)){
Category parentCategory = categoryService.getOne(new LambdaQueryWrapper<Category>().eq(Category::getCategoryId, category.getParentId()));
if(Objects.isNull(parentCategory) || Objects.equals(parentCategory.getStatus(),0)){
// 修改失败,上级分类不存在或者不为正常状态
throw new YamiShopBindException("修改失败,上级分类不存在或者不为正常状态");
}
}
categoryService.updateCategory(category);
return ServerResponseEntity.success();
}
/**
*
*/
@SysLog("删除分类")
@DeleteMapping("/{categoryId}")
@PreAuthorize("@pms.hasPermission('prod:category:delete')")
public ServerResponseEntity<String> delete(@PathVariable("categoryId") Long categoryId){
if (categoryService.count(new LambdaQueryWrapper<Category>().eq(Category::getParentId,categoryId)) >0) {
return ServerResponseEntity.showFailMsg("请删除子分类,再删除该分类");
}
categoryService.deleteCategory(categoryId);
return ServerResponseEntity.success();
}
/**
*
*/
@GetMapping("/listCategory")
public ServerResponseEntity<List<Category>> listCategory(){
return ServerResponseEntity.success(categoryService.list(new LambdaQueryWrapper<Category>()
.le(Category::getGrade, 2)
.eq(Category::getShopId, SecurityUtils.getSysUser().getShopId())
.orderByAsc(Category::getSeq)));
}
/**
*
*/
@GetMapping("/listProdCategory")
public ServerResponseEntity<List<Category>> listProdCategory(){
List<Category> categories = categoryService.treeSelect(SecurityUtils.getSysUser().getShopId(),2);
return ServerResponseEntity.success(categories);
}
}
// 注入CategoryService用于与商品分类相关的业务逻辑处理例如查询、保存、更新、删除分类等操作。
@Autowired
private CategoryService categoryService;
/**
* CategoryService
* ID
* @PreAuthorize"prod:category:page"访
*
* @return ServerResponseEntity
*/
@GetMapping("/table")
@PreAuthorize("@pms.hasPermission('prod:category:page')")
public ServerResponseEntity<List<Category>> table() {
// 调用CategoryService的tableCategory方法传入当前登录用户所属的店铺ID获取用于菜单页面展示的分类列表信息。
List<Category> categoryMenuList = categoryService.tableCategory(SecurityUtils.getSysUser().getShopId());
// 将获取到的分类列表信息封装在成功的响应实体中返回给前端。
return ServerResponseEntity.success(categoryMenuList);
}
/**
* IDIDCategoryServicegetById
*
*
* @param categoryId
* @return ServerResponseEntity
*/
@GetMapping("/info/{categoryId}")
public ServerResponseEntity<Category> info(@PathVariable("categoryId") Long categoryId) {
Category category = categoryService.getById(categoryId);
return ServerResponseEntity.success(category);
}
/**
* Category
* IDID
* CategoryServicesaveCategory
* @PreAuthorize"prod:category:save"访
* 使@SysLog便
*
* @param category ID
* @return ServerResponseEntityVoid
*/
@SysLog("保存分类")
@PostMapping
@PreAuthorize("@pms.hasPermission('prod:category:save')")
public ServerResponseEntity<Void> save(@RequestBody Category category) {
category.setShopId(SecurityUtils.getSysUser().getShopId());
category.setRecTime(new Date());
// 通过LambdaQueryWrapper构建查询条件查询在当前店铺下分类名称与要保存的分类名称相同的分类记录用于判断名称是否已存在。
Category categoryName = categoryService.getOne(new LambdaQueryWrapper<Category>()
.eq(Category::getCategoryName, category.getCategoryName())
.eq(Category::getShopId, category.getShopId()));
if (Objects.nonNull(categoryName)) {
throw new YamiShopBindException("类目名称已存在!");
}
categoryService.saveCategory(category);
return ServerResponseEntity.success();
}
/**
* Category
* IDID
* 线
* CategoryServiceupdateCategory
* @PreAuthorize"prod:category:update"访
* 使@SysLog便
*
* @param category ID
* @return ServerResponseEntity
*/
@SysLog("更新分类")
@PutMapping
@PreAuthorize("@pms.hasPermission('prod:category:update')")
public ServerResponseEntity<String> update(@RequestBody Category category) {
category.setShopId(SecurityUtils.getSysUser().getShopId());
if (Objects.equals(category.getParentId(), category.getCategoryId())) {
return ServerResponseEntity.showFailMsg("分类的上级不能是自己本身");
}
// 通过LambdaQueryWrapper构建查询条件查询在当前店铺下分类名称与要更新的分类名称相同且ID不同排除自身的分类记录用于判断名称是否已存在。
Category categoryName = categoryService.getOne(new LambdaQueryWrapper<Category>()
.eq(Category::getCategoryName, category.getCategoryName())
.eq(Category::getShopId, category.getShopId())
.ne(Category::getCategoryId, category.getCategoryId()));
if (categoryName!= null) {
throw new YamiShopBindException("类目名称已存在!");
}
Category categoryDb = categoryService.getById(category.getCategoryId());
// 如果从下线状态为0改成正常状态为1则需要判断上级的状态确保上级分类存在且为正常状态。
if (Objects.equals(categoryDb.getStatus(), 0) && Objects.equals(category.getStatus(), 1) &&!Objects.equals(category.getParentId(), 0L)) {
Category parentCategory = categoryService.getOne(new LambdaQueryWrapper<Category>()
.eq(Category::getCategoryId, category.getParentId()));
if (Objects.isNull(parentCategory) || Objects.equals(parentCategory.getStatus(), 0)) {
// 修改失败,上级分类不存在或者不为正常状态
throw new YamiShopBindException("修改失败,上级分类不存在或者不为正常状态");
}
}
categoryService.updateCategory(category);
return ServerResponseEntity.success();
}
/**
* ID
* CategoryServicedeleteCategory
* @PreAuthorize"prod:category:delete"访
* 使@SysLog便
*
* @param categoryId
* @return ServerResponseEntity
*/
@SysLog("删除分类")
@DeleteMapping("/{categoryId}")
@PreAuthorize("@pms.hasPermission('prod:category:delete')")
public ServerResponseEntity<String> delete(@PathVariable("categoryId") Long categoryId) {
// 通过LambdaQueryWrapper构建查询条件统计以当前分类ID为上级分类的子分类数量若数量大于0则说明还有子分类不允许删除。
if (categoryService.count(new LambdaQueryWrapper<Category>().eq(Category::getParentId, categoryId)) > 0) {
return ServerResponseEntity.showFailMsg("请删除子分类,再删除该分类");
}
categoryService.deleteCategory(categoryId);
return ServerResponseEntity.success();
}
/**
* LambdaQueryWrapper
* 2ID
*
*
* @return ServerResponseEntity
*/
@GetMapping("/listCategory")
public ServerResponseEntity<List<Category>> listCategory() {
return ServerResponseEntity.success(categoryService.list(new LambdaQueryWrapper<Category>()
.le(Category::getGrade, 2)
.eq(Category::getShopId, SecurityUtils.getSysUser().getShopId())
.orderByAsc(Category::getSeq)));
}
/**
* CategoryServicetreeSelect
* IDservice
*
*
* @return ServerResponseEntity
*/
@GetMapping("/listProdCategory")
public ServerResponseEntity<List<Category>> listProdCategory() {
List<Category> categories = categoryService.treeSelect(SecurityUtils.getSysUser().getShopId(), 2);
return ServerResponseEntity.success(categories);
}
}

@ -8,6 +8,7 @@
*
*/
// 该类所属的包名,用于在项目中对类进行组织和分类管理
package com.yami.shop.admin.controller;
import java.util.List;
@ -18,28 +19,35 @@ import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
// 引入对应的实体类这里是Delivery类用于表示配送相关的业务对象
import com.yami.shop.bean.model.Delivery;
// 引入配送服务层接口,用于调用具体的与配送相关的业务逻辑方法
import com.yami.shop.service.DeliveryService;
/**
*
* DeliveryControllerSpring RESTfulDelivery
*
* @author lgh on 2018/11/26.
*/
@RestController
// 定义该控制器类的基础请求路径,所有该类中的接口请求路径都将以此为前缀
@RequestMapping("/admin/delivery")
public class DeliveryController {
// 通过Spring的依赖注入机制自动注入DeliveryService的实例以便调用其提供的业务方法
@Autowired
private DeliveryService deliveryService;
/**
*
*/
/**
*
* DeliveryServicelist
*
* ServerResponseEntity
* @return ServerResponseEntityList<Delivery>
*/
@GetMapping("/list")
public ServerResponseEntity<List<Delivery>> page(){
List<Delivery> list = deliveryService.list();
return ServerResponseEntity.success(list);
}
}
public ServerResponseEntity<List<Delivery>> page() {
List<Delivery> list = deliveryService.list();
return ServerResponseEntity.success(list);
}
}

@ -27,6 +27,7 @@ import java.util.Objects;
/**
* controller
*
* @author lgh
*
*/
@ -34,32 +35,52 @@ import java.util.Objects;
@RequestMapping("/admin/file")
public class FileController {
@Autowired
private AttachFileService attachFileService;
@Autowired
private Qiniu qiniu;
@Autowired
private ImgUploadUtil imgUploadUtil;
// 自动注入附件文件服务层对象,用于处理文件上传等相关业务逻辑
@Autowired
private AttachFileService attachFileService;
// 自动注入七牛相关配置对象,用于在文件上传到七牛云存储时获取相关配置信息
@Autowired
private Qiniu qiniu;
// 自动注入图片上传工具类对象,用于获取上传类型、路径等相关信息,辅助文件上传操作
@Autowired
private ImgUploadUtil imgUploadUtil;
@PostMapping("/upload/element")
public ServerResponseEntity<String> uploadElementFile(@RequestParam("file") MultipartFile file) throws IOException{
if(file.isEmpty()){
/**
* element
* @param file MultipartFile
* @return ServerResponseEntity<String>
* @throws IOException IO
*/
@PostMapping("/upload/element")
public ServerResponseEntity<String> uploadElementFile(@RequestParam("file") MultipartFile file) throws IOException {
// 判断上传的文件是否为空,如果为空则直接返回成功状态的空响应
if (file.isEmpty()) {
return ServerResponseEntity.success();
}
String fileName = attachFileService.uploadFile(file);
// 调用附件文件服务层的方法上传文件,并获取上传后的文件名
String fileName = attachFileService.uploadFile(file);
return ServerResponseEntity.success(fileName);
}
}
@PostMapping("/upload/tinymceEditor")
public ServerResponseEntity<String> uploadTinymceEditorImages(@RequestParam("editorFile") MultipartFile editorFile) throws IOException{
String fileName = attachFileService.uploadFile(editorFile);
String data = "";
if (Objects.equals(imgUploadUtil.getUploadType(), UploadType.LOCAL.value())) {
data = imgUploadUtil.getUploadPath() + fileName;
} else if (Objects.equals(imgUploadUtil.getUploadType(), UploadType.QINIU.value())) {
data = qiniu.getResourcesUrl() + fileName;
}
/**
* tinymce
* @param editorFile MultipartFile
* @return ServerResponseEntity<String>访
* @throws IOException IO
*/
@PostMapping("/upload/tinymceEditor")
public ServerResponseEntity<String> uploadTinymceEditorImages(@RequestParam("editorFile") MultipartFile editorFile) throws IOException {
// 调用附件文件服务层的方法上传文件,并获取上传后的文件名
String fileName = attachFileService.uploadFile(editorFile);
String data = "";
// 根据图片上传工具类获取的上传类型进行不同的路径拼接处理
if (Objects.equals(imgUploadUtil.getUploadType(), UploadType.LOCAL.value())) {
// 如果是本地存储类型,将本地存储路径和文件名拼接起来作为图片的访问路径
data = imgUploadUtil.getUploadPath() + fileName;
} else if (Objects.equals(imgUploadUtil.getUploadType(), UploadType.QINIU.value())) {
// 如果是七牛云存储类型将七牛云资源的基础访问URL和文件名拼接起来作为图片的访问路径
data = qiniu.getResourcesUrl() + fileName;
}
return ServerResponseEntity.success(data);
}
}
}
}

@ -27,76 +27,95 @@ import java.util.Date;
import java.util.List;
/**
*
*
* @author lgh on 2019/03/27.
*/
@RestController
@RequestMapping("/admin/hotSearch")
public class HotSearchController {
// 自动注入热门搜索服务层接口,通过该接口调用具体的业务逻辑方法
@Autowired
private HotSearchService hotSearchService;
/**
*
*/
/**
*
*
* 'admin:hotSearch:page' 访
*/
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('admin:hotSearch:page')")
public ServerResponseEntity<IPage<HotSearch>> page(HotSearch hotSearch,PageParam<HotSearch> page){
IPage<HotSearch> hotSearchs = hotSearchService.page(page,new LambdaQueryWrapper<HotSearch>()
.eq(HotSearch::getShopId, SecurityUtils.getSysUser().getShopId())
.like(StrUtil.isNotBlank(hotSearch.getContent()), HotSearch::getContent,hotSearch.getContent())
.like(StrUtil.isNotBlank(hotSearch.getTitle()), HotSearch::getTitle,hotSearch.getTitle())
.eq(hotSearch.getStatus()!=null, HotSearch::getStatus,hotSearch.getStatus())
.orderByAsc(HotSearch::getSeq)
);
return ServerResponseEntity.success(hotSearchs);
}
@PreAuthorize("@pms.hasPermission('admin:hotSearch:page')")
public ServerResponseEntity<IPage<HotSearch>> page(HotSearch hotSearch, PageParam<HotSearch> page) {
// 创建一个 LambdaQueryWrapper 用于构建查询条件,结合 MyBatis Plus 进行数据库查询
IPage<HotSearch> hotSearchs = hotSearchService.page(page, new LambdaQueryWrapper<HotSearch>()
// 根据当前登录用户所属店铺的 ID 进行筛选,确保只获取该店铺相关的热门搜索记录
.eq(HotSearch::getShopId, SecurityUtils.getSysUser().getShopId())
// 如果传入的热门搜索内容不为空,则模糊匹配内容字段进行查询
.like(StrUtil.isNotBlank(hotSearch.getContent()), HotSearch::getContent, hotSearch.getContent())
// 如果传入的热门搜索标题不为空,则模糊匹配标题字段进行查询
.like(StrUtil.isNotBlank(hotSearch.getTitle()), HotSearch::getTitle, hotSearch.getTitle())
// 如果传入的状态不为空,则精确匹配状态字段进行查询
.eq(hotSearch.getStatus()!= null, HotSearch::getStatus, hotSearch.getStatus())
// 按照序号字段升序排序结果,方便呈现顺序相关的展示需求
.orderByAsc(HotSearch::getSeq)
);
return ServerResponseEntity.success(hotSearchs);
}
/**
*
*/
@GetMapping("/info/{id}")
public ServerResponseEntity<HotSearch> info(@PathVariable("id") Long id){
HotSearch hotSearch = hotSearchService.getById(id);
return ServerResponseEntity.success(hotSearch);
}
* ID
* ID
*/
@GetMapping("/info/{id}")
public ServerResponseEntity<HotSearch> info(@PathVariable("id") Long id) {
// 通过服务层的 getById 方法,根据传入的 ID 获取对应的热门搜索记录
HotSearch hotSearch = hotSearchService.getById(id);
return ServerResponseEntity.success(hotSearch);
}
/**
*
*/
@PostMapping
@PreAuthorize("@pms.hasPermission('admin:hotSearch:save')")
public ServerResponseEntity<Void> save(@RequestBody @Valid HotSearch hotSearch){
hotSearch.setRecDate(new Date());
hotSearch.setShopId(SecurityUtils.getSysUser().getShopId());
hotSearchService.save(hotSearch);
//清除缓存
hotSearchService.removeHotSearchDtoCacheByShopId(SecurityUtils.getSysUser().getShopId());
return ServerResponseEntity.success();
}
/**
*
* HotSearch ID
* 'admin:hotSearch:save'
*/
@PostMapping
@PreAuthorize("@pms.hasPermission('admin:hotSearch:save')")
public ServerResponseEntity<Void> save(@RequestBody @Valid HotSearch hotSearch) {
// 设置记录的创建日期为当前日期
hotSearch.setRecDate(new Date());
// 设置记录所属的店铺 ID从当前登录用户的信息中获取
hotSearch.setShopId(SecurityUtils.getSysUser().getShopId());
hotSearchService.save(hotSearch);
// 清除对应店铺的热门搜索缓存,保证数据的一致性
hotSearchService.removeHotSearchDtoCacheByShopId(SecurityUtils.getSysUser().getShopId());
return ServerResponseEntity.success();
}
/**
*
*/
@PutMapping
@PreAuthorize("@pms.hasPermission('admin:hotSearch:update')")
public ServerResponseEntity<Void> update(@RequestBody @Valid HotSearch hotSearch){
hotSearchService.updateById(hotSearch);
//清除缓存
hotSearchService.removeHotSearchDtoCacheByShopId(SecurityUtils.getSysUser().getShopId());
return ServerResponseEntity.success();
}
/**
*
* HotSearch
* 'admin:hotSearch:update'
*/
@PutMapping
@PreAuthorize("@pms.hasPermission('admin:hotSearch:update')")
public ServerResponseEntity<Void> update(@RequestBody @Valid HotSearch hotSearch) {
hotSearchService.updateById(hotSearch);
// 清除对应店铺的热门搜索缓存,确保缓存数据与数据库最新数据一致
hotSearchService.removeHotSearchDtoCacheByShopId(SecurityUtils.getSysUser().getShopId());
return ServerResponseEntity.success();
}
/**
*
*/
@DeleteMapping
@PreAuthorize("@pms.hasPermission('admin:hotSearch:delete')")
public ServerResponseEntity<Void> delete(@RequestBody List<Long> ids){
hotSearchService.removeByIds(ids);
//清除缓存
hotSearchService.removeHotSearchDtoCacheByShopId(SecurityUtils.getSysUser().getShopId());
return ServerResponseEntity.success();
}
}
/**
* ID
* ID
* 'admin:hotSearch:delete'
*/
@DeleteMapping
@PreAuthorize("@pms.hasPermission('admin:hotSearch:delete')")
public ServerResponseEntity<Void> delete(@RequestBody List<Long> ids) {
hotSearchService.removeByIds(ids);
// 清除对应店铺的热门搜索缓存,保证数据的准确性
hotSearchService.removeHotSearchDtoCacheByShopId(SecurityUtils.getSysUser().getShopId());
return ServerResponseEntity.success();
}
}

@ -29,42 +29,64 @@ import java.util.Date;
import java.util.Objects;
/**
* IndexImg
*
*
*
* @author lgh on 2018/11/26.
*/
@RestController
@RequestMapping("/admin/indexImg")
public class IndexImgController {
// 注入IndexImgService用于处理与首页图片相关的业务逻辑例如图片信息的查询、保存、更新、删除等操作。
@Autowired
private IndexImgService indexImgService;
// 注入ProductService用于获取商品相关信息在涉及首页图片与商品关联的业务逻辑中会用到比如校验商品状态等情况。
@Autowired
private ProductService productService;
/**
*
* IndexImgPageParam
* IndexImgService
* @PreAuthorize"admin:indexImg:page"访
*
* @param indexImg IndexImg
* @param page
* @return ServerResponseEntity
*/
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('admin:indexImg:page')")
public ServerResponseEntity<IPage<IndexImg>> page(IndexImg indexImg, PageParam<IndexImg> page) {
// 使用IndexImgService的page方法进行分页查询传入分页参数和LambdaQueryWrapper构建的查询条件
// 根据传入的indexImg对象的状态是否不为空来决定是否添加状态筛选条件同时按照顺序排序。
IPage<IndexImg> indexImgPage = indexImgService.page(page,
new LambdaQueryWrapper<IndexImg>()
.eq(indexImg.getStatus() != null, IndexImg::getStatus, indexImg.getStatus())
.orderByAsc(IndexImg::getSeq));
.eq(indexImg.getStatus()!= null, IndexImg::getStatus, indexImg.getStatus())
.orderByAsc(IndexImg::getSeq));
return ServerResponseEntity.success(indexImgPage);
}
/**
*
* IDIDID
* IndexImgService
*
* @PreAuthorize"admin:indexImg:info"访
*
* @param imgId
* @return ServerResponseEntity
*/
@GetMapping("/info/{imgId}")
@PreAuthorize("@pms.hasPermission('admin:indexImg:info')")
public ServerResponseEntity<IndexImg> info(@PathVariable("imgId") Long imgId) {
Long shopId = SecurityUtils.getSysUser().getShopId();
// 通过IndexImgService根据店铺ID和图片ID查询对应的首页图片对象使用LambdaQueryWrapper构建查询条件。
IndexImg indexImg = indexImgService.getOne(new LambdaQueryWrapper<IndexImg>().eq(IndexImg::getShopId, shopId).eq(IndexImg::getImgId, imgId));
if (Objects.nonNull(indexImg.getRelation())) {
// 若图片存在关联商品通过relation属性判断则通过ProductService根据商品ID获取商品对象。
Product product = productService.getProductByProdId(indexImg.getRelation());
// 将商品的图片和商品名称设置到首页图片对象的相应属性中,方便前端展示关联商品的相关信息。
indexImg.setPic(product.getPic());
indexImg.setProdName(product.getProdName());
}
@ -72,7 +94,13 @@ public class IndexImgController {
}
/**
*
* @ValidIndexImg
* IDIDcheckProdStatus
* IndexImgServicesave
* @PreAuthorize"admin:indexImg:save"访
*
* @param indexImg
* @return ServerResponseEntityVoid
*/
@PostMapping
@PreAuthorize("@pms.hasPermission('admin:indexImg:save')")
@ -87,7 +115,13 @@ public class IndexImgController {
}
/**
*
* @ValidIndexImg
* checkProdStatusIndexImgServicesaveOrUpdate
*
* @PreAuthorize"admin:indexImg:update"访
*
* @param indexImg
* @return ServerResponseEntityVoid
*/
@PutMapping
@PreAuthorize("@pms.hasPermission('admin:indexImg:update')")
@ -99,7 +133,12 @@ public class IndexImgController {
}
/**
*
* IDIndexImgServicedeleteIndexImgByIds
* ID
* @PreAuthorize"admin:indexImg:delete"访
*
* @param ids ID
* @return ServerResponseEntityVoid
*/
@DeleteMapping
@PreAuthorize("@pms.hasPermission('admin:indexImg:delete')")
@ -109,6 +148,13 @@ public class IndexImgController {
return ServerResponseEntity.success();
}
/**
* 0
* IDID
* 1
*
* @param indexImg ID
*/
private void checkProdStatus(IndexImg indexImg) {
if (!Objects.equals(indexImg.getType(), 0)) {
return;
@ -124,4 +170,4 @@ public class IndexImgController {
throw new YamiShopBindException("该商品未上架,请选择别的商品");
}
}
}
}

@ -13,7 +13,6 @@ package com.yami.shop.admin.controller;
import java.util.Arrays;
import java.util.List;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import com.yami.shop.common.response.ServerResponseEntity;
@ -27,7 +26,6 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.yami.shop.common.util.PageParam;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yami.shop.bean.enums.MessageStatus;
@ -36,31 +34,44 @@ import com.yami.shop.service.MessageService;
import cn.hutool.core.util.StrUtil;
/**
* MessageControllerSpring RESTfulMessage
*
* @author lgh on 2018/10/15.
*/
@RestController
@RequestMapping("/admin/message")
public class MessageController {
// 通过Spring的依赖注入机制自动注入MessageService的实例以便调用其提供的业务方法来处理消息相关逻辑。
@Autowired
private MessageService messageService;
/**
*
*
* MessagePageParam
* 使MessageServicepageLambdaQueryWrapper
*
* ServerResponseEntity
* @param message
* @param page
* @return ServerResponseEntityIPage<Message>
*/
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('admin:message:page')")
public ServerResponseEntity<IPage<Message>> page(Message message,PageParam<Message> page) {
public ServerResponseEntity<IPage<Message>> page(Message message, PageParam<Message> page) {
IPage<Message> messages = messageService.page(page, new LambdaQueryWrapper<Message>()
.like(StrUtil.isNotBlank(message.getUserName()), Message::getUserName, message.getUserName())
.eq(message.getStatus() != null, Message::getStatus, message.getStatus()));
.like(StrUtil.isNotBlank(message.getUserName()), Message::getUserName, message.getUserName())
.eq(message.getStatus()!= null, Message::getStatus, message.getStatus()));
return ServerResponseEntity.success(messages);
}
/**
*
* ID
* IDMessageServicegetById
* ServerResponseEntity
* @param id ID
* @return ServerResponseEntityMessage
*/
@GetMapping("/info/{id}")
@PreAuthorize("@pms.hasPermission('admin:message:info')")
@ -70,7 +81,11 @@ public class MessageController {
}
/**
*
*
* MessageMessageServicesave
* ServerResponseEntityVoid
* @param message
* @return ServerResponseEntityVoid
*/
@PostMapping
@PreAuthorize("@pms.hasPermission('admin:message:save')")
@ -80,7 +95,11 @@ public class MessageController {
}
/**
*
*
* MessageMessageServiceupdateByIdID
* ServerResponseEntityVoid
* @param message
* @return ServerResponseEntityVoid
*/
@PutMapping
@PreAuthorize("@pms.hasPermission('admin:message:update')")
@ -90,7 +109,11 @@ public class MessageController {
}
/**
*
*
* IDMessageIDIDMessageStatus.RELEASE.value()
* MessageServiceupdateByIdServerResponseEntityVoid
* @param id ID
* @return ServerResponseEntityVoid
*/
@PutMapping("/release/{id}")
@PreAuthorize("@pms.hasPermission('admin:message:release')")
@ -103,7 +126,11 @@ public class MessageController {
}
/**
*
*
* IDMessageIDIDMessageStatus.CANCEL.value()
* MessageServiceupdateByIdServerResponseEntityVoid
* @param id ID
* @return ServerResponseEntityVoid
*/
@PutMapping("/cancel/{id}")
@PreAuthorize("@pms.hasPermission('admin:message:cancel')")
@ -116,7 +143,11 @@ public class MessageController {
}
/**
*
*
* IDListMessageServiceremoveByIds
* IDServerResponseEntityVoid
* @param ids ID
* @return ServerResponseEntityVoid
*/
@DeleteMapping("/{ids}")
@PreAuthorize("@pms.hasPermission('admin:message:delete')")
@ -124,4 +155,4 @@ public class MessageController {
messageService.removeByIds(Arrays.asList(ids));
return ServerResponseEntity.success();
}
}
}

@ -27,7 +27,7 @@ import java.util.Date;
/**
*
*
*
* @author hzm
* @date
*/
@ -36,30 +36,31 @@ import java.util.Date;
@RequestMapping("/shop/notice")
public class NoticeController {
// 通过构造函数注入NoticeService用于调用业务层方法来处理公告相关的业务逻辑
private final NoticeService noticeService;
/**
*
*
* @param page
* @param notice
* @return
*
*
* @param page
* @param notice
* @return ServerResponseEntity<IPage<Notice>>
*/
@GetMapping("/page")
public ServerResponseEntity<IPage<Notice>> getNoticePage(PageParam<Notice> page, Notice notice) {
// 使用MyBatis Plus的LambdaQueryWrapper构建查询条件根据传入的公告对象中的非空字段进行筛选
// 例如根据状态、是否置顶、标题等进行筛选,并按照更新时间降序排列
IPage<Notice> noticePage = noticeService.page(page, new LambdaQueryWrapper<Notice>()
.eq(notice.getStatus() != null, Notice::getStatus, notice.getStatus())
.eq(notice.getIsTop()!=null,Notice::getIsTop,notice.getIsTop())
.like(notice.getTitle() != null, Notice::getTitle, notice.getTitle()).orderByDesc(Notice::getUpdateTime));
.eq(notice.getStatus()!= null, Notice::getStatus, notice.getStatus())
.eq(notice.getIsTop()!= null, Notice::getIsTop, notice.getIsTop())
.like(notice.getTitle()!= null, Notice::getTitle, notice.getTitle()).orderByDesc(Notice::getUpdateTime));
return ServerResponseEntity.success(noticePage);
}
/**
* id
*
* @param id id
* @return
* id
* @param id id
* @return ServerResponseEntity<Notice>id
*/
@GetMapping("/info/{id}")
public ServerResponseEntity<Notice> getById(@PathVariable("id") Long id) {
@ -67,57 +68,65 @@ public class NoticeController {
}
/**
*
*
* @param notice
* @return
*
*
* @param notice
* @return ServerResponseEntity<Boolean>truefalse
*/
@SysLog("新增公告管理")
@PostMapping
@PreAuthorize("@pms.hasPermission('shop:notice:save')")
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'shop:notice:save'权限的用户才能访问该接口
public ServerResponseEntity<Boolean> save(@RequestBody @Valid Notice notice) {
// 设置公告所属店铺的id从当前登录用户信息中获取
notice.setShopId(SecurityUtils.getSysUser().getShopId());
if (notice.getStatus() == 1) {
// 如果公告状态为已发布1表示已发布则设置发布时间为当前时间
notice.setPublishTime(new Date());
}
notice.setUpdateTime(new Date());
// 调用业务层方法先移除相关的公告列表具体逻辑由业务层的removeNoticeList方法决定
noticeService.removeNoticeList();
return ServerResponseEntity.success(noticeService.save(notice));
}
/**
*
*
* @param notice
* @return
*
*
* @param notice
* @return ServerResponseEntity<Boolean>truefalse
*/
@SysLog("修改公告管理")
@PutMapping
@PreAuthorize("@pms.hasPermission('shop:notice:update')")
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'shop:notice:update'权限的用户才能访问该接口
public ServerResponseEntity<Boolean> updateById(@RequestBody @Valid Notice notice) {
Notice oldNotice = noticeService.getById(notice.getId());
if (oldNotice.getStatus() == 0 && notice.getStatus() == 1) {
// 如果原公告状态为未发布0表示未发布修改后变为已发布1表示已发布则设置发布时间为当前时间
notice.setPublishTime(new Date());
}
notice.setUpdateTime(new Date());
// 调用业务层方法先移除相关的公告列表具体逻辑由业务层的removeNoticeList方法决定
noticeService.removeNoticeList();
// 移除要修改的这条公告具体逻辑由业务层的removeNoticeById方法决定可能是缓存清除等相关操作
noticeService.removeNoticeById(notice.getId());
return ServerResponseEntity.success(noticeService.updateById(notice));
}
/**
* id
*
* @param id id
* @return
* id
* id
* @param id id
* @return ServerResponseEntity<Boolean>truefalse
*/
@SysLog("删除公告管理")
@DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('shop:notice:delete')")
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'shop:notice:delete'权限的用户才能访问该接口
public ServerResponseEntity<Boolean> removeById(@PathVariable Long id) {
noticeService.removeNoticeList();
noticeService.removeNoticeById(id);
return ServerResponseEntity.success(noticeService.removeById(id));
}
}
}

@ -44,6 +44,7 @@ import java.util.Date;
import java.util.List;
/**
* Excel
* @author lgh on 2018/09/15.
*/
@Slf4j
@ -51,104 +52,143 @@ import java.util.List;
@RequestMapping("/order/order")
public class OrderController {
// 自动注入订单服务层接口,通过该接口调用具体的业务逻辑方法,处理订单相关业务
@Autowired
private OrderService orderService;
// 自动注入订单项服务层接口,用于处理与订单中具体商品项相关的业务逻辑
@Autowired
private OrderItemService orderItemService;
// 自动注入用户地址订单服务层接口,用于获取订单相关的用户地址信息等业务逻辑
@Autowired
private UserAddrOrderService userAddrOrderService;
// 自动注入商品服务层接口,可能用于商品相关的业务操作,比如缓存清理等
@Autowired
private ProductService productService;
// 自动注入库存单元SKU服务层接口同样可能涉及到缓存清理等与库存单元相关的业务操作
@Autowired
private SkuService skuService;
/**
*
*
* OrderParamPageParam
* 'order:order:page' 访
*/
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('order:order:page')")
public ServerResponseEntity<IPage<Order>> page(OrderParam orderParam,PageParam<Order> page) {
public ServerResponseEntity<IPage<Order>> page(OrderParam orderParam, PageParam<Order> page) {
// 获取当前登录用户所属店铺的 ID用于后续筛选该店铺下的订单数据
Long shopId = SecurityUtils.getSysUser().getShopId();
// 将店铺 ID 设置到订单查询参数中,确保查询的是当前店铺的订单
orderParam.setShopId(shopId);
// 调用订单服务层的方法,按照订单参数和分页参数获取详细的订单分页信息
IPage<Order> orderPage = orderService.pageOrdersDetailByOrderParam(page, orderParam);
return ServerResponseEntity.success(orderPage);
}
/**
*
*
*
* 'order:order:info'
*/
@GetMapping("/orderInfo/{orderNumber}")
@PreAuthorize("@pms.hasPermission('order:order:info')")
public ServerResponseEntity<Order> info(@PathVariable("orderNumber") String orderNumber) {
// 获取当前登录用户所属店铺的 ID用于权限校验
Long shopId = SecurityUtils.getSysUser().getShopId();
// 通过订单服务层的方法,根据订单编号获取订单基本信息
Order order = orderService.getOrderByOrderNumber(orderNumber);
// 校验当前用户所属店铺 ID 是否与订单所属店铺 ID 一致,不一致则抛出无权限异常
if (!Objects.equal(shopId, order.getShopId())) {
throw new YamiShopBindException("您没有权限获取该订单信息");
}
// 通过订单项服务层的方法,根据订单编号获取该订单下的所有订单项信息
List<OrderItem> orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber);
// 将订单项信息设置到订单对象中,完善订单的详细信息
order.setOrderItems(orderItems);
// 通过用户地址订单服务层的方法,根据订单关联的地址 ID 获取用户地址信息
UserAddrOrder userAddrOrder = userAddrOrderService.getById(order.getAddrOrderId());
// 将用户地址信息设置到订单对象中
order.setUserAddrOrder(userAddrOrder);
return ServerResponseEntity.success(order);
}
/**
*
*
* DeliveryOrderParam
* SKU 'order:order:delivery'
*/
@PutMapping("/delivery")
@PreAuthorize("@pms.hasPermission('order:order:delivery')")
public ServerResponseEntity<Void> delivery(@RequestBody DeliveryOrderParam deliveryOrderParam) {
// 获取当前登录用户所属店铺的 ID用于权限校验
Long shopId = SecurityUtils.getSysUser().getShopId();
// 通过订单服务层的方法,根据订单编号获取订单基本信息
Order order = orderService.getOrderByOrderNumber(deliveryOrderParam.getOrderNumber());
// 校验当前用户所属店铺 ID 是否与订单所属店铺 ID 一致,不一致则抛出无权限异常
if (!Objects.equal(shopId, order.getShopId())) {
throw new YamiShopBindException("您没有权限修改该订单信息");
}
// 创建一个新的 Order 对象,用于设置要更新的订单信息
Order orderParam = new Order();
// 设置订单 ID确保更新的是正确的订单记录
orderParam.setOrderId(order.getOrderId());
// 设置快递 ID来自发货参数
orderParam.setDvyId(deliveryOrderParam.getDvyId());
// 设置快递单号,来自发货参数
orderParam.setDvyFlowId(deliveryOrderParam.getDvyFlowId());
// 设置发货时间为当前时间
orderParam.setDvyTime(new Date());
// 设置订单状态为已发货(通过 OrderStatus 枚举获取对应状态值)
orderParam.setStatus(OrderStatus.CONSIGNMENT.value());
// 设置用户 ID与原订单一致
orderParam.setUserId(order.getUserId());
// 调用订单服务层的方法,更新订单的发货相关信息
orderService.delivery(orderParam);
// 通过订单项服务层的方法,根据订单编号获取该订单下的所有订单项信息
List<OrderItem> orderItems = orderItemService.getOrderItemsByOrderNumber(deliveryOrderParam.getOrderNumber());
// 遍历订单项清除对应商品和库存单元SKU的缓存
for (OrderItem orderItem : orderItems) {
productService.removeProductCacheByProdId(orderItem.getProdId());
skuService.removeSkuCacheBySkuId(orderItem.getSkuId(),orderItem.getProdId());
skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId());
}
return ServerResponseEntity.success();
}
/**
*
* Excel
*
* 使 Hutool Excel Excel
* 'order:order:waitingConsignmentExcel' 访
*
* @param order
* @param consignmentName
* @param consignmentMobile
* @param consignmentAddr
* @param order ID
* @param startTime @DateTimeFormat
* @param endTime @DateTimeFormat
* @param consignmentName Excel
* @param consignmentMobile Excel
* @param consignmentAddr Excel
* @param response HttpServletResponse Excel
*/
@GetMapping("/waitingConsignmentExcel")
@PreAuthorize("@pms.hasPermission('order:order:waitingConsignmentExcel')")
public void waitingConsignmentExcel(Order order, @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date startTime,
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date endTime, String consignmentName, String consignmentMobile,
String consignmentAddr, HttpServletResponse response) {
// 获取当前登录用户所属店铺的 ID设置到订单筛选条件对象中确保查询的是当前店铺的待发货订单
Long shopId = SecurityUtils.getSysUser().getShopId();
order.setShopId(shopId);
// 设置订单状态为已支付(待发货状态对应的前置状态,这里假设已支付的订单待发货),用于筛选待发货订单
order.setStatus(OrderStatus.PADYED.value());
// 调用订单服务层的方法,根据订单筛选条件(包括时间范围、订单状态等)查询符合条件的待发货订单列表
List<Order> orders = orderService.listOrdersDetailByOrder(order, startTime, endTime);
//通过工具类创建writer
// 通过 Hutool 的 ExcelUtil 工具类创建一个 ExcelWriter 对象,用于后续写入 Excel 数据
ExcelWriter writer = ExcelUtil.getBigWriter();
// 获取 Excel 表格的 Sheet 对象,用于设置列宽等表格样式相关操作
Sheet sheet = writer.getSheet();
// 设置各列的宽度,单位是字符宽度的 1/256这里根据实际需求设置了不同列的宽度
sheet.setColumnWidth(0, 20 * 256);
sheet.setColumnWidth(1, 20 * 256);
sheet.setColumnWidth(2, 20 * 256);
@ -157,19 +197,27 @@ public class OrderController {
sheet.setColumnWidth(7, 60 * 256);
sheet.setColumnWidth(8, 60 * 256);
sheet.setColumnWidth(9, 60 * 256);
// 待发货
// 设置 Excel 表格的表头信息,定义了要展示的各列字段名称
String[] hearder = {"订单编号", "收件人", "手机", "收货地址", "商品名称", "数量", "发件人姓名", "发件人手机号", "发货地址", "备注"};
// 合并表头单元格,用于展示标题等整体说明信息,这里合并了除第一列外的所有表头列
writer.merge(hearder.length - 1, "发货信息整理");
// 将表头行数据写入 Excel 表格
writer.writeRow(Arrays.asList(hearder));
int row = 1;
// 遍历查询到的每个订单
for (Order dbOrder : orders) {
// 获取订单对应的用户地址信息
UserAddrOrder addr = dbOrder.getUserAddrOrder();
// 拼接完整的收货地址信息
String addrInfo = addr.getProvince() + addr.getCity() + addr.getArea() + addr.getAddr();
// 获取该订单下的所有订单项信息
List<OrderItem> orderItems = dbOrder.getOrderItems();
row++;
// 遍历每个订单项,将订单和订单项相关信息逐行写入 Excel 表格
for (OrderItem orderItem : orderItems) {
// 第0列开始
// 0 列开始写入数据,列索引递增
int col = 0;
writer.writeCellValue(col++, row, dbOrder.getOrderNumber());
writer.writeCellValue(col++, row, addr.getReceiver());
@ -183,105 +231,51 @@ public class OrderController {
writer.writeCellValue(col++, row, dbOrder.getRemarks());
}
}
// 将整理好的 Excel 数据通过响应流输出给客户端,实现下载功能
writeExcel(response, writer);
}
/**
*
* Excel
*
* 使 Hutool Excel Excel
* 'order:order:soldExcel' 访
*
* @param order
* @param order ID
* @param startTime @DateTimeFormat
* @param endTime @DateTimeFormat
* @param response HttpServletResponse Excel
*/
@GetMapping("/soldExcel")
@PreAuthorize("@pms.hasPermission('order:order:soldExcel')")
public void soldExcel(Order order, @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date startTime,
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date endTime, HttpServletResponse response) {
// 获取当前登录用户所属店铺的 ID设置到订单筛选条件对象中确保查询的是当前店铺的已销售订单
Long shopId = SecurityUtils.getSysUser().getShopId();
order.setShopId(shopId);
// 设置订单为已支付状态,用于筛选已销售的订单(这里简单以是否已支付作为已销售的判断条件之一)
order.setIsPayed(1);
// 调用订单服务层的方法,根据订单筛选条件(包括时间范围、是否已支付等)查询符合条件的已销售订单列表
List<Order> orders = orderService.listOrdersDetailByOrder(order, startTime, endTime);
//通过工具类创建writer
// 通过 Hutool 的 ExcelUtil 工具类创建一个 ExcelWriter 对象,用于后续写入 Excel 数据
ExcelWriter writer = ExcelUtil.getBigWriter();
// 待发货
String[] hearder = {"订单编号", "下单时间", "收件人", "手机", "收货地址", "商品名称", "数量", "订单应付", "订单运费", "订单实付"};
// 获取 Excel 表格的 Sheet 对象,用于设置列宽等表格样式相关操作
Sheet sheet = writer.getSheet();
// 设置各列的宽度,单位是字符宽度的 1/256这里根据实际需求设置了不同列的宽度
sheet.setColumnWidth(0, 20 * 256);
sheet.setColumnWidth(1, 20 * 256);
sheet.setColumnWidth(3, 20 * 256);
sheet.setColumnWidth(4, 60 * 256);
sheet.setColumnWidth(5, 60 * 256);
// 设置 Excel 表格的表头信息,定义了要展示的各列字段名称
String[] hearder = {"订单编号", "下单时间", "收件人", "手机", "收货地址", "商品名称", "数量", "订单应付", "订单运费", "订单实付"};
// 合并表头单元格,用于展示标题等整体说明信息,这里合并了除第一列外的所有表头列
writer.merge(hearder.length - 1, "销售信息整理");
// 将表头行数据写入 Excel 表格
writer.writeRow(Arrays.asList(hearder));
int row = 1;
for (Order dbOrder : orders) {
UserAddrOrder addr = dbOrder.getUserAddrOrder();
String addrInfo = addr.getProvince() + addr.getCity() + addr.getArea() + addr.getAddr();
List<OrderItem> orderItems = dbOrder.getOrderItems();
int firstRow = row + 1;
int lastRow = row + orderItems.size();
int col = -1;
// 订单编号
mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getOrderNumber());
// 下单时间
mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getCreateTime());
// 收件人
mergeIfNeed(writer, firstRow, lastRow, ++col, col, addr.getReceiver());
// "手机"
mergeIfNeed(writer, firstRow, lastRow, ++col, col, addr.getMobile());
// "收货地址"
mergeIfNeed(writer, firstRow, lastRow, ++col, col, addrInfo);
int prodNameCol = ++col;
int prodCountCol = ++col;
for (OrderItem orderItem : orderItems) {
row++;
// 商品名称
writer.writeCellValue(prodNameCol, row, orderItem.getProdName());
// 数量
writer.writeCellValue(prodCountCol, row, orderItem.getProdCount());
}
// 订单应付
mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getTotal());
// 订单运费
mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getFreightAmount());
// 订单实付
mergeIfNeed(writer, firstRow, lastRow, ++col, col, dbOrder.getActualTotal());
}
writeExcel(response, writer);
}
/**
*
*/
private void mergeIfNeed(ExcelWriter writer, int firstRow, int lastRow, int firstColumn, int lastColumn, Object content) {
if (content instanceof Date) {
content = DateUtil.format((Date) content, DatePattern.NORM_DATETIME_PATTERN);
}
if (lastRow - firstRow > 0 || lastColumn - firstColumn > 0) {
writer.merge(firstRow, lastRow, firstColumn, lastColumn, content, false);
} else {
writer.writeCellValue(firstColumn, firstRow, content);
}
}
private void writeExcel(HttpServletResponse response, ExcelWriter writer) {
//response为HttpServletResponse对象
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
//test.xls是弹出下载对话框的文件名不能为中文中文请自行编码
response.setHeader("Content-Disposition", "attachment;filename=1.xls");
ServletOutputStream servletOutputStream = null;
try {
servletOutputStream = response.getOutputStream();
writer.flush(servletOutputStream);
servletOutputStream.flush();
} catch (IORuntimeException | IOException e) {
log.error("写出Excel错误", e);
} finally {
IoUtil.close(writer);
}
}
}
// 遍历查询到的每个订单
for (Order db

@ -28,9 +28,10 @@ import jakarta.validation.Valid;
import java.util.Arrays;
import java.util.Objects;
/**
* PickAddr
*
*
*
* @author lgh on 2018/10/17.
*/
@ -38,64 +39,97 @@ import java.util.Objects;
@RequestMapping("/shop/pickAddr")
public class PickAddrController {
// 注入PickAddrService用于与提货地址相关的业务逻辑处理例如查询、保存、更新、删除提货地址等操作。
@Autowired
private PickAddrService pickAddrService;
/**
*
*/
/**
* PickAddrPageParam
* PickAddrServiceID
*
* @PreAuthorize"shop:pickAddr:page"访
*
* @param pickAddr PickAddr
* @param page
* @return ServerResponseEntity
*/
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('shop:pickAddr:page')")
public ServerResponseEntity<IPage<PickAddr>> page(PickAddr pickAddr,PageParam<PickAddr> page){
IPage<PickAddr> pickAddrs = pickAddrService.page(page,new LambdaQueryWrapper<PickAddr>()
.like(StrUtil.isNotBlank(pickAddr.getAddrName()),PickAddr::getAddrName,pickAddr.getAddrName())
.orderByDesc(PickAddr::getAddrId));
return ServerResponseEntity.success(pickAddrs);
}
@PreAuthorize("@pms.hasPermission('shop:pickAddr:page')")
public ServerResponseEntity<IPage<PickAddr>> page(PickAddr pickAddr, PageParam<PickAddr> page) {
// 使用PickAddrService的page方法进行分页查询传入分页参数和LambdaQueryWrapper构建的查询条件。
// 通过StrUtil.isNotBlank判断提货地址名称是否不为空若不为空则添加模糊查询条件按照地址名称进行模糊匹配最后按照地址ID倒序排序。
IPage<PickAddr> pickAddrs = pickAddrService.page(page, new LambdaQueryWrapper<PickAddr>()
.like(StrUtil.isNotBlank(pickAddr.getAddrName()), PickAddr::getAddrName, pickAddr.getAddrName())
.orderByDesc(PickAddr::getAddrId));
return ServerResponseEntity.success(pickAddrs);
}
/**
*
*/
@GetMapping("/info/{id}")
@PreAuthorize("@pms.hasPermission('shop:pickAddr:info')")
public ServerResponseEntity<PickAddr> info(@PathVariable("id") Long id){
PickAddr pickAddr = pickAddrService.getById(id);
return ServerResponseEntity.success(pickAddr);
}
* IDIDPickAddrServicegetById
*
* @PreAuthorize"shop:pickAddr:info"访
*
* @param id
* @return ServerResponseEntity
*/
@GetMapping("/info/{id}")
@PreAuthorize("@pms.hasPermission('shop:pickAddr:info')")
public ServerResponseEntity<PickAddr> info(@PathVariable("id") Long id) {
PickAddr pickAddr = pickAddrService.getById(id);
return ServerResponseEntity.success(pickAddr);
}
/**
*
*/
@PostMapping
@PreAuthorize("@pms.hasPermission('shop:pickAddr:save')")
public ServerResponseEntity<Void> save(@Valid @RequestBody PickAddr pickAddr){
pickAddr.setShopId(SecurityUtils.getSysUser().getShopId());
pickAddrService.save(pickAddr);
return ServerResponseEntity.success();
}
/**
* @ValidPickAddr
* IDIDPickAddrServicesave
*
* @PreAuthorize"shop:pickAddr:save"访
*
* @param pickAddr
* @return ServerResponseEntityVoid
*/
@PostMapping
@PreAuthorize("@pms.hasPermission('shop:pickAddr:save')")
public ServerResponseEntity<Void> save(@Valid @RequestBody PickAddr pickAddr) {
pickAddr.setShopId(SecurityUtils.getSysUser().getShopId());
pickAddrService.save(pickAddr);
return ServerResponseEntity.success();
}
/**
*
*/
@PutMapping
@PreAuthorize("@pms.hasPermission('shop:pickAddr:update')")
public ServerResponseEntity<Void> update(@Valid @RequestBody PickAddr pickAddr){
PickAddr dbPickAddr = pickAddrService.getById(pickAddr.getAddrId());
/**
* @ValidPickAddr
* PickAddrServicegetByIdID
* IDID
* PickAddrServiceupdateById
* @PreAuthorize"shop:pickAddr:update"访
*
* @param pickAddr
* @return ServerResponseEntityVoid
*/
@PutMapping
@PreAuthorize("@pms.hasPermission('shop:pickAddr:update')")
public ServerResponseEntity<Void> update(@Valid @RequestBody PickAddr pickAddr) {
PickAddr dbPickAddr = pickAddrService.getById(pickAddr.getAddrId());
if (!Objects.equals(dbPickAddr.getShopId(),SecurityUtils.getSysUser().getShopId())) {
throw new YamiShopBindException(ResponseEnum.UNAUTHORIZED);
}
pickAddrService.updateById(pickAddr);
return ServerResponseEntity.success();
}
if (!Objects.equals(dbPickAddr.getShopId(), SecurityUtils.getSysUser().getShopId())) {
throw new YamiShopBindException(ResponseEnum.UNAUTHORIZED);
}
pickAddrService.updateById(pickAddr);
return ServerResponseEntity.success();
}
/**
*
*/
@DeleteMapping
@PreAuthorize("@pms.hasPermission('shop:pickAddr:delete')")
public ServerResponseEntity<Void> delete(@RequestBody Long[] ids){
pickAddrService.removeByIds(Arrays.asList(ids));
return ServerResponseEntity.success();
}
}
/**
* IDPickAddrServiceremoveByIds
* ID
* @PreAuthorize"shop:pickAddr:delete"访
*
* @param ids ID
* @return ServerResponseEntityVoid
*/
@DeleteMapping
@PreAuthorize("@pms.hasPermission('shop:pickAddr:delete')")
public ServerResponseEntity<Void> delete(@RequestBody Long[] ids) {
pickAddrService.removeByIds(Arrays.asList(ids));
return ServerResponseEntity.success();
}
}

@ -10,100 +10,126 @@
package com.yami.shop.admin.controller;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yami.shop.bean.model.ProdComm;
import com.yami.shop.common.annotation.SysLog;
import com.yami.shop.common.util.Json;
import com.yami.shop.service.ProdCommService;
import lombok.AllArgsConstructor;
import jakarta.validation.Valid;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yami.shop.bean.model.PickAddr;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.response.ResponseEnum;
import com.yami.shop.common.response.ServerResponseEntity;
import com.yami.shop.common.util.PageParam;
import com.yami.shop.security.admin.util.SecurityUtils;
import com.yami.shop.service.PickAddrService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.yami.shop.common.util.PageParam;
import com.baomidou.mybatisplus.core.metadata.IPage;
import java.util.Date;
import jakarta.validation.Valid;
import java.util.Arrays;
import java.util.Objects;
/**
*
* PickAddr
*
*
*
* @author xwc
* @date 2019-04-19 10:43:57
* @author lgh on 2018/10/17.
*/
@RestController
@AllArgsConstructor
@RequestMapping("/prod/prodComm" )
public class ProdCommController {
@RequestMapping("/shop/pickAddr")
public class PickAddrController {
private final ProdCommService prodCommService;
// 注入PickAddrService用于与提货地址相关的业务逻辑处理例如查询、保存、更新、删除提货地址等操作。
@Autowired
private PickAddrService pickAddrService;
/**
*
* @param page
* @param prodComm
* @return
* PickAddrPageParam
* PickAddrServiceID
*
* @PreAuthorize"shop:pickAddr:page"访
*
* @param pickAddr PickAddr
* @param page
* @return ServerResponseEntity
*/
@GetMapping("/page" )
@PreAuthorize("@pms.hasPermission('prod:prodComm:page')" )
public ServerResponseEntity<IPage<ProdComm>> getProdCommPage(PageParam page, ProdComm prodComm) {
return ServerResponseEntity.success(prodCommService.getProdCommPage(page,prodComm));
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('shop:pickAddr:page')")
public ServerResponseEntity<IPage<PickAddr>> page(PickAddr pickAddr, PageParam<PickAddr> page) {
// 使用PickAddrService的page方法进行分页查询传入分页参数和LambdaQueryWrapper构建的查询条件。
// 通过StrUtil.isNotBlank判断提货地址名称是否不为空若不为空则添加模糊查询条件按照地址名称进行模糊匹配最后按照地址ID倒序排序。
IPage<PickAddr> pickAddrs = pickAddrService.page(page, new LambdaQueryWrapper<PickAddr>()
.like(StrUtil.isNotBlank(pickAddr.getAddrName()), PickAddr::getAddrName, pickAddr.getAddrName())
.orderByDesc(PickAddr::getAddrId));
return ServerResponseEntity.success(pickAddrs);
}
/**
* id
* @param prodCommId id
* @return
* IDIDPickAddrServicegetById
*
* @PreAuthorize"shop:pickAddr:info"访
*
* @param id
* @return ServerResponseEntity
*/
@GetMapping("/info/{prodCommId}" )
@PreAuthorize("@pms.hasPermission('prod:prodComm:info')" )
public ServerResponseEntity<ProdComm> getById(@PathVariable("prodCommId" ) Long prodCommId) {
return ServerResponseEntity.success(prodCommService.getById(prodCommId));
@GetMapping("/info/{id}")
@PreAuthorize("@pms.hasPermission('shop:pickAddr:info')")
public ServerResponseEntity<PickAddr> info(@PathVariable("id") Long id) {
PickAddr pickAddr = pickAddrService.getById(id);
return ServerResponseEntity.success(pickAddr);
}
/**
*
* @param prodComm
* @return
* @ValidPickAddr
* IDIDPickAddrServicesave
*
* @PreAuthorize"shop:pickAddr:save"访
*
* @param pickAddr
* @return ServerResponseEntityVoid
*/
@SysLog("新增商品评论" )
@PostMapping
@PreAuthorize("@pms.hasPermission('prod:prodComm:save')" )
public ServerResponseEntity<Boolean> save(@RequestBody @Valid ProdComm prodComm) {
return ServerResponseEntity.success(prodCommService.save(prodComm));
@PreAuthorize("@pms.hasPermission('shop:pickAddr:save')")
public ServerResponseEntity<Void> save(@Valid @RequestBody PickAddr pickAddr) {
pickAddr.setShopId(SecurityUtils.getSysUser().getShopId());
pickAddrService.save(pickAddr);
return ServerResponseEntity.success();
}
/**
*
* @param prodComm
* @return
* @ValidPickAddr
* PickAddrServicegetByIdID
* IDID
* PickAddrServiceupdateById
* @PreAuthorize"shop:pickAddr:update"访
*
* @param pickAddr
* @return ServerResponseEntityVoid
*/
@SysLog("修改商品评论" )
@PutMapping
@PreAuthorize("@pms.hasPermission('prod:prodComm:update')" )
public ServerResponseEntity<Boolean> updateById(@RequestBody @Valid ProdComm prodComm) {
prodComm.setReplyTime(new Date());
return ServerResponseEntity.success(prodCommService.updateById(prodComm));
@PreAuthorize("@pms.hasPermission('shop:pickAddr:update')")
public ServerResponseEntity<Void> update(@Valid @RequestBody PickAddr pickAddr) {
PickAddr dbPickAddr = pickAddrService.getById(pickAddr.getAddrId());
if (!Objects.equals(dbPickAddr.getShopId(), SecurityUtils.getSysUser().getShopId())) {
throw new YamiShopBindException(ResponseEnum.UNAUTHORIZED);
}
pickAddrService.updateById(pickAddr);
return ServerResponseEntity.success();
}
/**
* id
* @param prodCommId id
* @return
* IDPickAddrServiceremoveByIds
* ID
* @PreAuthorize"shop:pickAddr:delete"访
*
* @param ids ID
* @return ServerResponseEntityVoid
*/
@SysLog("删除商品评论" )
@DeleteMapping("/{prodCommId}" )
@PreAuthorize("@pms.hasPermission('prod:prodComm:delete')" )
public ServerResponseEntity<Boolean> removeById(@PathVariable Long prodCommId) {
return ServerResponseEntity.success(prodCommService.removeById(prodCommId));
@DeleteMapping
@PreAuthorize("@pms.hasPermission('shop:pickAddr:delete')")
public ServerResponseEntity<Void> delete(@RequestBody Long[] ids) {
pickAddrService.removeByIds(Arrays.asList(ids));
return ServerResponseEntity.success();
}
}
}

@ -10,7 +10,6 @@
package com.yami.shop.admin.controller;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
@ -31,7 +30,7 @@ import java.util.List;
/**
*
*
*
* @author hzm
* @date 2019-04-18 09:08:36
*/
@ -39,33 +38,34 @@ import java.util.List;
@RequestMapping("/prod/prodTag")
public class ProdTagController {
// 通过Spring的自动注入机制注入ProdTagService对象用于调用业务层方法来处理商品分组相关的业务逻辑
@Autowired
private ProdTagService prodTagService;
/**
*
*
* @param page
* @param prodTag
* @return
*
*
* @param page
* @param prodTag
* @return ServerResponseEntity<IPage<ProdTag>>
*/
@GetMapping("/page")
public ServerResponseEntity<IPage<ProdTag>> getProdTagPage(PageParam<ProdTag> page, ProdTag prodTag) {
// 使用MyBatis Plus的LambdaQueryWrapper构建查询条件根据传入的商品分组标签对象中的非空字段进行筛选
// 例如根据状态、标题等进行筛选并按照序号seq和创建时间降序排列
IPage<ProdTag> tagPage = prodTagService.page(
page, new LambdaQueryWrapper<ProdTag>()
.eq(prodTag.getStatus() != null, ProdTag::getStatus, prodTag.getStatus())
.like(prodTag.getTitle() != null, ProdTag::getTitle, prodTag.getTitle())
.orderByDesc(ProdTag::getSeq, ProdTag::getCreateTime));
.eq(prodTag.getStatus()!= null, ProdTag::getStatus, prodTag.getStatus())
.like(prodTag.getTitle()!= null, ProdTag::getTitle, prodTag.getTitle())
.orderByDesc(ProdTag::getSeq, ProdTag::getCreateTime));
return ServerResponseEntity.success(tagPage);
}
/**
* id
*
* @param id id
* @return
* id
* @param id id
* @return ServerResponseEntity<ProdTag>id
*/
@GetMapping("/info/{id}")
public ServerResponseEntity<ProdTag> getById(@PathVariable("id") Long id) {
@ -73,67 +73,85 @@ public class ProdTagController {
}
/**
*
*
* @param prodTag
* @return
*
*
* @param prodTag
* @return ServerResponseEntity<Boolean>truefalse
*/
@SysLog("新增商品分组标签")
@PostMapping
@PreAuthorize("@pms.hasPermission('prod:prodTag:save')")
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'prod:prodTag:save'权限的用户才能访问该接口
public ServerResponseEntity<Boolean> save(@RequestBody @Valid ProdTag prodTag) {
// 查看是否相同的标签
// 查看是否已存在相同名称的标签,通过标题进行模糊查询
List<ProdTag> list = prodTagService.list(new LambdaQueryWrapper<ProdTag>().like(ProdTag::getTitle, prodTag.getTitle()));
if (CollectionUtil.isNotEmpty(list)) {
// 如果查询到相同名称的标签列表不为空,则抛出异常,提示标签名称已存在,不能添加相同的标签
throw new YamiShopBindException("标签名称已存在,不能添加相同的标签");
}
// 设置默认属性将是否为默认标签设置为0表示非默认
prodTag.setIsDefault(0);
// 设置商品数量初始值为0
prodTag.setProdCount(0L);
// 设置创建时间为当前时间
prodTag.setCreateTime(new Date());
// 设置更新时间为当前时间
prodTag.setUpdateTime(new Date());
// 设置商品分组标签所属店铺的id从当前登录用户信息中获取
prodTag.setShopId(SecurityUtils.getSysUser().getShopId());
// 调用业务层方法先进行相关的商品分组标签清理操作具体逻辑由业务层的removeProdTag方法决定
prodTagService.removeProdTag();
return ServerResponseEntity.success(prodTagService.save(prodTag));
}
/**
*
*
* @param prodTag
* @return
*
*
* @param prodTag
* @return ServerResponseEntity<Boolean>truefalse
*/
@SysLog("修改商品分组标签")
@PutMapping
@PreAuthorize("@pms.hasPermission('prod:prodTag:update')")
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'prod:prodTag:update'权限的用户才能访问该接口
public ServerResponseEntity<Boolean> updateById(@RequestBody @Valid ProdTag prodTag) {
// 更新商品分组标签的更新时间为当前时间
prodTag.setUpdateTime(new Date());
// 调用业务层方法先进行相关的商品分组标签清理操作具体逻辑由业务层的removeProdTag方法决定
prodTagService.removeProdTag();
return ServerResponseEntity.success(prodTagService.updateById(prodTag));
}
/**
* id
*
* @param id id
* @return
* id
* id
* @param id id
* @return ServerResponseEntity<Boolean>truefalse
*/
@SysLog("删除商品分组标签")
@DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('prod:prodTag:delete')")
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'prod:prodTag:delete'权限的用户才能访问该接口
public ServerResponseEntity<Boolean> removeById(@PathVariable Long id) {
// 根据传入的id获取对应的商品分组标签对象
ProdTag prodTag = prodTagService.getById(id);
if (prodTag.getIsDefault() != 0) {
if (prodTag.getIsDefault()!= 0) {
// 如果该商品分组标签是默认标签isDefault不为0则抛出异常提示默认标签不能删除
throw new YamiShopBindException("默认标签不能删除");
}
// 调用业务层方法先进行相关的商品分组标签清理操作具体逻辑由业务层的removeProdTag方法决定
prodTagService.removeProdTag();
return ServerResponseEntity.success(prodTagService.removeById(id));
}
/**
*
*
* @return ServerResponseEntity<List<ProdTag>>
*/
@GetMapping("/listTagList")
public ServerResponseEntity<List<ProdTag>> listTagList() {
return ServerResponseEntity.success(prodTagService.listProdTag());
}
}
}

@ -10,7 +10,6 @@
package com.yami.shop.admin.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yami.shop.common.util.PageParam;
@ -25,74 +24,95 @@ import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
/**
*
*
* ID
*
* @author hzm
* @date 2019-04-18 16:28:01
*/
@RestController
@AllArgsConstructor
@RequestMapping("/generator/prodTagReference" )
@RequestMapping("/generator/prodTagReference")
public class ProdTagReferenceController {
// 通过构造函数注入的方式,引入分组标签引用服务层接口,用于调用具体的业务逻辑方法
private final ProdTagReferenceService prodTagReferenceService;
/**
*
* @param page
* @param prodTagReference
* @return
*
* PageParamProdTagReference
* MyBatis Plus LambdaQueryWrapper
*
*
* @param page
* @param prodTagReference
* @return ServerResponseEntity 便
*/
@GetMapping("/page" )
@GetMapping("/page")
public ServerResponseEntity<IPage<ProdTagReference>> getProdTagReferencePage(PageParam page, ProdTagReference prodTagReference) {
return ServerResponseEntity.success(prodTagReferenceService.page(page, new LambdaQueryWrapper<ProdTagReference>()));
}
/**
* id
* @param referenceId id
* @return
* ID
* ID
* ServerResponseEntity 便
*
* @param referenceId ID
* @return ServerResponseEntity
*/
@GetMapping("/info/{referenceId}" )
public ServerResponseEntity<ProdTagReference> getById(@PathVariable("referenceId" ) Long referenceId) {
@GetMapping("/info/{referenceId}")
public ServerResponseEntity<ProdTagReference> getById(@PathVariable("referenceId") Long referenceId) {
return ServerResponseEntity.success(prodTagReferenceService.getById(referenceId));
}
/**
*
* @param prodTagReference
* @return
*
* ProdTagReference
* @SysLog
* 'generator:prodTagReference:save'
* ServerResponseEntity
*
* @param prodTagReference
* @return ServerResponseEntity
*/
@SysLog("新增分组标签引用" )
@SysLog("新增分组标签引用")
@PostMapping
@PreAuthorize("@pms.hasPermission('generator:prodTagReference:save')" )
@PreAuthorize("@pms.hasPermission('generator:prodTagReference:save')")
public ServerResponseEntity<Boolean> save(@RequestBody @Valid ProdTagReference prodTagReference) {
return ServerResponseEntity.success(prodTagReferenceService.save(prodTagReference));
}
/**
*
* @param prodTagReference
* @return
*
* ProdTagReference
* @SysLog
* 'generator:prodTagReference:update'
* ServerResponseEntity
*
* @param prodTagReference
* @return ServerResponseEntity
*/
@SysLog("修改分组标签引用" )
@SysLog("修改分组标签引用")
@PutMapping
@PreAuthorize("@pms.hasPermission('generator:prodTagReference:update')" )
@PreAuthorize("@pms.hasPermission('generator:prodTagReference:update')")
public ServerResponseEntity<Boolean> updateById(@RequestBody @Valid ProdTagReference prodTagReference) {
return ServerResponseEntity.success(prodTagReferenceService.updateById(prodTagReference));
}
/**
* id
* @param referenceId id
* @return
* ID
* ID
* @SysLog 'generator:prodTagReference:delete'
* ServerResponseEntity
*
* @param referenceId ID
* @return ServerResponseEntity
*/
@SysLog("删除分组标签引用" )
@DeleteMapping("/{referenceId}" )
@PreAuthorize("@pms.hasPermission('generator:prodTagReference:delete')" )
@SysLog("删除分组标签引用")
@DeleteMapping("/{referenceId}")
@PreAuthorize("@pms.hasPermission('generator:prodTagReference:delete')")
public ServerResponseEntity<Boolean> removeById(@PathVariable Long referenceId) {
return ServerResponseEntity.success(prodTagReferenceService.removeById(referenceId));
}
}
}

@ -36,9 +36,9 @@ import java.util.Date;
import java.util.List;
import java.util.Objects;
/**
* controller
*
*
*
* @author lgh
*/
@ -46,36 +46,54 @@ import java.util.Objects;
@RequestMapping("/prod/prod")
public class ProductController {
// 注入ProductService用于处理与商品相关的核心业务逻辑例如商品信息的查询、保存、更新、删除等操作。
@Autowired
private ProductService productService;
// 注入SkuService用于处理商品规格Sku相关的业务逻辑比如根据商品ID获取商品规格列表等操作在商品相关操作中常涉及规格信息的处理。
@Autowired
private SkuService skuService;
// 注入ProdTagReferenceService用于获取商品与标签关联相关的业务逻辑例如获取某个商品关联的标签ID列表等操作用于处理商品的分组标签信息。
@Autowired
private ProdTagReferenceService prodTagReferenceService;
// 注入BasketService用于处理购物车相关的业务逻辑在商品操作涉及影响购物车缓存的场景下如商品修改、删除等会调用其方法来清除相关缓存保证数据一致性。
@Autowired
private BasketService basketService;
/**
*
* ProductParamPageParam
* ProductServiceID
*
* @PreAuthorize"prod:prod:page"访
*
* @param product ProductParam
* @param page
* @return ServerResponseEntity
*/
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('prod:prod:page')")
public ServerResponseEntity<IPage<Product>> page(ProductParam product, PageParam<Product> page) {
// 使用ProductService的page方法进行分页查询传入分页参数和LambdaQueryWrapper构建的查询条件。
// 通过StrUtil.isNotBlank判断商品名称是否不为空若不为空则添加模糊查询条件按照商品名称进行模糊匹配同时根据店铺ID和商品状态如果不为空进行筛选最后按照上架时间倒序排序。
IPage<Product> products = productService.page(page,
new LambdaQueryWrapper<Product>()
.like(StrUtil.isNotBlank(product.getProdName()), Product::getProdName, product.getProdName())
.eq(Product::getShopId, SecurityUtils.getSysUser().getShopId())
.eq(product.getStatus() != null, Product::getStatus, product.getStatus())
.orderByDesc(Product::getPutawayTime));
.like(StrUtil.isNotBlank(product.getProdName()), Product::getProdName, product.getProdName())
.eq(Product::getShopId, SecurityUtils.getSysUser().getShopId())
.eq(product.getStatus()!= null, Product::getStatus, product.getStatus())
.orderByDesc(Product::getPutawayTime));
return ServerResponseEntity.success(products);
}
/**
*
* IDIDProductServicegetProductByProdId
* IDIDSkuService
* IDProdTagReferenceService
* @PreAuthorize"prod:prod:info"访
*
* @param prodId
* @return ServerResponseEntityID
*/
@GetMapping("/info/{prodId}")
@PreAuthorize("@pms.hasPermission('prod:prod:info')")
@ -87,14 +105,21 @@ public class ProductController {
List<Sku> skuList = skuService.listByProdId(prodId);
prod.setSkuList(skuList);
//获取分组标签
// 获取分组标签通过ProdTagReferenceService的listTagIdByProdId方法获取该商品关联的标签ID列表并设置到商品对象中。
List<Long> listTagId = prodTagReferenceService.listTagIdByProdId(prodId);
prod.setTagList(listTagId);
return ServerResponseEntity.success(prod);
}
/**
*
* @ValidProductParam
* checkParamProductParamProductBeanUtil
* JSONIDID
* 1ProductServicesaveProduct
* @PreAuthorize"prod:prod:save"访
*
* @param productParam
* @return ServerResponseEntity
*/
@PostMapping
@PreAuthorize("@pms.hasPermission('prod:prod:save')")
@ -114,7 +139,16 @@ public class ProductController {
}
/**
*
* @ValidProductParam
* checkParamProductServicegetProductByProdIdID
* IDID
* ProductParamProductBeanUtilJSON
* ProductServiceupdateProduct
* IDBasketService
* @PreAuthorize"prod:prod:update"访
*
* @param productParam
* @return ServerResponseEntity
*/
@PutMapping
@PreAuthorize("@pms.hasPermission('prod:prod:update')")
@ -151,7 +185,14 @@ public class ProductController {
}
/**
*
* IDProductServicegetProductByProdId
* IDID
* ProductServiceremoveProductByProdId
* IDBasketService
*
*
* @param prodId
* @return ServerResponseEntity
*/
public ServerResponseEntity<Void> delete(Long prodId) {
Product dbProduct = productService.getProductByProdId(prodId);
@ -159,7 +200,7 @@ public class ProductController {
throw new YamiShopBindException("无法获取非本店铺商品信息");
}
List<Sku> dbSkus = skuService.listByProdId(dbProduct.getProdId());
// 删除商品
// 删除商品通过ProductService的removeProductByProdId方法根据商品ID删除商品信息。
productService.removeProductByProdId(prodId);
for (Sku sku : dbSkus) {
@ -177,7 +218,11 @@ public class ProductController {
}
/**
*
* IDIDdelete
* @PreAuthorize"prod:prod:delete"访
*
* @param prodIds ID
* @return ServerResponseEntity
*/
@DeleteMapping
@PreAuthorize("@pms.hasPermission('prod:prod:delete')")
@ -189,7 +234,14 @@ public class ProductController {
}
/**
*
* IDProductID
* 1ProductServiceupdateById
* ProductServiceIDBasketService
* @PreAuthorize"prod:prod:status"访
*
* @param prodId
* @param prodStatus 10
* @return ServerResponseEntity
*/
@PutMapping("/prodStatus")
@PreAuthorize("@pms.hasPermission('prod:prod:status')")
@ -210,26 +262,8 @@ public class ProductController {
return ServerResponseEntity.success();
}
private void checkParam(ProductParam productParam) {
if (CollectionUtil.isEmpty(productParam.getTagList())) {
throw new YamiShopBindException("请选择产品分组");
}
Product.DeliveryModeVO deliveryMode = productParam.getDeliveryModeVo();
boolean hasDeliverMode = deliveryMode != null
&& (deliveryMode.getHasShopDelivery() || deliveryMode.getHasUserPickUp());
if (!hasDeliverMode) {
throw new YamiShopBindException("请选择配送方式");
}
List<Sku> skuList = productParam.getSkuList();
boolean isAllUnUse = true;
for (Sku sku : skuList) {
if (sku.getStatus() == 1) {
isAllUnUse = false;
}
}
if (isAllUnUse) {
throw new YamiShopBindException("至少要启用一种商品规格");
}
}
}
/**
* ProductParam
* 1.
* 2.
* 3.

@ -8,6 +8,7 @@
*
*/
// 该类所属的包名,表明其位于管理端控制器相关的包下,用于对店铺详情相关操作进行控制和处理
package com.yami.shop.admin.controller;
import cn.hutool.core.util.StrUtil;
@ -30,131 +31,183 @@ import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;
/**
* ShopDetailControllerSpring RESTfulShopDetail
*
*
* @author lgh on 2018/08/29.
*/
@RestController
// 定义该控制器类的基础请求路径,所有该类中的接口请求路径都将以此为前缀,表明是与店铺详情相关的操作接口
@RequestMapping("/shop/shopDetail")
public class ShopDetailController {
// 通过Spring的依赖注入机制自动注入ShopDetailService的实例以便调用其提供的与店铺详情相关的业务逻辑方法
@Autowired
private ShopDetailService shopDetailService;
/**
*
*/
@PutMapping("/isDistribution")
public ServerResponseEntity<Void> updateIsDistribution(@RequestParam Integer isDistribution){
ShopDetail shopDetail=new ShopDetail();
shopDetail.setShopId(SecurityUtils.getSysUser().getShopId());
shopDetail.setIsDistribution(isDistribution);
shopDetailService.updateById(shopDetail);
// 更新完成后删除缓存
shopDetailService.removeShopDetailCacheByShopId(shopDetail.getShopId());
return ServerResponseEntity.success();
}
/**
*
*/
@GetMapping("/info")
@PreAuthorize("@pms.hasPermission('shop:shopDetail:info')")
public ServerResponseEntity<ShopDetail> info(){
ShopDetail shopDetail = shopDetailService.getShopDetailByShopId(SecurityUtils.getSysUser().getShopId());
return ServerResponseEntity.success(shopDetail);
}
/**
*
*/
/**
*
* isDistributionShopDetailIDSecurityUtilsID
* ShopDetailServiceupdateByIdremoveShopDetailCacheByShopIdID
* ServerResponseEntityVoid
*
* @param isDistribution 01
* @return ServerResponseEntityVoid
*/
@PutMapping("/isDistribution")
public ServerResponseEntity<Void> updateIsDistribution(@RequestParam Integer isDistribution) {
ShopDetail shopDetail = new ShopDetail();
shopDetail.setShopId(SecurityUtils.getSysUser().getShopId());
shopDetail.setIsDistribution(isDistribution);
shopDetailService.updateById(shopDetail);
// 更新完成后删除缓存
shopDetailService.removeShopDetailCacheByShopId(shopDetail.getShopId());
return ServerResponseEntity.success();
}
/**
*
* SecurityUtilsIDShopDetailServicegetShopDetailByShopId
* ServerResponseEntity
*
* @return ServerResponseEntityShopDetail
*/
@GetMapping("/info")
@PreAuthorize("@pms.hasPermission('shop:shopDetail:info')")
public ServerResponseEntity<ShopDetail> info() {
ShopDetail shopDetail = shopDetailService.getShopDetailByShopId(SecurityUtils.getSysUser().getShopId());
return ServerResponseEntity.success(shopDetail);
}
/**
*
* ShopDetailPageParam
* 使ShopDetailServicepageLambdaQueryWrapper
* IDServerResponseEntity
*
* @param shopDetail
* @param page
* @return ServerResponseEntityIPage<ShopDetail>
*/
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('shop:shopDetail:page')")
public ServerResponseEntity<IPage<ShopDetail>> page(ShopDetail shopDetail,PageParam<ShopDetail> page){
IPage<ShopDetail> shopDetails = shopDetailService.page(page,
new LambdaQueryWrapper<ShopDetail>()
.like(StrUtil.isNotBlank(shopDetail.getShopName()),ShopDetail::getShopName,shopDetail.getShopName())
.orderByDesc(ShopDetail::getShopId));
return ServerResponseEntity.success(shopDetails);
}
/**
*
*/
@GetMapping("/info/{shopId}")
@PreAuthorize("@pms.hasPermission('shop:shopDetail:info')")
public ServerResponseEntity<ShopDetail> info(@PathVariable("shopId") Long shopId){
ShopDetail shopDetail = shopDetailService.getShopDetailByShopId(shopId);
// 店铺图片
return ServerResponseEntity.success(shopDetail);
}
/**
*
*/
@PostMapping
@PreAuthorize("@pms.hasPermission('shop:shopDetail:save')")
public ServerResponseEntity<Void> save(@Valid ShopDetailParam shopDetailParam){
ShopDetail shopDetail = BeanUtil.copyProperties(shopDetailParam, ShopDetail.class);
shopDetail.setCreateTime(new Date());
shopDetail.setShopStatus(1);
shopDetailService.save(shopDetail);
return ServerResponseEntity.success();
}
/**
*
*/
@PutMapping
@PreAuthorize("@pms.hasPermission('shop:shopDetail:update')")
public ServerResponseEntity<Void> update(@Valid ShopDetailParam shopDetailParam){
ShopDetail daShopDetail = shopDetailService.getShopDetailByShopId(shopDetailParam.getShopId());
ShopDetail shopDetail = BeanUtil.copyProperties(shopDetailParam, ShopDetail.class);
shopDetail.setUpdateTime(new Date());
shopDetailService.updateShopDetail(shopDetail,daShopDetail);
return ServerResponseEntity.success();
}
/**
*
*/
@DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('shop:shopDetail:delete')")
public ServerResponseEntity<Void> delete(@PathVariable Long id){
shopDetailService.deleteShopDetailByShopId(id);
return ServerResponseEntity.success();
}
/**
*
*/
@PutMapping("/shopStatus")
@PreAuthorize("@pms.hasPermission('shop:shopDetail:shopStatus')")
public ServerResponseEntity<Void> shopStatus(@RequestParam Long shopId,@RequestParam Integer shopStatus){
ShopDetail shopDetail = new ShopDetail();
shopDetail.setShopId(shopId);
shopDetail.setShopStatus(shopStatus);
shopDetailService.updateById(shopDetail);
// 更新完成后删除缓存
shopDetailService.removeShopDetailCacheByShopId(shopDetail.getShopId());
return ServerResponseEntity.success();
}
/**
*
*/
@PreAuthorize("@pms.hasPermission('shop:shopDetail:page')")
public ServerResponseEntity<IPage<ShopDetail>> page(ShopDetail shopDetail, PageParam<ShopDetail> page) {
IPage<ShopDetail> shopDetails = shopDetailService.page(page,
new LambdaQueryWrapper<ShopDetail>()
.like(StrUtil.isNotBlank(shopDetail.getShopName()), ShopDetail::getShopName, shopDetail.getShopName())
.orderByDesc(ShopDetail::getShopId));
return ServerResponseEntity.success(shopDetails);
}
/**
* ID
* IDShopDetailServicegetShopDetailByShopId
* ServerResponseEntity
*
* @param shopId ID
* @return ServerResponseEntityShopDetail
*/
@GetMapping("/info/{shopId}")
@PreAuthorize("@pms.hasPermission('shop:shopDetail:info')")
public ServerResponseEntity<ShopDetail> info(@PathVariable("shopId") Long shopId) {
ShopDetail shopDetail = shopDetailService.getShopDetailByShopId(shopId);
// 店铺图片(此处可根据实际业务需求进一步处理店铺图片相关逻辑,目前只是简单返回店铺详情对象)
return ServerResponseEntity.success(shopDetail);
}
/**
*
* ShopDetailParamBeanUtilShopDetail
* new Date()1
* ShopDetailServicesaveServerResponseEntityVoid
*
* @param shopDetailParam
* @return ServerResponseEntityVoid
*/
@PostMapping
@PreAuthorize("@pms.hasPermission('shop:shopDetail:save')")
public ServerResponseEntity<Void> save(@Valid ShopDetailParam shopDetailParam) {
ShopDetail shopDetail = BeanUtil.copyProperties(shopDetailParam, ShopDetail.class);
shopDetail.setCreateTime(new Date());
shopDetail.setShopStatus(1);
shopDetailService.save(shopDetail);
return ServerResponseEntity.success();
}
/**
*
* ShopDetailServicegetShopDetailByShopIdShopDetailParamIDdaShopDetail
* ShopDetailParamBeanUtilShopDetailnew Date()
* ShopDetailServiceupdateShopDetail
* ServerResponseEntityVoid
*
* @param shopDetailParam
* @return ServerResponseEntityVoid
*/
@PutMapping
@PreAuthorize("@pms.hasPermission('shop:shopDetail:update')")
public ServerResponseEntity<Void> update(@Valid ShopDetailParam shopDetailParam) {
ShopDetail daShopDetail = shopDetailService.getShopDetailByShopId(shopDetailParam.getShopId());
ShopDetail shopDetail = BeanUtil.copyProperties(shopDetailParam, ShopDetail.class);
shopDetail.setUpdateTime(new Date());
shopDetailService.updateShopDetail(shopDetail, daShopDetail);
return ServerResponseEntity.success();
}
/**
* ID
* IDShopDetailServicedeleteShopDetailByShopId
* ServerResponseEntityVoid
*
* @param id ID
* @return ServerResponseEntityVoid
*/
@DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('shop:shopDetail:delete')")
public ServerResponseEntity<Void> delete(@PathVariable Long id) {
shopDetailService.deleteShopDetailByShopId(id);
return ServerResponseEntity.success();
}
/**
*
* IDshopIdshopStatusShopDetailID
* ShopDetailServiceupdateByIdremoveShopDetailCacheByShopIdID
* ServerResponseEntityVoid
*
* @param shopId ID
* @param shopStatus
* @return ServerResponseEntityVoid
*/
@PutMapping("/shopStatus")
@PreAuthorize("@pms.hasPermission('shop:shopDetail:shopStatus')")
public ServerResponseEntity<Void> shopStatus(@RequestParam Long shopId, @RequestParam Integer shopStatus) {
ShopDetail shopDetail = new ShopDetail();
shopDetail.setShopId(shopId);
shopDetail.setShopStatus(shopStatus);
shopDetailService.updateById(shopDetail);
// 更新完成后删除缓存
shopDetailService.removeShopDetailCacheByShopId(shopDetail.getShopId());
return ServerResponseEntity.success();
}
/**
*
* ShopDetailServicelistJava 8Stream
* IDShopDetail
* ServerResponseEntity
*
* @return ServerResponseEntityList<ShopDetail>ShopDetailID
*/
@GetMapping("/listShopName")
public ServerResponseEntity<List<ShopDetail>> listShopName(){
List<ShopDetail> list = shopDetailService.list().stream().map((dbShopDetail) ->{
ShopDetail shopDetail = new ShopDetail();
shopDetail.setShopId(dbShopDetail.getShopId());
shopDetail.setShopName(dbShopDetail.getShopName());
return shopDetail;
}).collect(Collectors.toList());
return ServerResponseEntity.success(list);
}
}
public ServerResponseEntity<List<ShopDetail>> listShopName() {
List<ShopDetail> list = shopDetailService.list().stream().map((dbShopDetail) -> {
ShopDetail shopDetail = new ShopDetail();
shopDetail.setShopId(dbShopDetail.getShopId());
shopDetail.setShopName(dbShopDetail.getShopName());
return shopDetail;
}).collect(Collectors.toList());
return ServerResponseEntity.success(list);
}
}

@ -31,95 +31,137 @@ import java.util.Objects;
/**
*
*
* idid
* @author lgh
*/
@RestController
@RequestMapping("/prod/spec")
public class SpecController {
// 通过Spring的自动注入机制注入ProdPropService对象用于调用业务层处理商品属性相关的业务逻辑
@Autowired
private ProdPropService prodPropService;
// 通过Spring的自动注入机制注入ProdPropValueService对象用于调用业务层处理商品属性值相关的业务逻辑
@Autowired
private ProdPropValueService prodPropValueService;
/**
*
*
*
* @param prodProp
* @param page
* @return ServerResponseEntity<IPage<ProdProp>>
*/
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('prod:spec:page')")
public ServerResponseEntity<IPage<ProdProp>> page(ProdProp prodProp,PageParam<ProdProp> page) {
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'prod:spec:page'权限的用户才能访问该接口
public ServerResponseEntity<IPage<ProdProp>> page(ProdProp prodProp, PageParam<ProdProp> page) {
// 设置规格的规则为指定的规格类型SPEC这里通过枚举值来表示
prodProp.setRule(ProdPropRule.SPEC.value());
// 设置规格所属店铺的id从当前登录用户信息中获取
prodProp.setShopId(SecurityUtils.getSysUser().getShopId());
// 调用业务层方法获取规格以及对应规格值的分页数据具体逻辑由业务层的pagePropAndValue方法决定
IPage<ProdProp> list = prodPropService.pagePropAndValue(prodProp, page);
return ServerResponseEntity.success(list);
}
/**
*
*
*
* @return ServerResponseEntity<List<ProdProp>>
*/
@GetMapping("/list")
public ServerResponseEntity<List<ProdProp>> list() {
List<ProdProp> list = prodPropService.list(new LambdaQueryWrapper<ProdProp>().eq(ProdProp::getRule, ProdPropRule.SPEC.value()).eq(ProdProp::getShopId, SecurityUtils.getSysUser().getShopId()));
// 使用MyBatis Plus的LambdaQueryWrapper构建查询条件筛选出规则为指定规格类型SPEC且属于当前登录用户店铺的规格信息
List<ProdProp> list = prodPropService.list(new LambdaQueryWrapper<ProdProp>()
.eq(ProdProp::getRule, ProdPropRule.SPEC.value())
.eq(ProdProp::getShopId, SecurityUtils.getSysUser().getShopId()));
return ServerResponseEntity.success(list);
}
/**
* id
* id
* id
* @param specId id
* @return ServerResponseEntity<List<ProdPropValue>>id
*/
@GetMapping("/listSpecValue/{specId}")
public ServerResponseEntity<List<ProdPropValue>> listSpecValue(@PathVariable("specId") Long specId) {
List<ProdPropValue> list = prodPropValueService.list(new LambdaQueryWrapper<ProdPropValue>().eq(ProdPropValue::getPropId, specId));
// 使用MyBatis Plus的LambdaQueryWrapper构建查询条件筛选出属性id即规格id为传入specId的规格值信息
List<ProdPropValue> list = prodPropValueService.list(new LambdaQueryWrapper<ProdPropValue>()
.eq(ProdPropValue::getPropId, specId));
return ServerResponseEntity.success(list);
}
/**
*
*
*
* @param prodProp
* @return ServerResponseEntity<Void>
*/
@PostMapping
@PreAuthorize("@pms.hasPermission('prod:spec:save')")
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'prod:spec:save'权限的用户才能访问该接口
public ServerResponseEntity<Void> save(@Valid @RequestBody ProdProp prodProp) {
// 设置规格的规则为指定的规格类型SPEC这里通过枚举值来表示
prodProp.setRule(ProdPropRule.SPEC.value());
// 设置规格所属店铺的id从当前登录用户信息中获取
prodProp.setShopId(SecurityUtils.getSysUser().getShopId());
// 调用业务层方法保存规格以及对应的规格值信息具体逻辑由业务层的saveProdPropAndValues方法决定
prodPropService.saveProdPropAndValues(prodProp);
return ServerResponseEntity.success();
}
/**
*
*
*
* @param prodProp
* @return ServerResponseEntity<Void>
*/
@PutMapping
@PreAuthorize("@pms.hasPermission('prod:spec:update')")
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'prod:spec:update'权限的用户才能访问该接口
public ServerResponseEntity<Void> update(@Valid @RequestBody ProdProp prodProp) {
// 根据传入的规格id从数据库中获取对应的规格对象用于后续权限校验等操作
ProdProp dbProdProp = prodPropService.getById(prodProp.getPropId());
if (!Objects.equals(dbProdProp.getShopId(), SecurityUtils.getSysUser().getShopId())) {
// 如果当前登录用户所属店铺id与要修改的规格所属店铺id不一致则抛出异常提示没有权限获取该商品规格信息
throw new YamiShopBindException("没有权限获取该商品规格信息");
}
// 设置规格的规则为指定的规格类型SPEC这里通过枚举值来表示
prodProp.setRule(ProdPropRule.SPEC.value());
// 设置规格所属店铺的id从当前登录用户信息中获取
prodProp.setShopId(SecurityUtils.getSysUser().getShopId());
// 调用业务层方法更新规格以及对应的规格值信息具体逻辑由业务层的updateProdPropAndValues方法决定
prodPropService.updateProdPropAndValues(prodProp);
return ServerResponseEntity.success();
}
/**
*
*
* id
* @param id id
* @return ServerResponseEntity<Void>
*/
@DeleteMapping("/{id}")
@PreAuthorize("@pms.hasPermission('prod:spec:delete')")
// 使用 @PreAuthorize 注解进行权限校验,只有拥有'prod:spec:delete'权限的用户才能访问该接口
public ServerResponseEntity<Void> delete(@PathVariable Long id) {
// 调用业务层方法删除规格以及对应的规格值信息传入规格id、规格规则类型以及当前登录用户所属店铺id等参数具体逻辑由业务层的deleteProdPropAndValues方法决定
prodPropService.deleteProdPropAndValues(id, ProdPropRule.SPEC.value(), SecurityUtils.getSysUser().getShopId());
return ServerResponseEntity.success();
}
/**
* id
* id
* id
* @return ServerResponseEntity<Long>id0L
*/
@GetMapping("/listSpecMaxValueId")
public ServerResponseEntity<Long> listSpecMaxValueId() {
// 使用MyBatis Plus的LambdaQueryWrapper构建查询条件按照规格值的自增id降序排列并通过last方法限制只取第一条记录即获取最大的自增id对应的规格值对象
ProdPropValue propValue = prodPropValueService.getOne(new LambdaQueryWrapper<ProdPropValue>()
.orderByDesc(ProdPropValue::getValueId).last("limit 1"));
return ServerResponseEntity.success(Objects.isNull(propValue) ? 0L : propValue.getValueId());
.orderByDesc(ProdPropValue::getValueId).last("limit 1"));
return ServerResponseEntity.success(Objects.isNull(propValue)? 0L : propValue.getValueId());
}
}
}

@ -26,32 +26,50 @@ import java.util.Date;
import java.util.List;
/**
* Transport
*
*
* @author lgh on 2018/11/16.
*/
@RestController
@RequestMapping("/shop/transport")
public class TransportController {
// 自动注入运费模板服务层接口,通过该接口调用具体的业务逻辑方法来处理运费模板相关业务
@Autowired
private TransportService transportService;
/**
*
*
* TransportPageParam
* 'shop:transport:page' 访
* ID
*
* @param transport
* @param page
* @return ServerResponseEntity 便
*/
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('shop:transport:page')")
public ServerResponseEntity<IPage<Transport>> page(Transport transport,PageParam<Transport> page) {
public ServerResponseEntity<IPage<Transport>> page(Transport transport, PageParam<Transport> page) {
// 获取当前登录用户所属店铺的 ID用于筛选当前店铺的运费模板数据
Long shopId = SecurityUtils.getSysUser().getShopId();
IPage<Transport> transports = transportService.page(page,
new LambdaQueryWrapper<Transport>()
.eq(Transport::getShopId, shopId)
.like(StringUtils.isNotBlank(transport.getTransName()), Transport::getTransName, transport.getTransName()));
// 根据店铺 ID 精确匹配,筛选出属于当前店铺的运费模板记录
.eq(Transport::getShopId, shopId)
// 如果传入的运费模板名称不为空,则进行模糊匹配名称字段进行查询,实现根据名称筛选的功能
.like(StringUtils.isNotBlank(transport.getTransName()), Transport::getTransName, transport.getTransName()));
return ServerResponseEntity.success(transports);
}
/**
*
* ID
* ID
* ServerResponseEntity 'shop:transport:info' 访
*
* @param id ID
* @return ServerResponseEntity
*/
@GetMapping("/info/{id}")
@PreAuthorize("@pms.hasPermission('shop:transport:info')")
@ -61,13 +79,20 @@ public class TransportController {
}
/**
*
*
* Transport ID
* 'shop:transport:save'
*
* @param transport
* @return ServerResponseEntity
*/
@PostMapping
@PreAuthorize("@pms.hasPermission('shop:transport:save')")
public ServerResponseEntity<Void> save(@RequestBody Transport transport) {
// 获取当前登录用户所属店铺的 ID设置到运费模板对象中明确该运费模板所属的店铺
Long shopId = SecurityUtils.getSysUser().getShopId();
transport.setShopId(shopId);
// 创建当前时间对象,作为运费模板的创建时间
Date createTime = new Date();
transport.setCreateTime(createTime);
transportService.insertTransportAndTransfee(transport);
@ -75,7 +100,12 @@ public class TransportController {
}
/**
*
*
* Transport
* 'shop:transport:update' ServerResponseEntity
*
* @param transport
* @return ServerResponseEntity
*/
@PutMapping
@PreAuthorize("@pms.hasPermission('shop:transport:update')")
@ -85,28 +115,37 @@ public class TransportController {
}
/**
*
*
* ID
* ID 'shop:transport:delete'
* ServerResponseEntity
*
* @param ids ID
* @return ServerResponseEntity
*/
@DeleteMapping
@PreAuthorize("@pms.hasPermission('shop:transport:delete')")
public ServerResponseEntity<Void> delete(@RequestBody Long[] ids) {
transportService.deleteTransportAndTransfeeAndTranscity(ids);
// 删除运费模板的缓存
// 删除运费模板的缓存,确保缓存数据与数据库最新数据一致,避免缓存数据造成的不一致问题
for (Long id : ids) {
transportService.removeTransportAndAllItemsCache(id);
}
return ServerResponseEntity.success();
}
/**
*
*
* ID
* ServerResponseEntity 便使
*
* @return ServerResponseEntity
*/
@GetMapping("/list")
public ServerResponseEntity<List<Transport>> list() {
// 获取当前登录用户所属店铺的 ID用于筛选当前店铺的运费模板数据
Long shopId = SecurityUtils.getSysUser().getShopId();
List<Transport> list = transportService.list(new LambdaQueryWrapper<Transport>().eq(Transport::getShopId, shopId));
return ServerResponseEntity.success(list);
}
}
}

@ -24,7 +24,9 @@ import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
/**
*
*
* ID
* 便
*
* @author hzm
* @date 2019-04-15 10:49:40
@ -34,26 +36,31 @@ import jakarta.validation.Valid;
@RequestMapping("/user/addr")
public class UserAddrController {
// 通过lombok的@AllArgsConstructor注解注入UserAddrService用于处理与用户地址相关的业务逻辑
// 例如地址信息的查询、保存、更新、删除等操作。
private final UserAddrService userAddrService;
/**
*
* PageParamUserAddr
* UserAddrServiceLambdaQueryWrapper
*
*
* @param page
* @param userAddr
* @return
* @param page
* @param userAddr 使
* @return ServerResponseEntity
*/
@GetMapping("/page")
public ServerResponseEntity<IPage<UserAddr>> getUserAddrPage(PageParam page, UserAddr userAddr) {
// 使用UserAddrService的page方法进行分页查询传入分页参数和一个空的LambdaQueryWrapper目前无额外筛选条件获取分页结果。
return ServerResponseEntity.success(userAddrService.page(page, new LambdaQueryWrapper<UserAddr>()));
}
/**
* id
* IDIDUserAddrServicegetById
*
*
* @param addrId id
* @return
* @param addrId
* @return ServerResponseEntity
*/
@GetMapping("/info/{addrId}")
public ServerResponseEntity<UserAddr> getById(@PathVariable("addrId") Long addrId) {
@ -61,10 +68,12 @@ public class UserAddrController {
}
/**
*
* @ValidUserAddr
* UserAddrServicesave
* @PreAuthorize"user:addr:save"访使@SysLog便
*
* @param userAddr
* @return
* @param userAddr
* @return ServerResponseEntitytruefalse
*/
@SysLog("新增用户地址管理")
@PostMapping
@ -74,10 +83,12 @@ public class UserAddrController {
}
/**
*
* @ValidUserAddr
* UserAddrServiceupdateById
* @PreAuthorize"user:addr:update"访使@SysLog便
*
* @param userAddr
* @return
* @param userAddr
* @return ServerResponseEntitytruefalse
*/
@SysLog("修改用户地址管理")
@PutMapping
@ -87,10 +98,12 @@ public class UserAddrController {
}
/**
* id
* IDIDUserAddrServiceremoveById
*
* @PreAuthorize"user:addr:delete"访使@SysLog便
*
* @param addrId id
* @return
* @param addrId
* @return ServerResponseEntitytruefalse
*/
@SysLog("删除用户地址管理")
@DeleteMapping("/{addrId}")
@ -99,4 +112,4 @@ public class UserAddrController {
return ServerResponseEntity.success(userAddrService.removeById(addrId));
}
}
}

@ -8,6 +8,7 @@
*
*/
// 该类所属的包名,表明此控制器类位于管理端相关的包下,用于处理用户相关的后台管理操作
package com.yami.shop.admin.controller;
import cn.hutool.core.util.StrUtil;
@ -24,57 +25,88 @@ import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.Date;
/**
* UserControllerSpring RESTfulUser
*
* @author lgh on 2018/10/16.
*/
@RestController
// 定义该控制器类的基础请求路径,所有该类中的接口请求路径都将以此为前缀,表明是针对用户的管理操作接口
@RequestMapping("/admin/user")
public class UserController {
// 通过Spring的依赖注入机制自动注入UserService的实例以便调用其提供的与用户相关的业务逻辑方法
@Autowired
private UserService userService;
/**
*
*
* UserPageParam
* 使UserServicepageLambdaQueryWrapper
*
*
* ServerResponseEntity
*
* @param user
* @param page
* @return ServerResponseEntityIPage<User>
*/
@GetMapping("/page")
@PreAuthorize("@pms.hasPermission('admin:user:page')")
public ServerResponseEntity<IPage<User>> page(User user,PageParam<User> page) {
public ServerResponseEntity<IPage<User>> page(User user, PageParam<User> page) {
IPage<User> userPage = userService.page(page, new LambdaQueryWrapper<User>()
.like(StrUtil.isNotBlank(user.getNickName()), User::getNickName, user.getNickName())
.eq(user.getStatus() != null, User::getStatus, user.getStatus()));
.like(StrUtil.isNotBlank(user.getNickName()), User::getNickName, user.getNickName())
.eq(user.getStatus()!= null, User::getStatus, user.getStatus()));
for (User userResult : userPage.getRecords()) {
userResult.setNickName(StrUtil.isBlank(userResult.getNickName()) ? "" : userResult.getNickName());
userResult.setNickName(StrUtil.isBlank(userResult.getNickName())? "" : userResult.getNickName());
}
return ServerResponseEntity.success(userPage);
}
/**
*
*
* ID
* UserServicegetById
* ServerResponseEntity
*
* @param userId ID
* @return ServerResponseEntityUser
*/
@GetMapping("/info/{userId}")
@PreAuthorize("@pms.hasPermission('admin:user:info')")
public ServerResponseEntity<User> info(@PathVariable("userId") String userId) {
User user = userService.getById(userId);
user.setNickName(StrUtil.isBlank(user.getNickName()) ? "" : user.getNickName());
user.setNickName(StrUtil.isBlank(user.getNickName())? "" : user.getNickName());
return ServerResponseEntity.success(user);
}
/**
*
*
*
* new Date()
* UserServiceupdateByIdID
* ServerResponseEntityVoid
*
* @param user
* @return ServerResponseEntityVoid
*/
@PutMapping
@PreAuthorize("@pms.hasPermission('admin:user:update')")
public ServerResponseEntity<Void> update(@RequestBody User user) {
user.setModifyTime(new Date());
user.setNickName(StrUtil.isBlank(user.getNickName()) ? "" : user.getNickName());
user.setNickName(StrUtil.isBlank(user.getNickName())? "" : user.getNickName());
userService.updateById(user);
return ServerResponseEntity.success();
}
/**
*
*
* IDList
* UserServiceremoveByIdsID
* ServerResponseEntityVoid
*
* @param userIds ID
* @return ServerResponseEntityVoid
*/
@DeleteMapping
@PreAuthorize("@pms.hasPermission('admin:user:delete')")
@ -82,4 +114,4 @@ public class UserController {
userService.removeByIds(Arrays.asList(userIds));
return ServerResponseEntity.success();
}
}
}

@ -27,31 +27,47 @@ import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
/**
* @author FrozenWatermelon
*
* xxl-jobjava
* @see com.yami.shop.admin.config.XxlJobConfig
*
* @author FrozenWatermelon
*/
@Component("orderTask")
public class OrderTask {
// 创建一个日志记录器,用于记录定时任务执行过程中的相关信息,方便后续查看任务执行情况以及排查问题。
private static final Logger logger = LoggerFactory.getLogger(OrderTask.class);
// 注入OrderService用于处理与订单相关的业务逻辑例如查询订单、取消订单、确认订单等操作。
@Autowired
private OrderService orderService;
// 注入ProductService用于处理与商品相关的业务逻辑在订单操作涉及影响商品缓存的场景下如订单取消、确认收货等会调用其方法来清除相关商品缓存保证数据一致性。
@Autowired
private ProductService productService;
// 注入SkuService用于处理与商品规格Sku相关的业务逻辑同样在订单操作涉及影响商品规格缓存的场景下会调用其方法来清除相关规格缓存保证数据一致性。
@Autowired
private SkuService skuService;
/**
* @XxlJobXXL-JOB"cancelOrder"
*
* 1.
* 2. OrderStatus.UNPAY.value()30
* 3.
* 4. OrderServicecancelOrders
* 5. OrderItemProductServiceSkuService
*
*/
@XxlJob("cancelOrder")
public void cancelOrder(){
public void cancelOrder() {
Date now = new Date();
logger.info("取消超时未支付订单。。。");
// 获取30分钟之前未支付的订单
List<Order> orders = orderService.listOrderAndOrderItems(OrderStatus.UNPAY.value(),DateUtil.offsetMinute(now, -30));
// 获取30分钟之前未支付的订单通过OrderService的listOrderAndOrderItems方法传入未支付状态值和30分钟之前的时间作为筛选条件进行查询。
List<Order> orders = orderService.listOrderAndOrderItems(OrderStatus.UNPAY.value(), DateUtil.offsetMinute(now, -30));
if (CollectionUtil.isEmpty(orders)) {
return;
}
@ -60,20 +76,27 @@ public class OrderTask {
List<OrderItem> orderItems = order.getOrderItems();
for (OrderItem orderItem : orderItems) {
productService.removeProductCacheByProdId(orderItem.getProdId());
skuService.removeSkuCacheBySkuId(orderItem.getSkuId(),orderItem.getProdId());
skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId());
}
}
}
/**
*
* @XxlJobXXL-JOB"confirmOrder"
*
* 1.
* 2. OrderStatus.CONSIGNMENT.value()15
* 3.
* 4. OrderServiceconfirmOrder
* 5. OrderItemProductServiceSkuService
*
*/
@XxlJob("confirmOrder")
public void confirmOrder(){
public void confirmOrder() {
Date now = new Date();
logger.info("系统自动确认收货订单。。。");
// 获取15天之前未支付的订单
List<Order> orders = orderService.listOrderAndOrderItems(OrderStatus.CONSIGNMENT.value(),DateUtil.offsetDay(now, -15));
// 获取15天之前已发货的订单通过OrderService的listOrderAndOrderItems方法传入已发货状态值和15天之前的时间作为筛选条件进行查询。
List<Order> orders = orderService.listOrderAndOrderItems(OrderStatus.CONSIGNMENT.value(), DateUtil.offsetDay(now, -15));
if (CollectionUtil.isEmpty(orders)) {
return;
}
@ -82,9 +105,9 @@ public class OrderTask {
List<OrderItem> orderItems = order.getOrderItems();
for (OrderItem orderItem : orderItems) {
productService.removeProductCacheByProdId(orderItem.getProdId());
skuService.removeSkuCacheBySkuId(orderItem.getSkuId(),orderItem.getProdId());
skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId());
}
}
}
}
}

@ -9,13 +9,13 @@
*/
// 声明该类所在的包名在Java中包用于对类进行组织和分类管理方便代码的模块化以及避免类名冲突等问题。
// 此处表明该类属于com.yami.shop这个包结构下后续相关的类可以按照包的层次结构进行合理组织和查找。
package com.yami.shop.api;
// 引入Spring Boot相关的核心注解用于标记这是一个Spring Boot应用程序它会开启Spring Boot的自动配置等一系列功能简化Spring应用的搭建和部署流程。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 用于以编程方式构建Spring Boot应用程序特别是在需要对应用的启动配置等进行更多定制化操作时会用到比如配置应用的启动类、配置文件等信息。
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// Spring Boot提供的一个用于支持将Spring Boot应用部署为Web应用例如部署到Servlet容器中的基础类通常需要继承它来做相关的适配和初始化工作。
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
// 用于指定Spring需要扫描的组件所在的基础包路径Spring会在这些包及其子包下查找带有相关注解如 @Component、@Service等的类并将它们注册为Spring容器中的组件便于进行依赖注入等操作。
@ -23,22 +23,28 @@ import org.springframework.context.annotation.ComponentScan;
/**
* @author lgh
* APISpring BootServlet
* APISpring BootServlet
*/
// @SpringBootApplication是一个复合注解它整合了多个Spring相关的注解例如 @Configuration表示这是一个配置类、@EnableAutoConfiguration开启Spring Boot的自动配置功能以及 @ComponentScan默认扫描当前类所在的包及其子包下的组件等。
// 在这里使用它来标记这个类是Spring Boot应用的主配置类Spring Boot会基于这个类进行一系列的启动和配置操作。
@SpringBootApplication
// 通过 @ComponentScan 注解指定Spring要扫描的基础包路径为 "com.yami.shop"意味着Spring会在这个包及其子包下查找并注册各种Spring组件如控制器、服务类、数据访问层等到Spring容器中以便进行后续的依赖注入和业务逻辑处理。
// 这样可以确保我们自定义的各个业务组件能够被Spring正确识别并管理起来方便在不同的组件之间进行依赖注入实现各种业务功能。
@ComponentScan(basePackages = {"com.yami.shop"})
public class ApiApplication extends SpringBootServletInitializer {
// 应用的主入口方法Java应用程序从这个方法开始执行。当运行这个类的 main 方法时会启动Spring Boot应用加载配置、初始化Spring容器并启动相关的Web服务等如果是Web应用的话
public static void main(String[] args) {
// 使用SpringApplication类的静态方法 run 来启动Spring Boot应用传入当前启动类ApiApplication.class以及命令行参数 argsSpring Boot会根据配置自动完成一系列的初始化和启动操作。
SpringApplication.run(ApiApplication.class, args);
}
// 应用的主入口方法Java应用程序从这个方法开始执行。当运行这个类的 main 方法时会启动Spring Boot应用加载配置、初始化Spring容器并启动相关的Web服务等如果是Web应用的话
// 它是整个应用启动的起始点类似于传统Java应用中程序开始执行的地方只不过在这里借助Spring Boot的功能来完成更复杂的应用启动流程。
public static void main(String[] args) {
// 使用SpringApplication类的静态方法 run 来启动Spring Boot应用传入当前启动类ApiApplication.class以及命令行参数 argsSpring Boot会根据配置自动完成一系列的初始化和启动操作。
// 这个方法内部会执行诸如加载配置文件application.properties或application.yml等、创建Spring容器ApplicationContext、扫描并注册组件、启动嵌入式Web服务器如果是Web应用等一系列操作使得整个应用能够正常运行起来。
SpringApplication.run(ApiApplication.class, args);
}
// 重写SpringBootServletInitializer类中的configure方法用于配置Spring Boot应用在Servlet容器中的启动方式这里返回一个SpringApplicationBuilder对象并指定应用的启动类为ApiApplication.class以便在Servlet容器中正确地启动Spring Boot应用。
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(ApiApplication.class);
}
// 重写SpringBootServletInitializer类中的configure方法用于配置Spring Boot应用在Servlet容器中的启动方式这里返回一个SpringApplicationBuilder对象并指定应用的启动类为ApiApplication.class以便在Servlet容器中正确地启动Spring Boot应用。
// 当将Spring Boot应用部署到外部的Servlet容器如Tomcat、Jetty等容器会调用这个方法来构建和配置Spring Boot应用确保应用能够在该容器环境下正常启动和运行实现与Servlet容器的良好集成。
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(ApiApplication.class);
}
}

@ -16,17 +16,43 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* ApiBeanConfigSpringSpringBean
* Spring
* 使 @Configuration SpringSpring
* Bean
* @AllArgsConstructor Lombok便
* SnowflakeBeanSnowflakeID
* ID
* @author lanhai
*/
@Configuration
@AllArgsConstructor
public class ApiBeanConfig {
// 通过Lombok生成的构造函数注入ApiConfig实例ApiConfig实例应该是用于承载和提供配置相关信息的类
// 在这里主要是为了获取其中的workerId和datacenterId两个参数这两个参数对于初始化Snowflake实例至关重要
// 它们在分布式环境下帮助确定Snowflake生成唯一ID的工作节点和数据中心标识从而保证生成的ID在整个分布式系统中是唯一的。
private final ApiConfig apiConfig;
/**
* snowflake @Bean Spring @Bean SpringBean
* SnowflakeSpringBean便Spring
* ID便使
* SnowflakeworkerIddatacenterIdapiConfig
* SnowflakeID
* @return SnowflakeSpring
* ID使
*/
@Bean
public Snowflake snowflake() {
// 使用从ApiConfig中获取的workerId和datacenterId来实例化Snowflake对象确保其在分布式环境下能正确生成唯一ID。
return new Snowflake(apiConfig.getWorkerId(), apiConfig.getDatacenterId());
}
}
}

@ -8,6 +8,7 @@
*
*/
// 该类所属的包名表明其位于商城API相关的配置包下主要负责处理商城API相关的配置信息加载与管理。
package com.yami.shop.api.config;
import lombok.Data;
@ -15,30 +16,52 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
/**
*
* ApiConfig
* Spring
* 使Spring便
*
* @author lgh
*/
@Data
@Component
// @Component注解是Spring框架中用于标记一个类为Spring组件的基础注解。当Spring容器进行组件扫描时通常基于@ComponentScan配置的扫描路径
// 一旦发现被@Component注解标记的类就会将其实例化并纳入Spring容器进行管理。这使得其他需要使用该类的地方可以通过依赖注入如@Autowired注解轻松获取其实例
// 从而实现了类之间的解耦以及配置信息的统一管理和共享。
@Component
// @PropertySource注解用于指定配置属性的来源文件这里的参数"classpath:api.properties"表示会从类路径下查找名为api.properties的文件作为配置信息的数据源。
// 在实际的项目构建和部署过程中这个文件会随着项目一起打包并且在应用启动时Spring会读取其中的配置内容按照后续的配置绑定规则进行属性赋值操作。
@PropertySource("classpath:api.properties")
// @ConfigurationProperties注解在Spring Boot应用中扮演着重要角色它用于将配置文件中的属性值绑定到对应的Java类的成员变量上。
// 这里的参数"prefix = "api""指定了配置文件中属性的前缀意味着Spring会查找以"api"开头的属性,并将其对应的值自动注入到这个类中名称相同(忽略大小写)的成员变量中。
// 例如,如果配置文件中有"api.datacenterId = 1"这样的配置项那么就会自动将值1赋给这个类中的datacenterId成员变量实现了配置文件与Java代码之间的无缝对接方便配置管理。
@ConfigurationProperties(prefix = "api")
// 使用lombok的@Data注解这是一个便捷的代码生成注解它会为类自动生成一系列常用的方法包括成员变量的Getter和Setter方法、toString方法、equals方法以及hashCode方法等。
// 通过提供这些自动生成的方法,使得在其他类中对该类的成员变量进行访问、赋值以及在对象比较、哈希计算等操作时更加方便,减少了手动编写这些重复代码的工作量,提高了代码的简洁性和可读性。
@Data
public class ApiConfig {
/**
* ID
*/
private Integer datacenterId;
/**
* ID
*
* IDIDSnowflakeIDID
*
*/
private Integer datacenterId;
/**
* ID
*/
private Integer workerId;
/**
* ID
*
* ID使
* IDSnowflakeIDID便
*/
private Integer workerId;
/**
*
*/
private String domainName;
/**
*
* 使访
*
* 便使访
*/
private String domainName;
}
}

@ -18,26 +18,42 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* Swagger使
* Swagger
* Swagger
*
* @author LGH
*/
@Configuration
public class SwaggerConfiguration {
@Bean
public GroupedOpenApi createRestApi() {
return GroupedOpenApi.builder()
.group("接口文档")
.packagesToScan("com.yami.shop.api").build();
}
/**
* OpenAPI
* @return GroupedOpenApi
*/
@Bean
public GroupedOpenApi createRestApi() {
return GroupedOpenApi.builder()
// 设置接口文档的分组名称方便在Swagger界面中对接口进行分类查看等操作
.group("接口文档")
// 指定要扫描的包路径Swagger会根据这个路径去查找接口相关的类进而生成对应的接口文档信息
.packagesToScan("com.yami.shop.api").build();
}
@Bean
public OpenAPI springShopOpenApi() {
return new OpenAPI()
.info(new Info().title("Mall4j接口文档")
.description("Mall4j接口文档openapi3.0 接口,用于前端对接")
.version("v0.0.1")
.license(new License().name("使用请遵守AGPL3.0授权协议").url("https://www.mall4j.com")));
}
}
/**
* OpenAPI
* @return OpenAPI
*/
@Bean
public OpenAPI springShopOpenApi() {
return new OpenAPI()
.info(new Info()
// 设置接口文档的标题会显示在Swagger界面的顶部等显著位置
.title("Mall4j接口文档")
// 设置接口文档的描述信息,用于简要说明文档的用途、涵盖范围等内容
.description("Mall4j接口文档openapi3.0 接口,用于前端对接")
// 设置接口文档的版本号,方便对不同版本的接口文档进行区分和管理
.version("v0.0.1")
// 设置接口文档的授权协议相关信息,包括协议名称和协议对应的网址
.license(new License().name("使用请遵守AGPL3.0授权协议").url("https://www.mall4j.com")));
}
}

@ -31,6 +31,8 @@ import java.util.Date;
import java.util.List;
/**
*
*
* @author lanhai
*/
@RestController
@ -39,120 +41,195 @@ import java.util.List;
@AllArgsConstructor
public class AddrController {
// 自动注入用户地址服务层接口,通过该接口调用具体的业务逻辑方法来处理用户地址相关业务
@Autowired
private UserAddrService userAddrService;
/**
*
*
* DTO
*
* @return ServerResponseEntity UserAddrDto 便使
*/
@GetMapping("/list")
@Operation(summary = "用户地址列表" , description = "获取用户的所有地址信息")
@Operation(summary = "用户地址列表", description = "获取用户的所有地址信息")
public ServerResponseEntity<List<UserAddrDto>> dvyList() {
// 获取当前用户的 ID用于筛选属于该用户的地址记录
String userId = SecurityUtils.getUser().getUserId();
List<UserAddr> userAddrs = userAddrService.list(new LambdaQueryWrapper<UserAddr>().eq(UserAddr::getUserId, userId).orderByDesc(UserAddr::getCommonAddr).orderByDesc(UserAddr::getUpdateTime));
// 使用 MyBatis Plus 的 LambdaQueryWrapper 构建查询条件,查询当前用户的所有地址信息,并按照常用地址和更新时间降序排序
List<UserAddr> userAddrs = userAddrService.list(new LambdaQueryWrapper<UserAddr>()
.eq(UserAddr::getUserId, userId)
.orderByDesc(UserAddr::getCommonAddr)
.orderByDesc(UserAddr::getUpdateTime));
// 使用 Hutool 的 BeanUtil 将查询到的 UserAddr 类型的地址列表转换为 UserAddrDto 类型列表,方便返回给前端展示
return ServerResponseEntity.success(BeanUtil.copyToList(userAddrs, UserAddrDto.class));
}
/**
* AddrParam
* ID 0
*
*
* @param addrParam @Valid
* @return ServerResponseEntity "添加地址成功" ID
*/
@PostMapping("/addAddr")
@Operation(summary = "新增用户地址" , description = "新增用户地址")
@Operation(summary = "新增用户地址", description = "新增用户地址")
public ServerResponseEntity<String> addAddr(@Valid @RequestBody AddrParam addrParam) {
// 获取当前用户的 ID用于关联新地址到该用户
String userId = SecurityUtils.getUser().getUserId();
if (addrParam.getAddrId() != null && addrParam.getAddrId() != 0) {
// 如果传入的地址 ID 不为空且不等于 0说明该地址可能已存在返回相应的错误提示信息
if (addrParam.getAddrId()!= null && addrParam.getAddrId()!= 0) {
return ServerResponseEntity.showFailMsg("该地址已存在");
}
// 统计当前用户已有的地址数量,用于判断是否需要将新地址设置为默认地址
long addrCount = userAddrService.count(new LambdaQueryWrapper<UserAddr>().eq(UserAddr::getUserId, userId));
// 使用 Hutool 的 BeanUtil 将 AddrParam 类型的参数对象转换为 UserAddr 类型,方便保存到数据库
UserAddr userAddr = BeanUtil.copyProperties(addrParam, UserAddr.class);
// 如果当前用户没有地址记录则将新地址设置为默认地址常用地址commonAddr 设为 1
if (addrCount == 0) {
userAddr.setCommonAddr(1);
} else {
// 否则设置为非默认地址commonAddr 设为 0
userAddr.setCommonAddr(0);
}
// 设置用户 ID明确该地址所属的用户
userAddr.setUserId(userId);
// 设置地址状态为有效(这里假设 1 表示有效状态)
userAddr.setStatus(1);
// 设置地址的创建时间为当前时间
userAddr.setCreateTime(new Date());
// 设置地址的更新时间为当前时间
userAddr.setUpdateTime(new Date());
// 调用用户地址服务层的保存方法,将新地址信息保存到数据库中
userAddrService.save(userAddr);
// 如果新地址被设置为默认地址commonAddr 为 1则清除默认地址缓存确保缓存数据的一致性
if (userAddr.getCommonAddr() == 1) {
// 清除默认地址缓存
userAddrService.removeUserAddrByUserId(0L, userId);
}
return ServerResponseEntity.success("添加地址成功");
}
/**
*
* AddrParam
*
*
* @param addrParam @Valid
* @return ServerResponseEntity "修改地址成功"
*/
@PutMapping("/updateAddr")
@Operation(summary = "修改订单用户地址" , description = "修改用户地址")
@Operation(summary = "修改订单用户地址", description = "修改用户地址")
public ServerResponseEntity<String> updateAddr(@Valid @RequestBody AddrParam addrParam) {
// 获取当前用户的 ID用于验证地址是否属于该用户以及后续更新操作
String userId = SecurityUtils.getUser().getUserId();
// 根据传入的地址 ID 和用户 ID从数据库中查询对应的用户地址记录若不存在则返回相应的错误提示信息
UserAddr dbUserAddr = userAddrService.getUserAddrByUserId(addrParam.getAddrId(), userId);
if (dbUserAddr == null) {
return ServerResponseEntity.showFailMsg("该地址已被删除");
}
// 使用 Hutool 的 BeanUtil 将 AddrParam 类型的参数对象转换为 UserAddr 类型,准备更新到数据库
UserAddr userAddr = BeanUtil.copyProperties(addrParam, UserAddr.class);
// 设置用户 ID确保更新的是正确用户的地址记录
userAddr.setUserId(userId);
// 设置地址的更新时间为当前时间
userAddr.setUpdateTime(new Date());
// 调用用户地址服务层的更新方法,将更新后的地址信息保存到数据库中
userAddrService.updateById(userAddr);
// 清除当前地址缓存
// 清除当前地址的缓存,保证缓存数据与数据库最新数据一致
userAddrService.removeUserAddrByUserId(addrParam.getAddrId(), userId);
// 清除默认地址缓存
// 清除默认地址缓存,确保默认地址相关数据的准确性
userAddrService.removeUserAddrByUserId(0L, userId);
return ServerResponseEntity.success("修改地址成功");
}
/**
*
* ID
*
*
* @param addrId ID
* @return ServerResponseEntity "删除地址成功"
*/
@DeleteMapping("/deleteAddr/{addrId}")
@Operation(summary = "删除订单用户地址" , description = "根据地址id删除用户地址")
@Parameter(name = "addrId", description = "地址ID" , required = true)
@Operation(summary = "删除订单用户地址", description = "根据地址id删除用户地址")
@Parameter(name = "addrId", description = "地址ID", required = true)
public ServerResponseEntity<String> deleteDvy(@PathVariable("addrId") Long addrId) {
// 获取当前用户的 ID用于验证地址是否属于该用户以及后续删除操作
String userId = SecurityUtils.getUser().getUserId();
// 根据传入的地址 ID 和用户 ID从数据库中查询对应的用户地址记录若不存在则返回相应的错误提示信息
UserAddr userAddr = userAddrService.getUserAddrByUserId(addrId, userId);
if (userAddr == null) {
return ServerResponseEntity.showFailMsg("该地址已被删除");
}
// 如果该地址是默认地址commonAddr 为 1则不允许删除返回相应的错误提示信息
if (userAddr.getCommonAddr() == 1) {
return ServerResponseEntity.showFailMsg("默认地址无法删除");
}
// 调用用户地址服务层的删除方法,从数据库中删除该地址记录
userAddrService.removeById(addrId);
// 清除对应地址的缓存,保证缓存数据与数据库数据的一致性
userAddrService.removeUserAddrByUserId(addrId, userId);
return ServerResponseEntity.success("删除地址成功");
}
/**
*
* ID
*
*
* @param addrId ID
* @return ServerResponseEntity "修改地址成功"
*/
@PutMapping("/defaultAddr/{addrId}")
@Operation(summary = "设置默认地址" , description = "根据地址id设置默认地址")
@Operation(summary = "设置默认地址", description = "根据地址id设置默认地址")
public ServerResponseEntity<String> defaultAddr(@PathVariable("addrId") Long addrId) {
// 获取当前用户的 ID用于关联默认地址到该用户
String userId = SecurityUtils.getUser().getUserId();
// 调用用户地址服务层的方法,将指定地址 ID 的地址设置为当前用户的默认地址
userAddrService.updateDefaultUserAddr(addrId, userId);
// 清除默认地址的缓存,保证缓存数据与数据库最新数据一致
userAddrService.removeUserAddrByUserId(0L, userId);
// 清除当前设置为默认地址的地址缓存,确保相关数据准确性
userAddrService.removeUserAddrByUserId(addrId, userId);
return ServerResponseEntity.success("修改地址成功");
}
/**
*
* ID UserAddrDto
*
*
* @param addrId ID
* @return ServerResponseEntity UserAddrDto
*/
@GetMapping("/addrInfo/{addrId}")
@Operation(summary = "获取地址信息" , description = "根据地址id获取地址信息")
@Parameter(name = "addrId", description = "地址ID" , required = true)
@Operation(summary = "获取地址信息", description = "根据地址id获取地址信息")
@Parameter(name = "addrId", description = "地址ID", required = true)
public ServerResponseEntity<UserAddrDto> addrInfo(@PathVariable("addrId") Long addrId) {
// 获取当前用户的 ID用于验证地址是否属于该用户以及查询操作
String userId = SecurityUtils.getUser().getUserId();
// 根据传入的地址 ID 和用户 ID从数据库中查询对应的用户地址记录若不存在则抛出相应的异常
UserAddr userAddr = userAddrService.getUserAddrByUserId(addrId, userId);
if (userAddr == null) {
throw new YamiShopBindException("该地址已被删除");
}
// 使用 Hutool 的 BeanUtil 将查询到的 UserAddr 类型的地址信息转换为 UserAddrDto 类型,方便返回给前端展示
return ServerResponseEntity.success(BeanUtil.copyProperties(userAddr, UserAddrDto.class));
}
}
}

@ -24,23 +24,36 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
*
*
*
* @author lanhai
*/
@RestController
// 使用 @Tag 注解为该控制器类添加标签说明,用于在 API 文档(如 Swagger 生成的文档)中对该类下的接口进行分类展示,这里表明是“首页轮播图接口”相关的一组接口。
@Tag(name = "首页轮播图接口")
public class IndexImgController {
// 注入IndexImgService用于与首页轮播图相关的业务逻辑处理例如从数据库中查询轮播图列表等操作。
@Autowired
private IndexImgService indexImgService;
/**
*
*
* @GetMapping HTTP GET /indexImgs访
* @Operation API summarydescription便使
*
* @return ServerResponseEntityIndexImgDto
*/
@GetMapping("/indexImgs")
@Operation(summary = "首页轮播图" , description = "获取首页轮播图列表信息")
@Operation(summary = "首页轮播图", description = "获取首页轮播图列表信息")
public ServerResponseEntity<List<IndexImgDto>> indexImgs() {
// 调用IndexImgService的listIndexImg方法从数据库或其他数据源获取首页轮播图的原始数据列表以IndexImg对象列表形式返回
List<IndexImg> indexImgList = indexImgService.listIndexImg();
// 使用hutool的BeanUtil工具类将IndexImg对象列表转换为IndexImgDto对象列表IndexImgDto可能是用于对外展示的、经过筛选或格式调整后的视图对象
// 这样可以避免直接将内部的业务实体对象暴露给客户端,更好地控制数据的展示格式和安全性。
List<IndexImgDto> indexImgDtos = BeanUtil.copyToList(indexImgList, IndexImgDto.class);
// 将转换后的IndexImgDto列表封装在表示成功的ServerResponseEntity对象中返回给客户端遵循统一的接口响应格式规范。
return ServerResponseEntity.success(indexImgDtos);
}
}
}

@ -8,6 +8,7 @@
*
*/
// 该类所属的包名表明其位于商城API的控制器包下主要用于处理与“我的订单”相关的接口请求及业务逻辑。
package com.yami.shop.api.controller;
import cn.hutool.core.bean.BeanUtil;
@ -36,58 +37,85 @@ import java.util.List;
import java.util.Objects;
/**
* MyOrderControllerSpring RESTful
*
*
* @author lanhai
*/
@RestController
// 定义该控制器类的基础请求路径,所有该类中的接口请求路径都将以此为前缀,表明是与“我的订单”相关的操作接口。
@RequestMapping("/p/myOrder")
// 使用Swagger的@Tag注解对该控制器类进行标记用于在API文档中生成对应的分类标签方便接口文档的分类展示和阅读这里表示该类下的接口都属于“我的订单接口”这一分类。
@Tag(name = "我的订单接口")
// 使用lombok的@AllArgsConstructor注解自动生成包含所有final字段的构造函数方便依赖注入这里会为所有注入的服务类生成对应的构造函数参数。
@AllArgsConstructor
public class MyOrderController {
// 通过构造函数注入OrderService实例用于调用与订单相关的业务逻辑方法比如获取订单、取消订单、确认收货等操作。
private final OrderService orderService;
// 通过构造函数注入UserAddrOrderService实例用于处理用户地址与订单相关的业务逻辑可能涉及获取订单对应的用户地址等操作。
private final UserAddrOrderService userAddrOrderService;
// 通过构造函数注入ProductService实例用于操作产品相关的业务逻辑例如可能在某些操作后需要清除产品缓存等情况会用到该服务。
private final ProductService productService;
// 通过构造函数注入SkuService实例用于处理商品库存单元SKU相关的业务逻辑比如根据SKU ID进行缓存清除等操作。
private final SkuService skuService;
// 通过构造函数注入MyOrderService实例用于获取与“我的订单”相关的分页数据等业务逻辑例如根据用户ID和订单状态查询订单列表等操作。
private final MyOrderService myOrderService;
// 通过构造函数注入ShopDetailService实例用于获取店铺详情信息比如在获取订单详情时需要展示店铺名称等信息时会调用该服务获取相应数据。
private final ShopDetailService shopDetailService;
// 通过构造函数注入OrderItemService实例用于获取订单商品项相关的业务逻辑例如根据订单号获取订单中包含的具体商品项信息等操作。
private final OrderItemService orderItemService;
/**
*
*
* orderNumber
* ServerResponseEntity
*
* @param orderNumber
* @return ServerResponseEntityOrderShopDto
*/
@GetMapping("/orderDetail")
// 使用Swagger的@Operation注解对该接口方法进行描述用于在API文档中生成对应的接口说明信息这里简要说明了该接口的功能是获取订单详情信息。
@Operation(summary = "订单详情信息", description = "根据订单号获取订单详情信息")
// 使用Swagger的@Parameter注解对接口方法的参数进行描述这里指定了orderNumber参数的名称、描述以及其为必填项等信息方便在API文档中展示参数详情。
@Parameter(name = "orderNumber", description = "订单号", required = true)
public ServerResponseEntity<OrderShopDto> orderDetail(@RequestParam(value = "orderNumber") String orderNumber) {
// 通过SecurityUtils获取当前登录用户的ID用于后续验证用户是否有权限获取该订单信息等操作。
String userId = SecurityUtils.getUser().getUserId();
OrderShopDto orderShopDto = new OrderShopDto();
// 调用OrderService的getOrderByOrderNumber方法根据订单号获取对应的订单对象如果订单不存在则抛出异常。
Order order = orderService.getOrderByOrderNumber(orderNumber);
if (order == null) {
throw new RuntimeException("该订单不存在");
}
// 验证当前登录用户是否有权限获取该订单信息通过比较订单中的用户ID和当前登录用户的ID是否一致来判断如果不一致则抛出异常。
if (!Objects.equals(order.getUserId(), userId)) {
throw new RuntimeException("你没有权限获取该订单信息");
}
// 调用ShopDetailService的getShopDetailByShopId方法根据订单所属店铺的ID获取店铺详情信息用于填充订单详情中的店铺相关信息。
ShopDetail shopDetail = shopDetailService.getShopDetailByShopId(order.getShopId());
// 调用UserAddrOrderService的getById方法根据订单关联的地址订单ID获取对应的用户地址订单对象用于获取用户地址信息。
UserAddrOrder userAddrOrder = userAddrOrderService.getById(order.getAddrOrderId());
// 使用BeanUtil将UserAddrOrder对象的属性复制到UserAddrDto对象中进行数据类型转换方便后续统一处理和返回数据。
UserAddrDto userAddrDto = BeanUtil.copyProperties(userAddrOrder, UserAddrDto.class);
// 调用OrderItemService的getOrderItemsByOrderNumber方法根据订单号获取该订单下包含的所有商品项信息列表。
List<OrderItem> orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber);
// 使用BeanUtil将List<OrderItem>中的每个OrderItem对象的属性复制到OrderItemDto对象中组成新的列表用于后续统一处理和返回数据。
List<OrderItemDto> orderItemList = BeanUtil.copyToList(orderItems, OrderItemDto.class);
// 将获取到的店铺相关信息设置到OrderShopDto对象中如店铺ID、店铺名称等。
orderShopDto.setShopId(shopDetail.getShopId());
orderShopDto.setShopName(shopDetail.getShopName());
// 设置订单的实际总价、用户地址信息、订单商品项信息、运费、优惠金额、创建时间、备注以及订单状态等信息到OrderShopDto对象中。
orderShopDto.setActualTotal(order.getActualTotal());
orderShopDto.setUserAddrDto(userAddrDto);
orderShopDto.setOrderItemDtos(orderItemList);
@ -97,21 +125,28 @@ public class MyOrderController {
orderShopDto.setRemarks(order.getRemarks());
orderShopDto.setStatus(order.getStatus());
// 计算订单中商品的总金额和商品总数量,通过遍历订单商品项列表,累加每个商品项的商品总金额和商品数量来实现。
double total = 0.0;
Integer totalNum = 0;
for (OrderItemDto orderItem : orderShopDto.getOrderItemDtos()) {
total = Arith.add(total, orderItem.getProductTotalAmount());
totalNum += orderItem.getProdCount();
}
// 将计算得到的商品总金额和商品总数量设置到OrderShopDto对象中。
orderShopDto.setTotal(total);
orderShopDto.setTotalNum(totalNum);
return ServerResponseEntity.success(orderShopDto);
}
/**
*
*
* statuspage
* 0ServerResponseEntity
*
* @param status 12
* @param page
* @return ServerResponseEntityIPage<MyOrderDto>
*/
@GetMapping("/myOrder")
@Operation(summary = "订单列表信息", description = "根据订单状态获取订单列表信息状态为0时获取所有订单")
@ -119,19 +154,29 @@ public class MyOrderController {
@Parameter(name = "status", description = "订单状态 1:待付款 2:待发货 3:待收货 4:待评价 5:成功 6:失败")
})
public ServerResponseEntity<IPage<MyOrderDto>> myOrder(@RequestParam(value = "status") Integer status, PageParam<MyOrderDto> page) {
// 通过SecurityUtils获取当前登录用户的ID用于查询该用户对应的订单列表信息。
String userId = SecurityUtils.getUser().getUserId();
// 调用MyOrderService的pageMyOrderByUserIdAndStatus方法根据用户ID和订单状态进行分页查询订单列表信息获取到IPage<MyOrderDto>类型的分页数据对象。
IPage<MyOrderDto> myOrderDtoIpage = myOrderService.pageMyOrderByUserIdAndStatus(page, userId, status);
return ServerResponseEntity.success(myOrderDtoIpage);
}
/**
*
*
* orderNumber
* OrderServicecancelOrders
* SKUServerResponseEntity
*
* @param orderNumber
* @return ServerResponseEntityString
*/
@PutMapping("/cancel/{orderNumber}")
@Operation(summary = "根据订单号取消订单", description = "根据订单号取消订单")
@Parameter(name = "orderNumber", description = "订单号", required = true)
public ServerResponseEntity<String> cancel(@PathVariable("orderNumber") String orderNumber) {
// 通过SecurityUtils获取当前登录用户的ID用于验证用户是否有权限操作该订单。
String userId = SecurityUtils.getUser().getUserId();
// 调用OrderService的getOrderByOrderNumber方法根据订单号获取对应的订单对象。
Order order = orderService.getOrderByOrderNumber(orderNumber);
if (!Objects.equals(order.getUserId(), userId)) {
throw new YamiShopBindException("你没有权限获取该订单信息");
@ -139,12 +184,14 @@ public class MyOrderController {
if (!Objects.equals(order.getStatus(), OrderStatus.UNPAY.value())) {
throw new YamiShopBindException("订单已支付,无法取消订单");
}
// 调用OrderItemService的getOrderItemsByOrderNumber方法根据订单号获取该订单下包含的所有商品项信息列表。
List<OrderItem> orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber);
order.setOrderItems(orderItems);
// 取消订单
// 调用OrderService的cancelOrders方法传入包含该订单的列表这里使用Collections.singletonList将单个订单包装成列表取消订单操作。
orderService.cancelOrders(Collections.singletonList(order));
// 清除缓存
// 循环遍历订单商品项信息列表针对每个商品项调用ProductService的removeProductCacheByProdId方法清除对应产品的缓存
// 以及调用SkuService的removeSkuCacheBySkuId方法清除对应SKU的缓存以保证数据的一致性和缓存的有效性。
for (OrderItem orderItem : orderItems) {
productService.removeProductCacheByProdId(orderItem.getProdId());
skuService.removeSkuCacheBySkuId(orderItem.getSkuId(), orderItem.getProdId());
@ -152,14 +199,21 @@ public class MyOrderController {
return ServerResponseEntity.success();
}
/**
*
*
* orderNumber
* OrderServiceconfirmOrder
* SKUServerResponseEntity
*
* @param orderNumber
* @return ServerResponseEntityString
*/
@PutMapping("/receipt/{orderNumber}")
@Operation(summary = "根据订单号确认收货", description = "根据订单号确认收货")
public ServerResponseEntity<String> receipt(@PathVariable("orderNumber") String orderNumber) {
// 通过SecurityUtils获取当前登录用户的ID用于验证用户是否有权限操作该订单。
String userId = SecurityUtils.getUser().getUserId();
// 调用OrderService的getOrderByOrderNumber方法根据订单号获取对应的订单对象。
Order order = orderService.getOrderByOrderNumber(orderNumber);
if (!Objects.equals(order.getUserId(), userId)) {
throw new YamiShopBindException("你没有权限获取该订单信息");
@ -167,9 +221,10 @@ public class MyOrderController {
if (!Objects.equals(order.getStatus(), OrderStatus.CONSIGNMENT.value())) {
throw new YamiShopBindException("订单未发货,无法确认收货");
}
// 调用OrderItemService的getOrderItemsByOrderNumber方法根据订单号获取该订单下包含的所有商品项信息列表。
List<OrderItem> orderItems = orderItemService.getOrderItemsByOrderNumber(orderNumber);
order.setOrderItems(orderItems);
// 确认收货
// 调用OrderService的confirmOrder方法传入包含该订单的列表这里使用Collections.singletonList将单个订单包装成列表进行确认收货操作。
orderService.confirmOrder(Collections.singletonList(order));
for (OrderItem orderItem : orderItems) {
@ -180,41 +235,10 @@ public class MyOrderController {
}
/**
*
*/
@DeleteMapping("/{orderNumber}")
@Operation(summary = "根据订单号删除订单", description = "根据订单号删除订单")
@Parameter(name = "orderNumber", description = "订单号", required = true)
public ServerResponseEntity<String> delete(@PathVariable("orderNumber") String orderNumber) {
String userId = SecurityUtils.getUser().getUserId();
Order order = orderService.getOrderByOrderNumber(orderNumber);
if (order == null) {
throw new YamiShopBindException("该订单不存在");
}
if (!Objects.equals(order.getUserId(), userId)) {
throw new YamiShopBindException("你没有权限获取该订单信息");
}
if (!Objects.equals(order.getStatus(), OrderStatus.SUCCESS.value()) && !Objects.equals(order.getStatus(), OrderStatus.CLOSE.value())) {
throw new YamiShopBindException("订单未完成或未关闭,无法删除订单");
}
// 删除订单
orderService.deleteOrders(Collections.singletonList(order));
return ServerResponseEntity.success("删除成功");
}
/**
*
*/
@GetMapping("/orderCount")
@Operation(summary = "获取我的订单订单数量", description = "获取我的订单订单数量")
public ServerResponseEntity<OrderCountData> getOrderCount() {
String userId = SecurityUtils.getUser().getUserId();
OrderCountData orderCountMap = orderService.getOrderCount(userId);
return ServerResponseEntity.success(orderCountMap);
}
}
*
* orderNumber
* OrderServicedeleteOrdersServerResponseEntity
*
*
* @param orderNumber
* @return ServerResponseEntity

@ -29,49 +29,70 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
*
*
* `Swagger`便使
*
* @author lanhai
*/
@RestController
@RequestMapping("/shop/notice")
@Tag(name = "公告管理接口")
// 使用 @AllArgsConstructor 注解,由 lombok 自动生成包含所有成员变量的构造函数,用于依赖注入
@AllArgsConstructor
public class NoticeController {
// 通过构造函数注入NoticeService用于调用业务层方法来获取公告相关的数据
private NoticeService noticeService;
/**
*
*
* DTO
*
* @return ServerResponseEntity<List<NoticeDto>>DTO
*/
@GetMapping("/topNoticeList")
@Operation(summary = "置顶公告列表信息" , description = "获取所有置顶公告列表信息")
@Operation(summary = "置顶公告列表信息", description = "获取所有置顶公告列表信息")
// 使用 @Operation 注解在Swagger文档中对该接口进行描述包括接口的简要总结和详细描述信息
public ServerResponseEntity<List<NoticeDto>> getTopNoticeList() {
// 调用服务层方法获取公告列表数据(这里获取的是所有的公告列表,后续可根据业务逻辑判断哪些是置顶的)
List<Notice> noticeList = noticeService.listNotice();
// 使用 hutool 工具类的方法将Notice类型的列表转换为NoticeDto类型的列表用于传输给前端展示的数据格式转换
List<NoticeDto> noticeDtoList = BeanUtil.copyToList(noticeList, NoticeDto.class);
return ServerResponseEntity.success(noticeDtoList);
}
/**
*
*
* IDDTO
*
* @param id ID @PathVariable
* @return ServerResponseEntity<NoticeDto>IDDTO
*/
@GetMapping("/info/{id}")
@Operation(summary = "公告详情" , description = "获取公告id公告详情")
@Operation(summary = "公告详情", description = "获取公告id公告详情")
public ServerResponseEntity<NoticeDto> getNoticeById(@PathVariable("id") Long id) {
// 调用服务层方法根据ID获取公告详情数据
Notice notice = noticeService.getNoticeById(id);
// 使用 hutool 工具类的方法将Notice对象转换为NoticeDto对象用于传输给前端展示的数据格式转换
NoticeDto noticeDto = BeanUtil.copyProperties(notice, NoticeDto.class);
return ServerResponseEntity.success(noticeDto);
}
/**
*
*
*
*
* @param page
* @return ServerResponseEntity<IPage<NoticeDto>>DTO
*/
@GetMapping("/noticeList")
@Operation(summary = "公告列表信息" , description = "获取所有公告列表信息")
@Operation(summary = "公告列表信息", description = "获取所有公告列表信息")
@Parameters({
})
// 使用 @Parameters 注解在Swagger文档中可以对接口的参数进行详细描述这里目前没有添加具体参数描述内容
public ServerResponseEntity<IPage<NoticeDto>> pageNotice(PageParam<NoticeDto> page) {
return ServerResponseEntity.success(noticeService.pageNotice(page));
}
}
}

@ -39,6 +39,9 @@ import java.util.List;
import java.util.Objects;
/**
*
*
*
* @author lanhai
*/
@RestController
@ -46,112 +49,148 @@ import java.util.Objects;
@Tag(name = "订单接口")
public class OrderController {
// 自动注入订单服务层接口,用于处理订单相关的核心业务逻辑,如保存订单、查询订单等操作
@Autowired
private OrderService orderService;
// 自动注入库存单元SKU服务层接口可能用于处理商品库存相关操作比如库存扣减、缓存清除等
@Autowired
private SkuService skuService;
// 自动注入商品服务层接口,可用于商品相关的业务操作,例如获取商品信息、清除商品缓存等
@Autowired
private ProductService productService;
// 自动注入用户地址服务层接口,用于获取用户地址相关信息,比如根据用户 ID 和地址 ID 查询具体地址详情
@Autowired
private UserAddrService userAddrService;
// 自动注入购物车服务层接口,用于处理购物车相关业务,如获取购物车商品项、根据购物车商品组装店铺相关信息等
@Autowired
private BasketService basketService;
// 自动注入 Spring 的应用上下文对象,用于发布事件,实现基于事件驱动的业务逻辑解耦,例如发布确认订单事件
@Autowired
private ApplicationContext applicationContext;
/**
*
*
* orderService
*
* @param orderParam @Valid ID ID
* @return ServerResponseEntity ShopCartOrderMergerDto 便
*/
@PostMapping("/confirm")
@Operation(summary = "结算,生成订单信息" , description = "传入下单所需要的参数进行下单")
@Operation(summary = "结算,生成订单信息", description = "传入下单所需要的参数进行下单")
public ServerResponseEntity<ShopCartOrderMergerDto> confirm(@Valid @RequestBody OrderParam orderParam) {
// 获取当前用户的 ID用于后续关联订单、地址等信息到该用户
String userId = SecurityUtils.getUser().getUserId();
// 订单地址信息
// 根据传入的地址 ID 和用户 ID从数据库中获取用户的订单地址信息,并转换为 DTO 类型方便后续返回给前端展示
UserAddr userAddr = userAddrService.getUserAddrByUserId(orderParam.getAddrId(), userId);
UserAddrDto userAddrDto = BeanUtil.copyProperties(userAddr, UserAddrDto.class);
// 组装获取用户提交的购物车商品项
List<ShopCartItemDto> shopCartItems = basketService.getShopCartItemsByOrderItems(orderParam.getBasketIds(),orderParam.getOrderItem(),userId);
// 组装获取用户提交的购物车商品项,根据传入的购物车商品项 ID 和用户 ID 等信息,调用购物车服务层方法获取具体的商品项信息列表,
// 如果购物车中没有选择商品(即列表为空),则抛出异常提示用户选择商品加入购物车
List<ShopCartItemDto> shopCartItems = basketService.getShopCartItemsByOrderItems(orderParam.getBasketIds(), orderParam.getOrderItem(), userId);
if (CollectionUtil.isEmpty(shopCartItems)) {
throw new YamiShopBindException("请选择您需要的商品加入购物车");
}
// 根据店铺组装购车中的商品信息,返回每个店铺中的购物车商品信息
// 根据店铺组装购车中的商品信息,将购物车中的商品项按照店铺进行分类整理,返回每个店铺中的购物车商品信息列表,方便后续计算每个店铺的订单详情
List<ShopCartDto> shopCarts = basketService.getShopCarts(shopCartItems);
// 将要返回给前端的完整的订单信息
// 创建一个将要返回给前端的完整的订单信息对象,用于逐步组装并填充订单相关的各种信息
ShopCartOrderMergerDto shopCartOrderMergerDto = new ShopCartOrderMergerDto();
// 将用户地址信息设置到订单信息对象中,作为订单的收货地址相关信息
shopCartOrderMergerDto.setUserAddr(userAddrDto);
// 所有店铺的订单信息
// 创建一个用于存放所有店铺的订单信息的列表,后续将每个店铺的订单信息对象添加到该列表中
List<ShopCartOrderDto> shopCartOrders = new ArrayList<>();
// 初始化订单相关的金额和数量变量,用于累加计算订单的总金额、实际支付金额、商品总数量以及优惠金额等信息
double actualTotal = 0.0;
double total = 0.0;
int totalCount = 0;
double orderReduce = 0.0;
// 遍历每个店铺的购物车信息,进行每个店铺订单信息的组装和金额等相关计算
for (ShopCartDto shopCart : shopCarts) {
// 每个店铺的订单信息
// 创建一个每个店铺的订单信息对象,用于填充该店铺相关的订单详细信息
ShopCartOrderDto shopCartOrder = new ShopCartOrderDto();
// 设置店铺 ID明确该订单信息所属的店铺
shopCartOrder.setShopId(shopCart.getShopId());
// 设置店铺名称,方便前端展示等使用
shopCartOrder.setShopName(shopCart.getShopName());
// 获取该店铺中的商品项折扣信息列表,包含了商品的折扣相关情况以及对应的商品项列表等信息
List<ShopCartItemDiscountDto> shopCartItemDiscounts = shopCart.getShopCartItemDiscounts();
// 店铺中所有商品项信息
// 创建一个用于存放该店铺中所有商品项信息的列表,后续将从商品项折扣信息中提取出所有商品项并添加到该列表中
List<ShopCartItemDto> shopAllShopCartItems = new ArrayList<>();
// 遍历商品项折扣信息列表,将每个折扣信息中的商品项添加到店铺所有商品项列表中,实现商品项的整合
for (ShopCartItemDiscountDto shopCartItemDiscount : shopCartItemDiscounts) {
List<ShopCartItemDto> discountShopCartItems = shopCartItemDiscount.getShopCartItems();
shopAllShopCartItems.addAll(discountShopCartItems);
}
// 将商品项折扣信息设置到店铺订单信息对象中,作为该店铺订单的商品项折扣相关内容
shopCartOrder.setShopCartItemDiscounts(shopCartItemDiscounts);
applicationContext.publishEvent(new ConfirmOrderEvent(shopCartOrder,orderParam,shopAllShopCartItems));
// 发布确认订单事件,将店铺订单信息、下单参数以及店铺所有商品项信息作为事件参数传递出去,
// 可以通过事件监听器实现一些额外的业务逻辑,例如基于事件驱动的业务流程扩展、通知等功能,实现业务逻辑的解耦
applicationContext.publishEvent(new ConfirmOrderEvent(shopCartOrder, orderParam, shopAllShopCartItems));
actualTotal = Arith.add(actualTotal,shopCartOrder.getActualTotal());
total = Arith.add(total,shopCartOrder.getTotal());
// 累加计算订单的实际支付金额,将当前店铺订单的实际支付金额累加到总实际支付金额中
actualTotal = Arith.add(actualTotal, shopCartOrder.getActualTotal());
// 累加计算订单的总金额,将当前店铺订单的总金额累加到总金额中
total = Arith.add(total, shopCartOrder.getTotal());
// 累加计算商品的总数量,将当前店铺订单的商品数量累加到总数量中
totalCount = totalCount + shopCartOrder.getTotalCount();
orderReduce = Arith.add(orderReduce,shopCartOrder.getShopReduce());
shopCartOrders.add(shopCartOrder);
// 累加计算订单的优惠金额,将当前店铺订单的优惠金额累加到总优惠金额中
orderReduce = Arith.add(orderReduce, shopCartOrder.getShopReduce());
// 将当前店铺的订单信息对象添加到所有店铺订单信息列表中,完成一个店铺订单信息的组装
shopCartOrders.add(shopCartOrder);
}
// 将计算好的订单总金额、实际支付金额、商品总数量以及优惠金额等信息设置到完整的订单信息对象中
shopCartOrderMergerDto.setActualTotal(actualTotal);
shopCartOrderMergerDto.setTotal(total);
shopCartOrderMergerDto.setTotalCount(totalCount);
shopCartOrderMergerDto.setShopCartOrders(shopCartOrders);
shopCartOrderMergerDto.setOrderReduce(orderReduce);
// 将组装好的完整订单信息存入缓存中,并返回缓存后的订单信息对象,方便后续提交订单等操作使用缓存数据,提高性能并保证数据一致性
shopCartOrderMergerDto = orderService.putConfirmOrderCache(userId, shopCartOrderMergerDto);
return ServerResponseEntity.success(shopCartOrderMergerDto);
}
/**
* / ,
* /
*
*
*
*
* @param submitOrderParam @Valid
* @return ServerResponseEntity OrderNumbersDto 便
*/
@PostMapping("/submit")
@Operation(summary = "提交订单,返回支付流水号" , description = "根据传入的参数判断是否为购物车提交订单,同时对购物车进行删除,用户开始进行支付")
@Operation(summary = "提交订单,返回支付流水号", description = "根据传入的参数判断是否为购物车提交订单,同时对购物车进行删除,用户开始进行支付")
public ServerResponseEntity<OrderNumbersDto> submitOrders(@Valid @RequestBody SubmitOrderParam submitOrderParam) {
// 获取当前用户的 ID用于后续从缓存中获取订单信息、关联订单等操作到该用户
String userId = SecurityUtils.getUser().getUserId();
// 从缓存中获取之前生成并缓存的确认订单信息,如果缓存中不存在则抛出异常提示订单已过期,需要重新下单
ShopCartOrderMergerDto mergerOrder = orderService.getConfirmOrderCache(userId);
if (mergerOrder == null) {
throw new YamiShopBindException("订单已过期,请重新下单");
}
// 获取提交订单参数中的店铺参数列表,用于后续设置订单的备注信息等操作
List<OrderShopParam> orderShopParams = submitOrderParam.getOrderShopParam();
// 获取缓存中订单信息里的所有店铺订单信息列表,用于遍历设置备注信息以及后续的缓存清除等相关操作
List<ShopCartOrderDto> shopCartOrders = mergerOrder.getShopCartOrders();
// 设置备注
// 设置订单备注信息,如果传入的店铺参数列表不为空,则遍历店铺订单信息和店铺参数,根据店铺 ID 匹配,将对应的备注信息设置到店铺订单对象中
if (CollectionUtil.isNotEmpty(orderShopParams)) {
for (ShopCartOrderDto shopCartOrder : shopCartOrders) {
for (OrderShopParam orderShopParam : orderShopParams) {
@ -162,36 +201,47 @@ public class OrderController {
}
}
List<Order> orders = orderService.submit(userId,mergerOrder);
// 调用订单服务层的提交订单方法,传入用户 ID 和缓存中的订单信息对象,提交订单并获取生成的订单列表,该方法内部会进行复杂的业务逻辑处理,
// 例如根据店铺拆单、保存订单信息到数据库、处理库存扣减等相关操作
List<Order> orders = orderService.submit(userId, mergerOrder);
// 创建一个字符串构建器,用于拼接订单编号,方便后续返回给前端一个用逗号分隔的订单编号字符串
StringBuilder orderNumbers = new StringBuilder();
// 遍历生成的订单列表,将每个订单的编号添加到字符串构建器中,并在每个编号后添加逗号进行分隔
for (Order order : orders) {
orderNumbers.append(order.getOrderNumber()).append(",");
}
// 删除最后一个多余的逗号,得到正确格式的订单编号字符串
orderNumbers.deleteCharAt(orderNumbers.length() - 1);
// 标记是否为购物车提交订单,初始化为 false后续根据购物车商品项的 ID 判断是否为购物车提交订单情况
boolean isShopCartOrder = false;
// 移除缓存
// 移除相关缓存,遍历每个店铺的订单信息、商品项折扣信息以及商品项信息,进行以下操作:
// 1. 判断商品项是否有购物车 ID如果有则说明是购物车提交订单并相应地设置标记变量。
// 2. 清除商品对应的库存单元SKU缓存通过调用 SKU 服务层的方法,传入商品的 SKU ID 和商品 ID。
// 3. 清除商品缓存,通过调用商品服务层的方法,传入商品 ID确保缓存数据与数据库最新状态一致避免数据不一致问题影响后续业务操作。
for (ShopCartOrderDto shopCartOrder : shopCartOrders) {
for (ShopCartItemDiscountDto shopCartItemDiscount : shopCartOrder.getShopCartItemDiscounts()) {
for (ShopCartItemDto shopCartItem : shopCartItemDiscount.getShopCartItems()) {
Long basketId = shopCartItem.getBasketId();
if (basketId != null && basketId != 0) {
if (basketId!= null && basketId!= 0) {
isShopCartOrder = true;
}
skuService.removeSkuCacheBySkuId(shopCartItem.getSkuId(),shopCartItem.getProdId());
skuService.removeSkuCacheBySkuId(shopCartItem.getSkuId(), shopCartItem.getProdId());
productService.removeProductCacheByProdId(shopCartItem.getProdId());
}
}
}
// 购物车提交订单时(即有购物车ID时)
// 如果是购物车提交订单(即标记变量为 true则调用购物车服务层的方法根据用户 ID 清除该用户的购物车商品项缓存,保证购物车数据的准确性
if (isShopCartOrder) {
basketService.removeShopCartItemsCacheByUserId(userId);
}
// 清除确认订单的缓存,通过调用订单服务层的方法,传入用户 ID确保缓存数据与实际业务状态一致避免缓存数据干扰下次下单操作
orderService.removeConfirmOrderCache(userId);
return ServerResponseEntity.success(new OrderNumbersDto(orderNumbers.toString()));
}
}
}

@ -26,45 +26,69 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
*
* PayService
*
* @author lanhai
*/
@RestController
// 设置该控制器类对应的请求映射路径,后续类中的接口方法路径会基于此进行拼接,这里表明是与订单支付相关的接口所在的基础路径。
@RequestMapping("/p/order")
// 使用 @Tag 注解为该控制器类添加标签说明,用于在 API 文档(如 Swagger 生成的文档)中对该类下的接口进行分类展示,这里表明是“订单接口”相关的一组接口。
@Tag(name = "订单接口")
// 通过Lombok的 @AllArgsConstructor 注解生成包含所有参数的构造函数用于依赖注入PayService实例方便后续调用支付相关的业务方法。
@AllArgsConstructor
public class PayController {
// 注入PayService用于处理与支付相关的核心业务逻辑例如发起支付、处理支付成功后的业务逻辑等操作。
private final PayService payService;
/**
*
* PayParam
* @PostMapping HTTP POST /payPOST
* @Operation API summarydescription便使
*
* @param payParam PayParam
* @return ServerResponseEntityVoid
*/
@PostMapping("/pay")
@Operation(summary = "根据订单号进行支付" , description = "根据订单号进行支付")
@Operation(summary = "根据订单号进行支付", description = "根据订单号进行支付")
public ServerResponseEntity<Void> pay(@RequestBody PayParam payParam) {
// 通过SecurityUtils工具类获取当前登录用户信息封装在YamiUser对象中后续可能用于验证用户身份、关联支付记录与用户等操作。
YamiUser user = SecurityUtils.getUser();
// 从获取到的用户对象中提取用户ID作为支付操作中关联用户的关键标识传递给后续的支付业务逻辑处理方法。
String userId = user.getUserId();
// 调用PayService的pay方法传入用户ID和支付参数发起支付操作该方法会根据传入的参数进行具体的支付流程处理
// 例如与支付平台交互、生成支付订单等并返回包含支付相关信息的PayInfoDto对象如支付单号等信息。
PayInfoDto payInfo = payService.pay(userId, payParam);
// 调用PayService的paySuccess方法传入支付单号等信息用于处理支付成功后的相关业务逻辑比如更新订单状态、记录支付记录等操作
// 第二个参数为空字符串可能表示一些额外的备注信息等具体含义取决于paySuccess方法的实现逻辑。
payService.paySuccess(payInfo.getPayNo(), "");
return ServerResponseEntity.success();
}
/**
*
* PayParam
* ServerResponseEntitytrue
* @PostMapping HTTP POST /normalPayPOST
* @Operation API
*
* @param payParam PayParam
* @return ServerResponseEntitytrue
*/
@PostMapping("/normalPay")
@Operation(summary = "根据订单号进行支付" , description = "根据订单号进行支付")
@Operation(summary = "根据订单号进行支付", description = "根据订单号进行支付")
public ServerResponseEntity<Boolean> normalPay(@RequestBody PayParam payParam) {
YamiUser user = SecurityUtils.getUser();
String userId = user.getUserId();
PayInfoDto pay = payService.pay(userId, payParam);
// 根据内部订单号更新order settlement
// 根据内部订单号更新order settlement调用PayService的paySuccess方法处理支付成功后的业务逻辑传入支付单号等信息
// 这里的操作与上面的支付接口中的paySuccess调用类似都是为了保证支付成功后相关业务数据的更新和一致性。
payService.paySuccess(pay.getPayNo(), "");
return ServerResponseEntity.success(true);
}
}
}

@ -8,6 +8,7 @@
*
*/
// 该类所属的包名表明其位于商城API的控制器包下从类名推测可能是用于处理支付通知相关的接口逻辑。
package com.yami.shop.api.controller;
import lombok.AllArgsConstructor;
@ -16,33 +17,44 @@ import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Hidden;
/**
* PayNoticeControllerSpring RESTful@HiddenAPI使Swagger
*
*
* @author lanhai
*/
@Hidden
// 使用@Hidden注解将该控制器标记为隐藏状态使其不在API文档中显示可能是因为该接口目前不需要对外暴露或者处于开发调试阶段等原因。
@RestController
// 表明该类是一个RESTful风格的控制器Spring会自动将其方法返回的对象转换为合适的响应格式如JSON等返回给客户端用于处理HTTP请求并返回响应。
@RequestMapping("/notice/pay")
// 定义该控制器类的基础请求路径,所有该类中的接口请求路径都将以此为前缀,表明是与支付通知相关的操作接口。
@AllArgsConstructor
// 使用lombok的@AllArgsConstructor注解会自动生成包含所有final字段的构造函数方便依赖注入。虽然目前部分依赖如wxMiniPayService和payService被注释掉了但如果后续启用相关功能这个构造函数可以方便地注入对应的服务实例。
public class PayNoticeController {
//模拟支付不需要回调
// /**
// * 小程序支付
// */
// private final WxPayService wxMiniPayService;
//
// private final PayService payService;
//
//
// @RequestMapping("/order")
// public ServerResponseEntity<Void> submit(@RequestBody String xmlData) throws WxPayException {
// WxPayOrderNotifyResult parseOrderNotifyResult = wxMiniPayService.parseOrderNotifyResult(xmlData);
//
// String payNo = parseOrderNotifyResult.getOutTradeNo();
// String bizPayNo = parseOrderNotifyResult.getTransactionId();
//
// // 根据内部订单号更新order settlement
// payService.paySuccess(payNo, bizPayNo);
//
//
// return ServerResponseEntity.success();
// }
}
// 以下两个成员变量对应的服务原本可能用于处理支付相关的业务逻辑,目前被注释掉了,可能是因为模拟支付不需要回调等情况暂时不用这些服务。
// 模拟支付不需要回调
// /**
// * 小程序支付
// */
// private final WxPayService wxMiniPayService;
//
// private final PayService payService;
// 以下是被注释掉的方法从方法名和代码逻辑来看它原本可能是用于接收支付结果的回调通知解析通知中的XML数据微信支付等可能会以XML格式返回支付结果信息
// 获取相关支付单号等信息然后根据内部订单号调用payService的paySuccess方法更新支付相关的业务数据比如订单的支付状态等最后返回表示操作成功的响应信息。
// 目前由于模拟支付不需要回调,所以该方法处于注释状态。
// @RequestMapping("/order")
// public ServerResponseEntity<Void> submit(@RequestBody String xmlData) throws WxPayException {
// WxPayOrderNotifyResult parseOrderNotifyResult = wxMiniPayService.parseOrderNotifyResult(xmlData);
//
// String payNo = parseOrderNotifyResult.getOutTradeNo();
// String bizPayNo = parseOrderNotifyResult.getTransactionId();
//
// // 根据内部订单号更新order settlement
// payService.paySuccess(payNo, bizPayNo);
//
//
// return ServerResponseEntity.success();
// }
}

@ -30,39 +30,76 @@ import org.springframework.web.bind.annotation.*;
import java.util.Date;
/**
*
*
* `Swagger`便使
*
* @author lanhai
*/
@RestController
@RequestMapping("/prodComm")
@Tag(name = "评论接口")
// 使用 @AllArgsConstructor 注解,由 lombok 自动生成包含所有成员变量的构造函数,用于依赖注入
@AllArgsConstructor
public class ProdCommController {
// 通过构造函数注入ProdCommService用于调用业务层方法来处理商品评论相关的业务逻辑
private final ProdCommService prodCommService;
/**
*
* ID
*
* @param prodId ID
* @return ServerResponseEntity<ProdCommDataDto>DTO
*/
@GetMapping("/prodCommData")
@Operation(summary = "返回商品评论数据(好评率 好评数量 中评数 差评数)" , description = "根据商品id获取")
@Operation(summary = "返回商品评论数据(好评率 好评数量 中评数 差评数)", description = "根据商品id获取")
public ServerResponseEntity<ProdCommDataDto> getProdCommData(Long prodId) {
return ServerResponseEntity.success(prodCommService.getProdCommDataByProdId(prodId));
}
/**
*
*
*
* @param page
* @return ServerResponseEntity<IPage<ProdCommDto>>DTO
*/
@GetMapping("/prodCommPageByUser")
@Operation(summary = "根据用户返回评论分页数据" , description = "传入页码")
@Operation(summary = "根据用户返回评论分页数据", description = "传入页码")
public ServerResponseEntity<IPage<ProdCommDto>> getProdCommPage(PageParam page) {
return ServerResponseEntity.success(prodCommService.getProdCommDtoPageByUserId(page, SecurityUtils.getUser().getUserId()));
}
@GetMapping("/prodCommPageByProd")
@Operation(summary = "根据商品返回评论分页数据" , description = "传入商品id和页码")
/**
*
* ID
*
*
* @param page
* @param prodId ID
* @param evaluate -1 null 0 1 2 3
* @return ServerResponseEntity<IPage<ProdCommDto>>DTO
*/
@GetMapping("/prodCommPageByProdId")
@Operation(summary = "根据商品返回评论分页数据", description = "传入商品id和页码")
@Parameters({
@Parameter(name = "prodId", description = "商品id" , required = true),
@Parameter(name = "evaluate", description = "-1或null 全部0好评 1中评 2差评 3有图" , required = true),
@Parameter(name = "prodId", description = "商品id", required = true),
@Parameter(name = "evaluate", description = "-1或null 全部0好评 1中评 2差评 3有图", required = true),
})
public ServerResponseEntity<IPage<ProdCommDto>> getProdCommPageByProdId(PageParam page, Long prodId, Integer evaluate) {
return ServerResponseEntity.success(prodCommService.getProdCommDtoPageByProdId(page, prodId, evaluate));
}
/**
*
* IDID
*
*
* @param prodCommParam IDIDID
* @return ServerResponseEntity<Void>
*/
@PostMapping
@Operation(summary = "添加评论")
public ServerResponseEntity<Void> saveProdCommPage(ProdCommParam prodCommParam) {
@ -81,10 +118,17 @@ public class ProdCommController {
return ServerResponseEntity.success();
}
/**
*
* ID
*
* @param prodCommId ID
* @return ServerResponseEntity<Void>
*/
@DeleteMapping
@Operation(summary = "删除评论" , description = "根据id删除")
@Operation(summary = "删除评论", description = "根据id删除")
public ServerResponseEntity<Void> deleteProdComm(Long prodCommId) {
prodCommService.removeById(prodCommId);
return ServerResponseEntity.success();
}
}
}

@ -37,6 +37,9 @@ import java.util.List;
import java.util.stream.Collectors;
/**
*
* 使
*
* @author lgh on 2018/11/26.
*/
@RestController
@ -44,49 +47,72 @@ import java.util.stream.Collectors;
@Tag(name = "商品接口")
public class ProdController {
// 自动注入商品服务层接口,用于调用与商品相关的核心业务逻辑方法,如根据分类 ID 查询商品列表、获取商品详情等操作
@Autowired
private ProductService prodService;
// 自动注入库存单元SKU服务层接口用于获取商品对应的 SKU 相关信息,例如根据商品 ID 查询其所有的 SKU 列表等操作
@Autowired
private SkuService skuService;
// 自动注入运费模板服务层接口,用于获取商品相关的运费模板信息,比如根据运费模板 ID 获取详细的运费模板及关联信息
@Autowired
private TransportService transportService;
/**
* ID ID ID
* 便
*
* @param categoryId ID
* @param page
* @return ServerResponseEntity ProductDto 便使
*/
@GetMapping("/pageProd")
@Operation(summary = "通过分类id商品列表信息" , description = "根据分类ID获取该分类下所有的商品列表信息")
@Operation(summary = "通过分类id商品列表信息", description = "根据分类ID获取该分类下所有的商品列表信息")
@Parameters({
@Parameter(name = "categoryId", description = "分类ID" , required = true),
@Parameter(name = "categoryId", description = "分类ID", required = true),
})
public ServerResponseEntity<IPage<ProductDto>> prodList(
@RequestParam(value = "categoryId") Long categoryId,PageParam<ProductDto> page) {
@RequestParam(value = "categoryId") Long categoryId, PageParam<ProductDto> page) {
IPage<ProductDto> productPage = prodService.pageByCategoryId(page, categoryId);
return ServerResponseEntity.success(productPage);
}
/**
* ID ID
* SKU ID
* 便
*
* @param prodId ID
* @return ServerResponseEntity ProductDto
*/
@GetMapping("/prodInfo")
@Operation(summary = "商品详情信息" , description = "根据商品IDprodId获取商品信息")
@Parameter(name = "prodId", description = "商品ID" , required = true)
@Operation(summary = "商品详情信息", description = "根据商品IDprodId获取商品信息")
@Parameter(name = "prodId", description = "商品ID", required = true)
public ServerResponseEntity<ProductDto> prodInfo(Long prodId) {
// 通过商品服务层的方法,根据商品 ID 从数据库中获取商品的基本信息,若商品不存在则直接返回空的成功响应(可根据实际需求优化此处逻辑,比如返回特定的错误提示)
Product product = prodService.getProductByProdId(prodId);
if (product == null) {
return ServerResponseEntity.success();
}
// 通过库存单元服务层的方法,根据商品 ID 查询该商品对应的所有 SKU 列表
List<Sku> skuList = skuService.listByProdId(prodId);
// 启用的sku列表
// 使用 Java 8 的 Stream API 过滤出状态为启用(这里假设状态为 1 表示启用)的 SKU 列表,方便前端展示可用的商品库存单元信息
List<Sku> useSkuList = skuList.stream().filter(sku -> sku.getStatus() == 1).collect(Collectors.toList());
// 将启用的 SKU 列表设置到商品对象中,完善商品的详细信息
product.setSkuList(useSkuList);
ProductDto productDto = BeanUtil.copyProperties(product, ProductDto.class);
// 使用 Hutool 的 BeanUtil 将商品对象转换为对应的 DTO 类型,方便按照前端所需的格式返回数据,避免直接暴露数据库实体对象带来的潜在风险
ProductDto productDto = BeanUtil.copyProperties(product, ProductDto.class);
// 商品的配送方式
// 商品的配送方式字符串解析为对应的对象(这里假设使用了自定义的 JSON 解析方法,具体根据实际情况而定),方便后续判断和获取相关的配送信息
Product.DeliveryModeVO deliveryModeVO = Json.parseObject(product.getDeliveryMode(), Product.DeliveryModeVO.class);
// 有店铺配送的方式, 且存在运费模板,才返回运费模板的信息,供前端查阅
if (deliveryModeVO.getHasShopDelivery() && product.getDeliveryTemplateId() != null) {
// 判断如果商品有店铺配送的方式,并且商品关联的运费模板 ID 不为空(即存在运费模板),则通过运费模板服务层的方法,
// 根据运费模板 ID 获取详细的运费模板及关联信息,并设置到商品详情的 DTO 对象中,供前端查阅运费相关情况,方便用户下单时参考运费成本
if (deliveryModeVO.getHasShopDelivery() && product.getDeliveryTemplateId()!= null) {
Transport transportAndAllItems = transportService.getTransportAndAllItems(product.getDeliveryTemplateId());
productDto.setTransport(transportAndAllItems);
}
@ -94,38 +120,65 @@ public class ProdController {
return ServerResponseEntity.success(productDto);
}
/**
*
* 便
*
* @param page
* @return ServerResponseEntity ProductDto 便使
*/
@GetMapping("/lastedProdPage")
@Operation(summary = "新品推荐" , description = "获取新品推荐商品列表")
@Parameters({
})
@Operation(summary = "新品推荐", description = "获取新品推荐商品列表")
@Parameters({})
public ServerResponseEntity<IPage<ProductDto>> lastedProdPage(PageParam<ProductDto> page) {
IPage<ProductDto> productPage = prodService.pageByPutAwayTime(page);
return ServerResponseEntity.success(productPage);
}
/**
* ID ID ID
* 便
*
* @param tagId ID
* @param page
* @return ServerResponseEntity ProductDto 便使
*/
@GetMapping("/prodListByTagId")
@Operation(summary = "通过分组标签获取商品列表" , description = "通过分组标签idtagId获取商品列表")
@Operation(summary = "通过分组标签获取商品列表", description = "通过分组标签idtagId获取商品列表")
@Parameters({
@Parameter(name = "tagId", description = "当前页默认为1" , required = true),
@Parameter(name = "tagId", description = "当前页默认为1", required = true),
})
public ServerResponseEntity<IPage<ProductDto>> prodListByTagId(
@RequestParam(value = "tagId") Long tagId,PageParam<ProductDto> page) {
@RequestParam(value = "tagId") Long tagId, PageParam<ProductDto> page) {
IPage<ProductDto> productPage = prodService.pageByTagId(page, tagId);
return ServerResponseEntity.success(productPage);
}
/**
*
*
*
* @param page
* @return ServerResponseEntity ProductDto 便使
*/
@GetMapping("/moreBuyProdList")
@Operation(summary = "每日疯抢" , description = "获取销量最多的商品列表")
@Operation(summary = "每日疯抢", description = "获取销量最多的商品列表")
@Parameters({})
public ServerResponseEntity<IPage<ProductDto>> moreBuyProdList(PageParam<ProductDto> page) {
IPage<ProductDto> productPage = prodService.moreBuyProdList(page);
return ServerResponseEntity.success(productPage);
}
/**
*
* 便
*
* @return ServerResponseEntity TagProductDto 便使
*/
@GetMapping("/tagProdList")
@Operation(summary = "首页所有标签商品接口" , description = "获取首页所有标签商品接口")
@Operation(summary = "首页所有标签商品接口", description = "获取首页所有标签商品接口")
public ServerResponseEntity<List<TagProductDto>> getTagProdList() {
List<TagProductDto> productDtoList = prodService.tagProdList();
return ServerResponseEntity.success(productDtoList);
}
}
}

@ -25,27 +25,39 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
*
* ProdTagService
*
* @author lanhai
*/
@RestController
// 设置该控制器类对应的请求映射路径,后续类中的接口方法路径会基于此进行拼接,这里表明是与商品分组标签相关接口所在的基础路径。
@RequestMapping("/prod/tag")
// 使用 @Tag 注解为该控制器类添加标签说明,用于在 API 文档(如 Swagger 生成的文档)中对该类下的接口进行分类展示,这里表明是“商品分组标签接口”相关的一组接口。
@Tag(name = "商品分组标签接口")
// 通过Lombok的 @AllArgsConstructor 注解生成包含所有参数的构造函数用于依赖注入ProdTagService实例方便后续调用其业务方法获取标签数据。
@AllArgsConstructor
public class ProdTagController {
// 注入ProdTagService用于处理与商品分组标签相关的业务逻辑例如从数据库中查询所有商品分组标签列表等操作。
private ProdTagService prodTagService;
/**
*
*
* @GetMapping HTTP GET /prodTagListGET
* @Operation API summarydescription便使
*
* @return ServerResponseEntityProdTagDto
*/
@GetMapping("/prodTagList")
@Operation(summary = "商品分组标签列表" , description = "获取所有的商品分组列表")
@Operation(summary = "商品分组标签列表", description = "获取所有的商品分组列表")
public ServerResponseEntity<List<ProdTagDto>> getProdTagList() {
// 调用ProdTagService的listProdTag方法从数据库或其他数据源获取商品分组标签的原始数据列表以ProdTag对象形式返回
List<ProdTag> prodTagList = prodTagService.listProdTag();
// 使用hutool的BeanUtil工具类将ProdTag对象列表转换为ProdTagDto对象列表ProdTagDto可能是用于对外展示的、经过筛选或格式调整后的视图对象
// 这样可以避免直接将内部的业务实体对象暴露给客户端,更好地控制数据的展示格式和安全性。
List<ProdTagDto> prodTagDtoList = BeanUtil.copyToList(prodTagList, ProdTagDto.class);
// 将转换后的ProdTagDto列表封装在表示成功的ServerResponseEntity对象中返回给客户端遵循统一的接口响应格式规范。
return ServerResponseEntity.success(prodTagDtoList);
}
}
}

@ -8,9 +8,9 @@
*
*/
// 该类所属的包名表明其位于商城API的控制器包下主要用于处理与搜索相关的各种接口请求及对应的业务逻辑例如热搜查询、商品搜索等功能。
package com.yami.shop.api.controller;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yami.shop.common.util.PageParam;
@ -32,66 +32,115 @@ import java.util.Collections;
import java.util.List;
/**
* SearchControllerSpring RESTful
* HotSearchServiceProductServiceSwagger便使
*
* @author lanhai
*/
@RestController
// 定义该控制器类的基础请求路径,所有该类中的接口请求路径都将以此为前缀,表明是与搜索相关的操作接口。
@RequestMapping("/search")
// 使用Swagger的@Tag注解对该控制器类进行标记用于在API文档中生成对应的分类标签方便接口文档的分类展示和阅读这里表示该类下的接口都属于“搜索接口”这一分类。
@Tag(name = "搜索接口")
// 使用lombok的@AllArgsConstructor注解自动生成包含所有final字段的构造函数方便依赖注入这里会为HotSearchService和ProductService生成对应的构造函数参数以便在类中使用这两个服务类的实例。
@AllArgsConstructor
public class SearchController {
// 通过构造函数注入HotSearchService实例用于调用与热搜相关的业务逻辑方法比如根据店铺ID或获取全局热搜等操作来获取热搜数据。
private final HotSearchService hotSearchService;
// 通过构造函数注入ProductService实例用于调用与商品搜索相关的业务逻辑方法例如根据商品名、排序条件等进行分页搜索商品并获取相应的商品搜索结果数据。
private final ProductService productService;
/**
* ID
* IDshopIdnumbersortHotSearchServicegetHotSearchDtoByShopId
* getListResponseEntityServerResponseEntity
*
* @param shopId
* @param number
* @param sort 01
* @return ServerResponseEntityList<HotSearchDto>HotSearchDto
*/
@GetMapping("/hotSearchByShopId")
@Operation(summary = "查看店铺热搜" , description = "根据店铺id,热搜数量获取热搜")
// 使用Swagger的@Operation注解对该接口方法进行描述用于在API文档中生成对应的接口说明信息这里简要说明了该接口的功能是查看店铺热搜。
@Operation(summary = "查看店铺热搜", description = "根据店铺id,热搜数量获取热搜")
// 使用Swagger的@Parameters注解对接口方法的多个参数进行统一描述通过包含多个@Parameter注解来分别详细说明每个参数的名称、描述以及是否必填等信息方便在API文档中展示参数详情。
@Parameters({
@Parameter(name = "shopId", description = "店铺id" , required = true),
@Parameter(name = "number", description = "取数" , required = true),
@Parameter(name = "sort", description = "是否按照顺序(0 否 1是)"),
@Parameter(name = "shopId", description = "店铺id", required = true),
@Parameter(name = "number", description = "取数", required = true),
@Parameter(name = "sort", description = "是否按照顺序(0 否 1是)")
})
public ServerResponseEntity<List<HotSearchDto>> hotSearchByShopId(Long shopId,Integer number,Integer sort) {
public ServerResponseEntity<List<HotSearchDto>> hotSearchByShopId(Long shopId, Integer number, Integer sort) {
List<HotSearchDto> list = hotSearchService.getHotSearchDtoByShopId(shopId);
return getListResponseEntity(number, sort, list);
}
/**
*
* numbersortHotSearchServicegetHotSearchDtoByShopIdID0L
* getListResponseEntityServerResponseEntity
*
* @param number
* @param sort 01
* @return ServerResponseEntityList<HotSearchDto>HotSearchDto
*/
@GetMapping("/hotSearch")
@Operation(summary = "查看全局热搜" , description = "根据店铺id,热搜数量获取热搜")
@Operation(summary = "查看全局热搜", description = "根据店铺id,热搜数量获取热搜")
@Parameters({
@Parameter(name = "number", description = "取数" , required = true),
@Parameter(name = "sort", description = "是否按照顺序(0 否 1是)", required = false ),
@Parameter(name = "number", description = "取数", required = true),
@Parameter(name = "sort", description = "是否按照顺序(0 否 1是)", required = false)
})
public ServerResponseEntity<List<HotSearchDto>> hotSearch(Integer number,Integer sort) {
public ServerResponseEntity<List<HotSearchDto>> hotSearch(Integer number, Integer sort) {
List<HotSearchDto> list = hotSearchService.getHotSearchDtoByShopId(0L);
return getListResponseEntity(number, sort, list);
}
/**
*
* numbersortlist
* null0使Collections.shuffle
* ServerResponseEntity
* numberServerResponseEntity
*
* @param number
* @param sort
* @param list HotSearchDto
* @return ServerResponseEntityList<HotSearchDto>
*/
private ServerResponseEntity<List<HotSearchDto>> getListResponseEntity(Integer number, Integer sort, List<HotSearchDto> list) {
if(sort == null || sort == 0){
if (sort == null || sort == 0) {
Collections.shuffle(list);
}
if(!CollectionUtil.isNotEmpty(list) || list.size()< number){
if (!CollectionUtil.isNotEmpty(list) || list.size() < number) {
return ServerResponseEntity.success(list);
}
return ServerResponseEntity.success(list.subList(0, number));
}
/**
*
* pageprodNamesortorderByIDshopIdProductServicegetSearchProdDtoPageByProdName
* IPage<SearchProdDto>ServerResponseEntity
*
* @param page
* @param prodName
* @param sort 012
* @param orderBy 01sort使
* @param shopId
* @return ServerResponseEntityIPage<SearchProdDto>
*/
@GetMapping("/searchProdPage")
@Operation(summary = "分页排序搜索商品" , description = "根据商品名搜索")
@Operation(summary = "分页排序搜索商品", description = "根据商品名搜索")
@Parameters({
@Parameter(name = "prodName", description = "商品名" , required = true),
@Parameter(name = "sort", description = "排序(0 默认排序 1销量排序 2价格排序)"),
@Parameter(name = "prodName", description = "商品名", required = true),
@Parameter(name = "orderBy", description = "排序(0升序 1降序)"),
@Parameter(name = "shopId", description = "店铺id" , required = true),
@Parameter(name = "shopId", description = "店铺id", required = true),
@Parameter(name = "sort", description = "排序(0 默认排序 1销量排序 2价格排序)")
})
public ServerResponseEntity<IPage<SearchProdDto>> searchProdPage(PageParam page, String prodName, Integer sort, Integer orderBy, Long shopId) {
return ServerResponseEntity.success(productService.getSearchProdDtoPageByProdName(page,prodName,sort,orderBy));
return ServerResponseEntity.success(productService.getSearchProdDtoPageByProdName(page, prodName, sort, orderBy));
}
}
}

@ -40,136 +40,195 @@ import java.util.Objects;
import java.util.stream.Collectors;
/**
*
* /
* `Swagger`便使
*
* @author lanhai
*/
@RestController
@RequestMapping("/p/shopCart")
@Tag(name = "购物车接口")
// 使用 @AllArgsConstructor 注解,由 lombok 自动生成包含所有成员变量的构造函数,用于依赖注入
@AllArgsConstructor
public class ShopCartController {
// 用于处理购物车相关业务逻辑的服务层对象,比如更新购物车、获取购物车商品项等操作
private final BasketService basketService;
// 用于处理商品相关业务逻辑的服务层对象,比如获取商品信息等操作
private final ProductService productService;
// 用于处理商品库存单元SKU相关业务逻辑的服务层对象比如获取SKU信息等操作
private final SkuService skuService;
// Spring应用上下文对象用于发布事件等操作例如发布购物车相关事件
private final ApplicationContext applicationContext;
/**
*
*
* ID
*
* @param basketIdShopCartParamMap
* @return
* @param basketIdShopCartParamMap ID
* @return ServerResponseEntity<List<ShopCartDto>>DTO
*/
@PostMapping("/info")
@Operation(summary = "获取用户购物车信息" , description = "获取用户购物车信息,参数为用户选中的活动项数组,以购物车id为key")
@Operation(summary = "获取用户购物车信息", description = "获取用户购物车信息,参数为用户选中的活动项数组,以购物车id为key")
public ServerResponseEntity<List<ShopCartDto>> info(@RequestBody Map<Long, ShopCartParam> basketIdShopCartParamMap) {
// 获取当前登录用户的ID用于关联购物车与用户
String userId = SecurityUtils.getUser().getUserId();
// 更新购物车信息
// 如果传入的购物车参数映射表不为空,调用购物车服务层方法根据参数更新购物车信息
if (MapUtil.isNotEmpty(basketIdShopCartParamMap)) {
basketService.updateBasketByShopCartParam(userId, basketIdShopCartParamMap);
}
// 拿到购物车的所有item
// 调用购物车服务层方法获取该用户的购物车商品项列表
List<ShopCartItemDto> shopCartItems = basketService.getShopCartItems(userId);
return ServerResponseEntity.success(basketService.getShopCarts(shopCartItems));
}
/**
*
* ID
*
* @param basketIds ID
* @return ServerResponseEntity<Void>
*/
@DeleteMapping("/deleteItem")
@Operation(summary = "删除用户购物车物品" , description = "通过购物车id删除用户购物车物品")
@Operation(summary = "删除用户购物车物品", description = "通过购物车id删除用户购物车物品")
public ServerResponseEntity<Void> deleteItem(@RequestBody List<Long> basketIds) {
// 获取当前登录用户的ID用于关联购物车与用户
String userId = SecurityUtils.getUser().getUserId();
basketService.deleteShopCartItemsByBasketIds(userId, basketIds);
return ServerResponseEntity.success();
}
/**
*
*
*
* @return ServerResponseEntity<String>"删除成功"
*/
@DeleteMapping("/deleteAll")
@Operation(summary = "清空用户购物车所有物品" , description = "清空用户购物车所有物品")
@Operation(summary = "清空用户购物车所有物品", description = "清空用户购物车所有物品")
public ServerResponseEntity<String> deleteAll() {
// 获取当前登录用户的ID用于关联购物车与用户
String userId = SecurityUtils.getUser().getUserId();
basketService.deleteAllShopCartItems(userId);
return ServerResponseEntity.success("删除成功");
}
/**
*
* 0
*
*
* @param param IDSKU IDID
* @return ServerResponseEntity<String>
*/
@PostMapping("/changeItem")
@Operation(summary = "添加、修改用户购物车物品", description = "通过商品id(prodId)、skuId、店铺Id(shopId),添加/修改用户购物车商品,并传入改变的商品个数(count)" +
"当count为正值时增加商品数量当count为负值时将减去商品的数量当最终count值小于0时会将商品从购物车里面删除")
public ServerResponseEntity<String> addItem(@Valid @RequestBody ChangeShopCartParam param) {
// 如果更改的商品数量为0则返回提示输入更改数量的失败响应
if (param.getCount() == 0) {
return ServerResponseEntity.showFailMsg("输入更改数量");
}
// 获取当前登录用户的ID用于关联购物车与用户
String userId = SecurityUtils.getUser().getUserId();
// 获取当前用户的购物车商品项列表,用于后续判断商品是否已在购物车中等操作
List<ShopCartItemDto> shopCartItems = basketService.getShopCartItems(userId);
// 根据传入的商品ID获取商品信息
Product prodParam = productService.getProductByProdId(param.getProdId());
// 根据传入的SKU ID获取SKU信息
Sku skuParam = skuService.getSkuBySkuId(param.getSkuId());
// 当商品状态不正常时,不能添加到购物车
if (prodParam.getStatus() != 1 || skuParam.getStatus() != 1) {
// 当商品状态不正常下架状态状态值不为1返回提示商品已下架的失败响应
if (prodParam.getStatus()!= 1 || skuParam.getStatus()!= 1) {
return ServerResponseEntity.showFailMsg("当前商品已下架");
}
// 遍历购物车商品项列表,判断要操作的商品是否已在购物车中
for (ShopCartItemDto shopCartItemDto : shopCartItems) {
if (Objects.equals(param.getSkuId(), shopCartItemDto.getSkuId())) {
Basket basket = new Basket();
basket.setUserId(userId);
// 更新购物车中该商品的数量,为原数量加上传入的更改数量
basket.setBasketCount(param.getCount() + shopCartItemDto.getProdCount());
basket.setBasketId(shopCartItemDto.getBasketId());
// 防止购物车变成负数
// 防止购物车商品数量变成负数如果数量小于等于0则删除该购物车商品项
if (basket.getBasketCount() <= 0) {
basketService.deleteShopCartItemsByBasketIds(userId, Collections.singletonList(basket.getBasketId()));
return ServerResponseEntity.success();
}
// 当sku实际库存不足时不能添加到购物车
// 当SKU实际库存不足当前库存小于购物车中要设置的数量且原购物车商品数量大于0说明是修改操作返回提示库存不足的失败响应
if (skuParam.getStocks() < basket.getBasketCount() && shopCartItemDto.getProdCount() > 0) {
return ServerResponseEntity.showFailMsg("库存不足");
}
// 调用购物车服务层方法更新购物车商品项信息
basketService.updateShopCartItem(basket);
return ServerResponseEntity.success();
}
}
// 防止购物车已被删除的情况下,添加了负数的商品
// 如果商品不在购物车中,且传入的更改数量为负数,说明商品已被删除,返回相应提示的失败响应
if (param.getCount() < 0) {
return ServerResponseEntity.showFailMsg("商品已从购物车移除");
}
// 当sku实际库存不足时不能添加到购物车
// 当SKU实际库存不足当前库存小于要添加的数量说明是添加操作返回提示库存不足的失败响应
if (skuParam.getStocks() < param.getCount()) {
return ServerResponseEntity.showFailMsg("库存不足");
}
// 所有都正常时
basketService.addShopCartItem(param,userId);
// 所有验证都通过时,调用购物车服务层方法添加购物车商品项,并返回添加成功的响应
basketService.addShopCartItem(param, userId);
return ServerResponseEntity.success("添加成功");
}
/**
*
* 0
*
* @return ServerResponseEntity<Integer>
*/
@GetMapping("/prodCount")
@Operation(summary = "获取购物车商品数量" , description = "获取所有购物车商品数量")
@Operation(summary = "获取购物车商品数量", description = "获取所有购物车商品数量")
public ServerResponseEntity<Integer> prodCount() {
// 获取当前登录用户的ID用于关联购物车与用户
String userId = SecurityUtils.getUser().getUserId();
// 获取当前用户的购物车商品项列表
List<ShopCartItemDto> shopCartItems = basketService.getShopCartItems(userId);
if (CollectionUtil.isEmpty(shopCartItems)) {
return ServerResponseEntity.success(0);
}
// 使用流操作计算购物车商品项列表中所有商品数量的总和
Integer totalCount = shopCartItems.stream().map(ShopCartItemDto::getProdCount).reduce(0, Integer::sum);
return ServerResponseEntity.success(totalCount);
}
/**
*
* ID
*
* @return ServerResponseEntity<List<ShopCartExpiryItemDto>>DTO
*/
@GetMapping("/expiryProdList")
@Operation(summary = "获取购物车失效商品信息" , description = "获取购物车失效商品列表")
@Operation(summary = "获取购物车失效商品信息", description = "获取购物车失效商品列表")
public ServerResponseEntity<List<ShopCartExpiryItemDto>> expiryProdList() {
// 获取当前登录用户的ID用于关联购物车与用户
String userId = SecurityUtils.getUser().getUserId();
// 获取当前用户购物车中的失效商品项列表
List<ShopCartItemDto> shopCartItems = basketService.getShopCartExpiryItems(userId);
//根据店铺ID划分item
// 根据店铺ID对失效商品项列表进行分组得到以店铺ID为键对应商品项列表为值的映射表
Map<Long, List<ShopCartItemDto>> shopCartItemDtoMap = shopCartItems.stream().collect(Collectors.groupingBy(ShopCartItemDto::getShopId));
// 返回一个店铺对应的所有信息
// 用于存储构建好的每个店铺对应的购物车失效商品信息对象列表
List<ShopCartExpiryItemDto> shopcartExpiryitems = Lists.newArrayList();
// 遍历分组后的店铺ID集合构建每个店铺对应的购物车失效商品信息对象并添加到列表中
for (Long key : shopCartItemDtoMap.keySet()) {
ShopCartExpiryItemDto shopCartExpiryItemDto = new ShopCartExpiryItemDto();
shopCartExpiryItemDto.setShopId(key);
@ -185,66 +244,44 @@ public class ShopCartController {
return ServerResponseEntity.success(shopcartExpiryitems);
}
/**
*
*
*
* @return ServerResponseEntity<Void>
*/
@DeleteMapping("/cleanExpiryProdList")
@Operation(summary = "清空用户失效商品" , description = "清空用户失效商品")
@Operation(summary = "清空用户失效商品", description = "清空用户失效商品")
public ServerResponseEntity<Void> cleanExpiryProdList() {
// 获取当前登录用户的ID用于关联购物车与用户
String userId = SecurityUtils.getUser().getUserId();
basketService.cleanExpiryProdList(userId);
return ServerResponseEntity.success();
}
/**
*
* IDIDDTO
*
* @param basketIds ID
* @return ServerResponseEntity<ShopCartAmountDto>DTO
*/
@PostMapping("/totalPay")
@Operation(summary = "获取选中购物项总计、选中的商品数量" , description = "获取选中购物项总计、选中的商品数量,参数为购物车id数组")
@Operation(summary = "获取选中购物项总计、选中的商品数量", description = "获取选中购物项总计、选中的商品数量,参数为购物车id数组")
public ServerResponseEntity<ShopCartAmountDto> getTotalPay(@RequestBody List<Long> basketIds) {
// 拿到购物车的所有item
List<ShopCartItemDto> dbShopCartItems = basketService.getShopCartItems(SecurityUtils.getUser().getUserId());
// 从所有购物车商品项中筛选出传入的购物车ID列表对应的商品项
List<ShopCartItemDto> chooseShopCartItems = dbShopCartItems
.stream()
.filter(shopCartItemDto -> {
for (Long basketId : basketIds) {
if (Objects.equals(basketId,shopCartItemDto.getBasketId())) {
return true;
}
}
return false;
})
.toList();
// 根据店铺ID划分item
Map<Long, List<ShopCartItemDto>> shopCartMap = chooseShopCartItems.stream().collect(Collectors.groupingBy(ShopCartItemDto::getShopId));
double total = 0.0;
int count = 0;
double reduce = 0.0;
for (Long shopId : shopCartMap.keySet()) {
//获取店铺的所有商品项
List<ShopCartItemDto> shopCartItemDtoList = shopCartMap.get(shopId);
// 构建每个店铺的购物车信息
ShopCartDto shopCart = new ShopCartDto();
shopCart.setShopId(shopId);
applicationContext.publishEvent(new ShopCartEvent(shopCart, shopCartItemDtoList));
List<ShopCartItemDiscountDto> shopCartItemDiscounts = shopCart.getShopCartItemDiscounts();
for (ShopCartItemDiscountDto shopCartItemDiscount : shopCartItemDiscounts) {
List<ShopCartItemDto> shopCartItems = shopCartItemDiscount.getShopCartItems();
for (ShopCartItemDto shopCartItem : shopCartItems) {
count = shopCartItem.getProdCount() + count;
total = Arith.add(shopCartItem.getProductTotalAmount(), total);
}
}
}
ShopCartAmountDto shopCartAmountDto = new ShopCartAmountDto();
shopCartAmountDto.setCount(count);
shopCartAmountDto.setTotalMoney(total);
shopCartAmountDto.setSubtractMoney(reduce);
shopCartAmountDto.setFinalMoney(Arith.sub(shopCartAmountDto.getTotalMoney(), shopCartAmountDto.getSubtractMoney()));
return ServerResponseEntity.success(shopCartAmountDto);
}
}
.stream()
.filter(shopCartItemDto -> {
for (Long basketId : basketIds) {
if (Objects.equals(basketId, shopCartItemDto.getBasketId())) {
return true;
}
}
return false;
})
.toList();

@ -27,6 +27,9 @@ import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* SKU ID
* DTO 使
*
* @author lanhai
*/
@RestController
@ -35,20 +38,29 @@ import java.util.List;
@AllArgsConstructor
public class SkuController {
// 通过构造函数注入的方式引入 SKU 服务层接口,方便后续调用其提供的业务逻辑方法来处理与 SKU 相关的操作,比如查询 SKU 列表等。
private final SkuService skuService;
/**
* ID ID 使 MyBatis Plus LambdaQueryWrapper
* 1 isDelete 0 SKU
* SKU DTO ServerResponseEntity 便使
*
* @param prodId ID SKU Sku prodId
* @return ServerResponseEntity SkuDto SKU SKU
*/
@GetMapping("/getSkuList")
@Operation(summary = "通过prodId获取商品全部规格列表" , description = "通过prodId获取商品全部规格列表")
@Parameter(name = "prodId", description = "商品id" )
@Operation(summary = "通过prodId获取商品全部规格列表", description = "通过prodId获取商品全部规格列表")
@Parameter(name = "prodId", description = "商品id")
public ServerResponseEntity<List<SkuDto>> getSkuListByProdId(Long prodId) {
// 使用 MyBatis Plus 的 LambdaQueryWrapper 构建查询条件,设置筛选条件为状态为启用、未被删除且属于指定商品 ID 的 SKU 记录。
List<Sku> skus = skuService.list(new LambdaQueryWrapper<Sku>()
.eq(Sku::getStatus, 1)
.eq(Sku::getIsDelete, 0)
.eq(Sku::getProdId, prodId)
.eq(Sku::getStatus, 1)
.eq(Sku::getIsDelete, 0)
.eq(Sku::getProdId, prodId)
);
// 使用 Hutool 的 BeanUtil 将查询到的 Sku 实体列表转换为 SkuDto 类型的列表,方便按照前端期望的格式返回数据,避免直接暴露实体对象的一些潜在问题。
List<SkuDto> skuDtoList = BeanUtil.copyToList(skus, SkuDto.class);
return ServerResponseEntity.success(skuDtoList);
}
}
}

@ -25,24 +25,38 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
*
* SmsLogService
*
* @author lanhai
*/
@RestController
// 设置该控制器类对应的请求映射路径,后续类中的接口方法路径会基于此进行拼接,这里表明是与发送验证码相关接口所在的基础路径。
@RequestMapping("/p/sms")
// 使用 @Tag 注解为该控制器类添加标签说明,用于在 API 文档(如 Swagger 生成的文档)中对该类下的接口进行分类展示,这里表明是“发送验证码接口”相关的一组接口。
@Tag(name = "发送验证码接口")
public class SmsController {
@Autowired
private SmsLogService smsLogService;
// 注入SmsLogService用于处理与短信验证码相关的业务逻辑例如实际发送短信、记录短信发送日志等操作。
@Autowired
private SmsLogService smsLogService;
/**
*
*
* @PostMapping HTTP POST /sendPOST
* @Operation API summarydescription便使
*
* @param sendSmsParam SendSmsParam
* @return ServerResponseEntityVoid
*/
@PostMapping("/send")
@Operation(summary = "发送验证码" , description = "用户的发送验证码")
@Operation(summary = "发送验证码", description = "用户的发送验证码")
public ServerResponseEntity<Void> audit(@RequestBody SendSmsParam sendSmsParam) {
String userId = SecurityUtils.getUser().getUserId();
smsLogService.sendSms(SmsType.VALID, userId, sendSmsParam.getMobile(),Maps.newHashMap());
return ServerResponseEntity.success();
// 通过SecurityUtils工具类获取当前登录用户的ID可能用于记录短信发送与用户的关联关系或者作为短信发送业务逻辑中的一部分验证等操作。
String userId = SecurityUtils.getUser().getUserId();
// 调用SmsLogService的sendSms方法来发送短信验证码传入短信类型这里指定为SmsType.VALID表示验证类型的短信、用户ID、手机号码以及一个空的Map可能用于传递一些额外的短信发送配置参数等目前为空作为参数进行短信发送操作。
smsLogService.sendSms(SmsType.VALID, userId, sendSmsParam.getMobile(), Maps.newHashMap());
return ServerResponseEntity.success();
}
}
}

@ -8,13 +8,13 @@
*
*/
// 该类所属的包名表明其位于商城API的控制器包下主要用于处理与用户收藏相关的各种接口请求及对应的业务逻辑比如获取收藏数据、判断是否收藏、添加或取消收藏等操作。
package com.yami.shop.api.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yami.shop.bean.app.dto.ProductDto;
import com.yami.shop.bean.app.dto.UserCollectionDto;
import com.yaami.shop.bean.app.dto.UserCollectionDto;
import com.yami.shop.bean.model.Product;
import com.yami.shop.bean.model.UserCollection;
import com.yami.shop.common.exception.YamiShopBindException;
@ -31,51 +31,87 @@ import org.springframework.web.bind.annotation.*;
import java.util.Date;
import java.util.Objects;
/**
* UserCollectionControllerSpring RESTful
* UserCollectionServiceProductServiceSwagger便使
*
* @author lanhai
*/
@RestController
// 定义该控制器类的基础请求路径,所有该类中的接口请求路径都将以此为前缀,表明是与用户收藏相关的操作接口。
@RequestMapping("/p/user/collection")
// 使用Swagger的@Tag注解对该控制器类进行标记用于在API文档中生成对应的分类标签方便接口文档的分类展示和阅读这里表示该类下的接口都属于“收藏接口”这一分类。
@Tag(name = "收藏接口")
// 使用lombok的@AllArgsConstructor注解自动生成包含所有final字段的构造函数方便依赖注入这里会为UserCollectionService和ProductService生成对应的构造函数参数以便在类中使用这两个服务类的实例。
@AllArgsConstructor
public class UserCollectionController {
// 通过构造函数注入UserCollectionService实例用于调用与用户收藏记录相关的业务逻辑方法比如查询收藏记录、添加或删除收藏记录等操作。
private final UserCollectionService userCollectionService;
// 通过构造函数注入ProductService实例用于调用与商品相关的业务逻辑方法例如判断商品是否存在、获取收藏商品列表等操作通常会结合用户收藏记录来实现相关业务功能。
private final ProductService productService;
/**
*
* pageUserCollectionServicegetUserCollectionDtoPageByUserIdIDSecurityUtils
* ServerResponseEntity
*
* @param page
* @return ServerResponseEntityIPage<UserCollectionDto>
*/
@GetMapping("/page")
@Operation(summary = "分页返回收藏数据" , description = "根据用户id获取")
@Operation(summary = "分页返回收藏数据", description = "根据用户id获取")
public ServerResponseEntity<IPage<UserCollectionDto>> getUserCollectionDtoPageByUserId(PageParam page) {
return ServerResponseEntity.success(userCollectionService.getUserCollectionDtoPageByUserId(page, SecurityUtils.getUser().getUserId()));
}
/**
* ID
* IDprodIdProductServicecountLambdaQueryWrapper1
* UserCollectionServicecountLambdaQueryWrapperIDIDSecurityUtils
* 0truefalseServerResponseEntity
*
* @param prodId
* @return ServerResponseEntityBooleantruefalse
*/
@GetMapping("isCollection")
@Operation(summary = "根据商品id获取该商品是否在收藏夹中" , description = "传入收藏商品id")
@Operation(summary = "根据商品id获取该商品是否在收藏夹中", description = "传入收藏商品id")
public ServerResponseEntity<Boolean> isCollection(Long prodId) {
if (productService.count(new LambdaQueryWrapper<Product>()
.eq(Product::getProdId, prodId)) < 1) {
.eq(Product::getProdId, prodId)) < 1) {
throw new YamiShopBindException("该商品不存在");
}
return ServerResponseEntity.success(userCollectionService.count(new LambdaQueryWrapper<UserCollection>()
.eq(UserCollection::getProdId, prodId)
.eq(UserCollection::getUserId, SecurityUtils.getUser().getUserId())) > 0);
.eq(UserCollection::getProdId, prodId)
.eq(UserCollection::getUserId, SecurityUtils.getUser().getUserId())) > 0);
}
/**
* /
* IDprodIdProductServicegetProductByProdIdIDSecurityUtils
* UserCollectionServicecountLambdaQueryWrapper0removeLambdaQueryWrapper
* 0UserCollectionnew Date()IDIDsave
* ServerResponseEntityVoid
*
* @param prodId
* @return ServerResponseEntityVoid
*/
@PostMapping("/addOrCancel")
@Operation(summary = "添加/取消收藏" , description = "传入收藏商品id,如果商品未收藏则收藏商品,已收藏则取消收藏")
@Parameter(name = "prodId", description = "商品id" , required = true)
@Operation(summary = "添加/取消收藏", description = "传入收藏商品id,如果商品未收藏则收藏商品,已收藏则取消收藏")
@Parameter(name = "prodId", description = "商品id", required = true)
public ServerResponseEntity<Void> addOrCancel(@RequestBody Long prodId) {
if (Objects.isNull(productService.getProductByProdId(prodId))) {
throw new YamiShopBindException("该商品不存在");
}
String userId = SecurityUtils.getUser().getUserId();
if (userCollectionService.count(new LambdaQueryWrapper<UserCollection>()
.eq(UserCollection::getProdId, prodId)
.eq(UserCollection::getUserId, userId)) > 0) {
.eq(UserCollection::getProdId, prodId)
.eq(UserCollection::getUserId, userId)) > 0) {
userCollectionService.remove(new LambdaQueryWrapper<UserCollection>()
.eq(UserCollection::getProdId, prodId)
.eq(UserCollection::getUserId, userId));
.eq(UserCollection::getProdId, prodId)
.eq(UserCollection::getUserId, userId));
} else {
UserCollection userCollection = new UserCollection();
userCollection.setCreateTime(new Date());
@ -86,22 +122,36 @@ public class UserCollectionController {
return ServerResponseEntity.success();
}
/**
*
* SecurityUtilsIDUserCollectionServicecountLambdaQueryWrapperID
* ServerResponseEntity
*
* @return ServerResponseEntityLong
*/
/**
*
*/
@GetMapping("count")
@Operation(summary = "查询用户收藏商品数量" , description = "查询用户收藏商品数量")
@Operation(summary = "查询用户收藏商品数量", description = "查询用户收藏商品数量")
public ServerResponseEntity<Long> findUserCollectionCount() {
String userId = SecurityUtils.getUser().getUserId();
return ServerResponseEntity.success(userCollectionService.count(new LambdaQueryWrapper<UserCollection>().eq(UserCollection::getUserId, userId)));
}
/**
*
* pageProductServicecollectionProdsIDSecurityUtils
* ServerResponseEntity
*
* @param page
* @return ServerResponseEntityIPage<ProductDto>
*/
@GetMapping("/prods")
@Operation(summary = "获取用户收藏商品列表" , description = "获取用户收藏商品列表")
@Operation(summary = "获取用户收藏商品列表", description = "获取用户收藏商品列表")
public ServerResponseEntity<IPage<ProductDto>> collectionProds(PageParam page) {
String userId = SecurityUtils.getUser().getUserId();
IPage<ProductDto> productDtoPage = productService.collectionProds(page, userId);
return ServerResponseEntity.success(productDtoPage);
}
}
}

@ -21,39 +21,58 @@ import lombok.AllArgsConstructor;
import cn.hutool.core.bean.BeanUtil;
import com.yami.shop.common.response.ServerResponseEntity;
import org.springframework.web.bind.annotation.*;
/**
*
* `Swagger`便使
*
* @author lanhai
*/
@RestController
@RequestMapping("/p/user")
@Tag(name = "用户接口")
// 使用 @AllArgsConstructor 注解,由 lombok 自动生成包含所有成员变量的构造函数,用于依赖注入
@AllArgsConstructor
public class UserController {
private final UserService userService;
// 通过构造函数注入UserService用于调用业务层方法来处理用户相关的业务逻辑
private final UserService userService;
/**
*
*/
@GetMapping("/userInfo")
@Operation(summary = "查看用户信息" , description = "根据用户IDuserId获取用户信息")
public ServerResponseEntity<UserDto> userInfo() {
String userId = SecurityUtils.getUser().getUserId();
User user = userService.getById(userId);
UserDto userDto = BeanUtil.copyProperties(user, UserDto.class);
return ServerResponseEntity.success(userDto);
}
/**
*
* IDDTO
*
* @return ServerResponseEntity<UserDto>DTO
*/
@GetMapping("/userInfo")
@Operation(summary = "查看用户信息", description = "根据用户IDuserId获取用户信息")
public ServerResponseEntity<UserDto> userInfo() {
// 获取当前登录用户的ID
String userId = SecurityUtils.getUser().getUserId();
// 调用服务层方法根据用户ID获取用户实体信息
User user = userService.getById(userId);
// 使用 hutool 工具类的方法将User对象转换为UserDto对象用于传输给前端展示的数据格式转换
UserDto userDto = BeanUtil.copyProperties(user, UserDto.class);
return ServerResponseEntity.success(userDto);
}
@PutMapping("/setUserInfo")
@Operation(summary = "设置用户信息" , description = "设置用户信息")
public ServerResponseEntity<Void> setUserInfo(@RequestBody UserInfoParam userInfoParam) {
String userId = SecurityUtils.getUser().getUserId();
User user = new User();
user.setUserId(userId);
user.setPic(userInfoParam.getAvatarUrl());
user.setNickName(userInfoParam.getNickName());
userService.updateById(user);
return ServerResponseEntity.success();
}
}
/**
*
*
*
* @param userInfoParam
* @return ServerResponseEntity<Void>
*/
@PutMapping("/setUserInfo")
@Operation(summary = "设置用户信息", description = "设置用户信息")
public ServerResponseEntity<Void> setUserInfo(@RequestBody UserInfoParam userInfoParam) {
// 获取当前登录用户的ID
String userId = SecurityUtils.getUser().getUserId();
User user = new User();
user.setUserId(userId);
user.setPic(userInfoParam.getAvatarUrl());
user.setNickName(userInfoParam.getNickName());
userService.updateById(user);
return ServerResponseEntity.success();
}
}

@ -1,6 +1,5 @@
package com.yami.shop.api.controller;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
@ -24,7 +23,9 @@ import jakarta.validation.Valid;
import java.util.Date;
/**
*
*
*
* 使
*
* @author SJL
*/
@ -34,68 +35,118 @@ import java.util.Date;
@AllArgsConstructor
public class UserRegisterController {
// 通过构造函数注入的方式引入用户服务层接口,用于调用与用户相关的业务逻辑方法,比如保存用户信息、根据条件查询用户等操作。
private final UserService userService;
// 通过构造函数注入的方式引入密码编码器,用于对用户密码进行加密处理,保证密码在存储和传输过程中的安全性。
private final PasswordEncoder passwordEncoder;
// 通过构造函数注入的方式引入令牌存储相关的类,用于处理用户登录后令牌的生成、存储以及获取相关信息等操作,实现用户身份认证和授权相关功能。
private final TokenStore tokenStore;
// 通过构造函数注入的方式引入密码管理相关的类,可能用于对用户输入的密码进行解密(如果有加密传输等情况)或者其他密码相关的管理操作。
private final PasswordManager passwordManager;
/**
* 使
*
* 访
*
* @param userRegisterParam @Valid
* @return ServerResponseEntity TokenInfoVO
*/
@PostMapping("/register")
@Operation(summary = "注册" , description = "用户注册或绑定手机号接口")
@Operation(summary = "注册", description = "用户注册或绑定手机号接口")
public ServerResponseEntity<TokenInfoVO> register(@Valid @RequestBody UserRegisterParam userRegisterParam) {
// 如果用户注册参数中的昵称nickName为空字符串则将用户名userName作为昵称使用保证昵称有值。
if (StrUtil.isBlank(userRegisterParam.getNickName())) {
userRegisterParam.setNickName(userRegisterParam.getUserName());
}
// 正在进行申请注册
// 通过用户服务层的方法,使用 MyBatis Plus 的 LambdaQueryWrapper 构建查询条件,统计数据库中昵称与传入的昵称相同的用户数量,
// 如果数量大于 0则表示该用户名昵称已被注册抛出异常提示用户无法重新注册。
if (userService.count(new LambdaQueryWrapper<User>().eq(User::getNickName, userRegisterParam.getNickName())) > 0) {
// 该用户名已注册,无法重新注册
throw new YamiShopBindException("该用户名已注册,无法重新注册");
}
// 创建当前时间对象,用于设置用户的修改时间、注册时间等属性,确保记录的时间信息准确。
Date now = new Date();
// 创建一个新的用户对象,用于填充用户注册信息并保存到数据库中。
User user = new User();
// 设置用户的修改时间为当前时间,表示该用户信息的最近一次修改时间。
user.setModifyTime(now);
// 设置用户的注册时间为当前时间,记录用户注册的具体时间点。
user.setUserRegtime(now);
// 设置用户状态为有效(这里假设状态值为 1 表示有效,可根据实际业务定义调整)。
user.setStatus(1);
// 设置用户的昵称,使用经过前面逻辑处理后的昵称值(可能是原昵称或者用户名)。
user.setNickName(userRegisterParam.getNickName());
// 设置用户的邮箱信息,从注册参数中获取并赋值给用户对象。
user.setUserMail(userRegisterParam.getUserMail());
// 通过密码管理类的方法,对用户注册参数中传入的密码进行解密操作(可能是之前加密传输过来的情况),获取解密后的密码字符串。
String decryptPassword = passwordManager.decryptPassword(userRegisterParam.getPassWord());
// 使用密码编码器对解密后的密码进行加密处理,将加密后的密码设置到用户对象中,保证密码在数据库中以加密形式存储,提高安全性。
user.setLoginPassword(passwordEncoder.encode(decryptPassword));
// 使用 Hutool 工具类生成一个简单的唯一用户 ID这里采用 UUID 的简化形式,可根据实际需求调整生成方式),作为用户的标识。
String userId = IdUtil.simpleUUID();
// 将生成的用户 ID 设置到用户对象中,确保每个用户在系统中有唯一的标识。
user.setUserId(userId);
// 调用用户服务层的保存方法,将填充好信息的用户对象保存到数据库中,完成用户注册操作。
userService.save(user);
// 2. 登录
// 模拟用户登录操作,创建一个用于存储在令牌中的用户信息对象,填充用户 ID、用户类型这里设置为普通用户通过枚举值获取、是否为管理员这里设置为否以及是否启用等信息。
UserInfoInTokenBO userInfoInTokenBO = new UserInfoInTokenBO();
userInfoInTokenBO.setUserId(user.getUserId());
userInfoInTokenBO.setSysType(SysTypeEnum.ORDINARY.value());
userInfoInTokenBO.setIsAdmin(0);
userInfoInTokenBO.setEnabled(true);
// 通过令牌存储类的方法,将用户信息存储并生成对应的令牌信息对象,然后返回包含令牌信息的响应结果给前端,方便后续用户进行身份认证和授权访问。
return ServerResponseEntity.success(tokenStore.storeAndGetVo(userInfoInTokenBO));
}
/**
*
*
*
*
* @param userPwdUpdateParam @Valid
* @return ServerResponseEntity
*/
@PutMapping("/updatePwd")
@Operation(summary = "修改密码" , description = "修改密码")
@Operation(summary = "修改密码", description = "修改密码")
public ServerResponseEntity<Void> updatePwd(@Valid @RequestBody UserRegisterParam userPwdUpdateParam) {
// 通过用户服务层的方法,使用 MyBatis Plus 的 LambdaQueryWrapper 构建查询条件,根据用户名(昵称)从数据库中查询对应的用户信息,若查询不到则抛出异常提示无法获取用户信息。
User user = userService.getOne(new LambdaQueryWrapper<User>().eq(User::getNickName, userPwdUpdateParam.getNickName()));
if (user == null) {
// 无法获取用户信息
throw new YamiShopBindException("无法获取用户信息");
}
// 通过密码管理类的方法,对用户修改密码参数中传入的新密码进行解密操作(可能是之前加密传输过来的情况),获取解密后的密码字符串。
String decryptPassword = passwordManager.decryptPassword(userPwdUpdateParam.getPassWord());
// 如果解密后的新密码为空字符串,则抛出异常提示新密码不能为空。
if (StrUtil.isBlank(decryptPassword)) {
// 新密码不能为空
throw new YamiShopBindException("新密码不能为空");
}
// 使用密码编码器对解密后的新密码进行加密处理,得到加密后的密码字符串。
String password = passwordEncoder.encode(decryptPassword);
// 如果加密后的新密码与用户原密码相同,则抛出异常提示新密码不能与原密码相同,保证密码修改的合理性。
if (StrUtil.equals(password, user.getLoginPassword())) {
// 新密码不能与原密码相同
throw new YamiShopBindException("新密码不能与原密码相同");
}
// 设置用户的修改时间为当前时间,表示此次密码修改操作的时间。
user.setModifyTime(new Date());
// 将加密后的新密码设置到用户对象中,更新用户的密码信息到数据库中,完成密码修改操作。
user.setLoginPassword(password);
// 调用用户服务层的更新方法,将更新后的用户对象信息保存到数据库中,确保密码修改生效。
userService.updateById(user);
return ServerResponseEntity.success();
}
}
}

@ -31,69 +31,105 @@ import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
/**
*
* ConfirmOrderEvent
*
*
*
* @author LGH
*/
@Component("defaultConfirmOrderListener")
@AllArgsConstructor
public class ConfirmOrderListener {
// 通过Lombok生成的构造函数注入UserAddrService用于根据用户地址ID和用户ID获取用户地址信息
// 在后续计算运费等操作中会用到该地址信息。
private final UserAddrService userAddrService;
// 注入TransportManagerService用于根据购物车商品项和用户地址计算运费是订单金额计算的重要组成部分。
private final TransportManagerService transportManagerService;
// 注入ProductService用于根据商品ID获取商品详细信息以验证商品状态以及参与订单金额计算等操作。
private final ProductService productService;
// 注入SkuService用于根据商品规格SkuID获取商品规格详细信息同样用于验证商品状态和相关金额计算等操作。
private final SkuService skuService;
/**
*
* ConfirmOrderEvent
* @EventListener 使
* @Order
*
* 1. ShopCartOrderDtoOrderParam
* 2. IDIDID
* 3. IDIDUserAddrService
* 4.
* - ProductServiceSkuService
* - 1
* -
* - TransportManagerService
* -
*
* @param event
*/
@EventListener(ConfirmOrderEvent.class)
@Order(ConfirmOrderOrder.DEFAULT)
public void defaultConfirmOrderEvent(ConfirmOrderEvent event) {
// 从确认订单事件对象中获取购物车订单信息,包含了订单整体的一些汇总信息以及后续需要更新的金额等相关属性。
ShopCartOrderDto shopCartOrderDto = event.getShopCartOrderDto();
// 从确认订单事件对象中获取订单参数其中包含了如用户选择的收货地址ID等关键信息用于后续查询地址等操作。
OrderParam orderParam = event.getOrderParam();
// 通过SecurityUtils工具类获取当前用户的ID用于后续根据用户ID来查询与之相关的用户地址等信息。
String userId = SecurityUtils.getUser().getUserId();
// 订单的地址信息
// 根据订单参数中的地址ID和当前用户ID通过UserAddrService获取用户地址信息该地址信息将用于计算运费等操作若不存在地址则返回null。
UserAddr userAddr = userAddrService.getUserAddrByUserId(orderParam.getAddrId(), userId);
// 初始化订单总金额为0.0,后续会累加每个商品项的金额得到最终的订单总金额。
double total = 0.0;
// 初始化商品总数量为0在遍历购物车商品项时会累加每个商品项的数量得到总的商品数量。
int totalCount = 0;
// 初始化运费为0.0,后续会根据每个商品项以及用户地址计算运费并累加得到总的运费。
double transfee = 0.0;
// 遍历购物车中的每个商品项,对每个商品项进行相关的信息获取、状态验证以及金额计算等操作。
for (ShopCartItemDto shopCartItem : event.getShopCartItems()) {
// 获取商品信息
// 通过ProductService根据商品ID获取商品详细信息,用于后续验证商品状态以及参与金额计算等操作。
Product product = productService.getProductByProdId(shopCartItem.getProdId());
// 获取sku信息
// 通过SkuService根据商品规格ID获取商品规格详细信息同样用于验证商品状态和参与金额计算等操作。
Sku sku = skuService.getSkuBySkuId(shopCartItem.getSkuId());
if (product == null || sku == null) {
throw new YamiShopBindException("购物车包含无法识别的商品");
}
if (product.getStatus() != 1 || sku.getStatus() != 1) {
if (product.getStatus()!= 1 || sku.getStatus()!= 1) {
throw new YamiShopBindException("商品[" + sku.getProdName() + "]已下架");
}
// 累加商品数量,将当前商品项的数量累加到总商品数量中。
totalCount = shopCartItem.getProdCount() + totalCount;
// 使用Arith工具类可能是自定义的用于精确计算的工具类将当前商品项的金额累加到总金额中得到订单的商品总金额。
total = Arith.add(shopCartItem.getProductTotalAmount(), total);
// 用户地址如果为空,则表示该用户从未设置过任何地址相关信息
if (userAddr != null) {
// 每个产品的运费相加
// 用户地址如果不为空,则表示用户设置了收货地址,需要计算该商品项对应的运费并累加到总运费中。
if (userAddr!= null) {
// 调用TransportManagerService的方法计算当前商品项对应的运费传入商品项和用户地址信息作为参数
// 并将计算得到的运费累加到总运费中,实现运费的累加计算。
transfee = Arith.add(transfee, transportManagerService.calculateTransfee(shopCartItem, userAddr));
}
// 设置商品项的实际总价(这里暂设置为商品项原本的金额,可能后续还有其他逻辑调整)。
shopCartItem.setActualTotal(shopCartItem.getProductTotalAmount());
// 更新购物车订单信息中的实际总价,将商品总金额和运费相加得到最终的实际总价。
shopCartOrderDto.setActualTotal(Arith.add(total, transfee));
// 更新购物车订单信息中的总金额,即商品总金额(不包含运费)。
shopCartOrderDto.setTotal(total);
// 更新购物车订单信息中的商品总数量。
shopCartOrderDto.setTotalCount(totalCount);
// 更新购物车订单信息中的运费。
shopCartOrderDto.setTransfee(transfee);
}
}
}
}

@ -8,6 +8,7 @@
*
*/
// 该类所属的包名表明其位于商城API相关的监听器包下主要用于监听与购物车相关的事件并执行相应的业务逻辑。
package com.yami.shop.api.listener;
import com.google.common.collect.Lists;
@ -23,25 +24,38 @@ import org.springframework.stereotype.Component;
import java.util.List;
/**
*
* ShopCartListener
* ShopCartEvent
*
* @author LGH
*/
@Component("defaultShopCartListener")
// 使用@Component注解将该类标记为Spring组件使得Spring容器能够识别并管理它。同时为该组件指定了一个名称"defaultShopCartListener"
// 在其他地方可以通过这个名称来获取该组件的实例(例如在基于名称进行依赖注入时)。
public class ShopCartListener {
/**
*
* @param event#getShopCart()
* @param event#shopCartItemDtoList
* @return
* defaultShopCartEventShopCartEvent
* 便
*
* @param event ShopCartEventevent.getShopCart()event.getShopCartItemDtoList()
* event.getShopCart()ShopCartDto
* event.getShopCartItemDtoList()ShopCartItemDtoShopCartItemDtoID
*/
@EventListener(ShopCartEvent.class)
// @EventListener注解用于标记该方法为一个事件监听器方法表明它会监听指定类型这里是ShopCartEvent类型的事件。当对应的事件在Spring应用上下文中被发布时
// 该方法就会被自动调用,从而执行相应的业务逻辑来处理这个事件。
@Order(ShopCartEventOrder.DEFAULT)
// @Order注解用于指定该事件监听器的执行顺序。在存在多个相同类型事件监听器的情况下Spring会按照这个顺序来依次调用它们确保业务逻辑按照预定的顺序执行。
// 这里的ShopCartEventOrder.DEFAULT表示该监听器的执行顺序遵循默认的设定具体的顺序数值可能在ShopCartEventOrder类中定义它决定了在整个购物车相关事件处理流程中的先后顺序。
public void defaultShopCartEvent(ShopCartEvent event) {
ShopCartDto shopCart = event.getShopCartDto();
List<ShopCartItemDto> shopCartItemDtoList = event.getShopCartItemDtoList();
// 对数据进行组装
List<ShopCartItemDiscountDto> shopCartItemDiscountDtoList = Lists.newArrayList();
// 创建一个ShopCartItemDiscountDto对象用于承载要组装的商品折扣等相关信息从类名推测可能涉及商品在购物车中的折扣相关数据处理
// 这里虽然目前只看到简单的设置操作,但可能后续会根据业务需求扩展更多与折扣计算、展示等相关的逻辑。
ShopCartItemDiscountDto shopCartItemDiscountDto = new ShopCartItemDiscountDto();
shopCartItemDiscountDto.setShopCartItems(shopCartItemDtoList);
@ -49,5 +63,4 @@ public class ShopCartListener {
shopCart.setShopCartItemDiscounts(shopCartItemDiscountDtoList);
}
}
}

@ -38,6 +38,8 @@ import java.util.*;
/**
*
*
*
*
* @author LGH
*/
@ -45,136 +47,162 @@ import java.util.*;
@AllArgsConstructor
public class SubmitOrderListener {
// 用于处理用户地址相关的订单服务,比如保存用户地址信息等操作
private final UserAddrOrderService userAddrOrderService;
// 用于处理商品相关业务逻辑的服务,例如获取商品信息等
private final ProductService productService;
// 用于处理商品库存单元SKU相关业务逻辑的服务比如获取SKU信息等
private final SkuService skuService;
// 雪花算法工具类,用于生成唯一的订单编号等
private final Snowflake snowflake;
// 订单商品项数据访问层接口,用于操作订单商品项相关的数据,比如插入、更新等操作
private final OrderItemMapper orderItemMapper;
// SKU数据访问层接口用于操作SKU相关的数据比如更新库存等操作
private final SkuMapper skuMapper;
// 商品数据访问层接口,用于操作商品相关的数据,比如更新库存等操作
private final ProductMapper productMapper;
// 订单数据访问层接口,用于操作订单相关的数据,比如插入、更新订单信息等操作
private final OrderMapper orderMapper;
// 订单结算数据访问层接口,用于操作订单结算相关的数据,比如插入订单结算记录等操作
private final OrderSettlementMapper orderSettlementMapper;
// 购物车数据访问层接口,用于操作购物车相关的数据,比如删除购物车中的商品等操作
private final BasketMapper basketMapper;
/**
*
*
* SubmitOrderEvent
*
*
* @param event
*/
@EventListener(SubmitOrderEvent.class)
@Order(SubmitOrderOrder.DEFAULT)
public void defaultSubmitOrderListener(SubmitOrderEvent event) {
// 获取当前时间,用于后续设置创建时间等时间相关的属性
Date now = new Date();
// 获取当前登录用户的ID用于关联订单与用户等操作
String userId = SecurityUtils.getUser().getUserId();
// 获取合并后的订单信息对象,包含了多个店铺的订单以及相关的商品、地址等信息
ShopCartOrderMergerDto mergerOrder = event.getMergerOrder();
// 订单商品参数
// 订单商品参数,即包含了各个店铺下的订单商品信息列表
List<ShopCartOrderDto> shopCartOrders = mergerOrder.getShopCartOrders();
// 用于存储需要删除的购物车商品ID列表后续会根据这个列表删除购物车中的对应商品
List<Long> basketIds = new ArrayList<>();
// 商品skuId为key 需要更新的sku为value的map
// 以商品SKU的ID为键需要更新的SKU对象为值的映射表用于记录商品SKU库存等信息的变化方便后续统一更新库存
Map<Long, Sku> skuStocksMap = new HashMap<>(16);
// 商品productId为key 需要更新的product为value的map
// 以商品的ID为键需要更新的商品对象为值的映射表用于记录商品库存等信息的变化方便后续统一更新库存
Map<Long, Product> prodStocksMap = new HashMap<>(16);
// 把订单地址保存到数据库
// 通过属性拷贝将合并订单中的用户地址信息复制到用户地址订单对象中
UserAddrOrder userAddrOrder = BeanUtil.copyProperties(mergerOrder.getUserAddr(), UserAddrOrder.class);
if (userAddrOrder == null) {
// 如果地址信息为空,则抛出异常,提示用户需要填写收货地址
throw new YamiShopBindException("请填写收货地址");
}
userAddrOrder.setUserId(userId);
userAddrOrder.setCreateTime(now);
// 调用服务层方法保存用户地址订单信息到数据库中
userAddrOrderService.save(userAddrOrder);
// 订单地址id
// 获取保存后的订单地址ID后续订单信息会关联该地址ID
Long addrOrderId = userAddrOrder.getAddrOrderId();
// 每个店铺生成一个订单
// 每个店铺生成一个订单,遍历各个店铺的订单商品信息列表进行订单创建等操作
for (ShopCartOrderDto shopCartOrderDto : shopCartOrders) {
createOrder(event, now, userId, basketIds, skuStocksMap, prodStocksMap, addrOrderId, shopCartOrderDto);
}
// 删除购物车的商品信息
// 如果存在需要删除的购物车商品ID则调用购物车数据访问层方法删除购物车中对应商品
if (!basketIds.isEmpty()) {
basketMapper.deleteShopCartItemsByBasketIds(userId, basketIds);
}
// 更新sku库存
// 遍历SKU库存映射表更新每个SKU的库存信息
skuStocksMap.forEach((key, sku) -> {
// 调用SKU数据访问层的方法更新库存如果更新的行数为0表示库存更新失败可能库存不足等原因
if (skuMapper.updateStocks(sku) == 0) {
// 如果库存更新失败清除对应SKU的缓存通过SKU服务层方法并抛出库存不足的异常提示用户对应商品库存不足
skuService.removeSkuCacheBySkuId(key, sku.getProdId());
throw new YamiShopBindException("商品:[" + sku.getProdName() + "]库存不足");
}
});
// 更新商品库存
// 遍历商品库存映射表,更新每个商品库存信息
prodStocksMap.forEach((prodId, prod) -> {
// 调用商品数据访问层的方法更新库存如果更新的行数为0表示库存更新失败可能库存不足等原因
if (productMapper.updateStocks(prod) == 0) {
// 如果库存更新失败,清除对应商品的缓存(通过商品服务层方法),并抛出库存不足的异常,提示用户对应商品库存不足
productService.removeProductCacheByProdId(prodId);
throw new YamiShopBindException("商品:[" + prod.getProdName() + "]库存不足");
}
});
}
/**
*
*
* ID
*
* @param event
* @param now
* @param userId ID
* @param basketIds ID
* @param skuStocksMap SKUIDSKUSKU
* @param prodStocksMap ID
* @param addrOrderId ID
* @param shopCartOrderDto
*/
private void createOrder(SubmitOrderEvent event, Date now, String userId, List<Long> basketIds, Map<Long, Sku> skuStocksMap, Map<Long, Product> prodStocksMap, Long addrOrderId, ShopCartOrderDto shopCartOrderDto) {
// 使用雪花算法生成的订单号
// 使用雪花算法生成唯一的订单,并转换为字符串类型
String orderNumber = String.valueOf(snowflake.nextId());
shopCartOrderDto.setOrderNumber(orderNumber);
// 获取店铺的ID用于关联订单与店铺
Long shopId = shopCartOrderDto.getShopId();
// 订单商品名称
// 用于构建订单商品名称字符串,将各个商品名称拼接起来,方便展示订单包含的商品信息
StringBuilder orderProdName = new StringBuilder(100);
// 用于存储该订单下的各个订单商品项信息,后续会将这些商品项关联到订单对象中
List<OrderItem> orderItems = new ArrayList<>();
// 获取店铺订单下的商品折扣信息列表,每个商品折扣信息对象包含了多个商品项信息
List<ShopCartItemDiscountDto> shopCartItemDiscounts = shopCartOrderDto.getShopCartItemDiscounts();
for (ShopCartItemDiscountDto shopCartItemDiscount : shopCartItemDiscounts) {
// 获取每个商品折扣信息对象下的商品项信息列表
List<ShopCartItemDto> shopCartItems = shopCartItemDiscount.getShopCartItems();
for (ShopCartItemDto shopCartItem : shopCartItems) {
// 检查并获取商品的SKU信息同时更新SKU库存映射表如果需要并处理库存不足等异常情况
Sku sku = checkAndGetSku(shopCartItem.getSkuId(), shopCartItem, skuStocksMap);
// 检查并获取商品信息,同时更新商品库存映射表(如果需要),并处理库存不足等异常情况
Product product = checkAndGetProd(shopCartItem.getProdId(), shopCartItem, prodStocksMap);
// 根据获取到的商品、SKU以及其他相关信息构建订单商品项对象
OrderItem orderItem = getOrderItem(now, userId, orderNumber, shopId, orderProdName, shopCartItem, sku, product);
orderItems.add(orderItem);
if (shopCartItem.getBasketId() != null && shopCartItem.getBasketId() != 0) {
if (shopCartItem.getBasketId()!= null && shopCartItem.getBasketId()!= 0) {
basketIds.add(shopCartItem.getBasketId());
}
}
}
// 对订单商品名称字符串进行处理,截取合适的长度,并去除末尾多余的逗号(如果有)
orderProdName.subSequence(0, Math.min(orderProdName.length() - 1, 100));
if (orderProdName.lastIndexOf(Constant.COMMA) == orderProdName.length() - 1) {
orderProdName.deleteCharAt(orderProdName.length() - 1);
}
// 订单信息
// 根据订单相关信息构建订单对象包括店铺ID、订单编号、商品名称、用户ID、金额等各种属性
com.yami.shop.bean.model.Order order = getOrder(now, userId, addrOrderId, shopCartOrderDto, orderNumber, shopId, orderProdName, orderItems);
event.getOrders().add(order);
// 插入订单结算表
// 插入订单结算数据构建订单结算对象并设置相关属性如用户ID、是否结算、创建时间、订单编号、支付金额等然后插入到数据库中
OrderSettlement orderSettlement = new OrderSettlement();
orderSettlement.setUserId(userId);
orderSettlement.setIsClearing(0);
@ -186,19 +214,35 @@ public class SubmitOrderListener {
orderSettlementMapper.insert(orderSettlement);
}
/**
*
* IDIDID
* IDID
*
* @param now
* @param userId IDID
* @param addrOrderId IDID
* @param shopCartOrderDto
* @param orderNumber
* @param shopId IDID
* @param orderProdName
* @param orderItems
* @return
*/
private com.yami.shop.bean.model.Order getOrder(Date now, String userId, Long addrOrderId, ShopCartOrderDto shopCartOrderDto, String orderNumber, Long shopId, StringBuilder orderProdName, List<OrderItem> orderItems) {
com.yami.shop.bean.model.Order order = new com.yami.shop.bean.model.Order();
order.setShopId(shopId);
order.setOrderNumber(orderNumber);
// 订单商品名称
// 设置订单商品名称属性
order.setProdName(orderProdName.toString());
// 用户id
// 设置用户ID属性
order.setUserId(userId);
// 商品总额
// 设置商品总额属性,从店铺订单信息中获取
order.setTotal(shopCartOrderDto.getTotal());
// 实际总额
// 设置实际总额属性,从店铺订单信息中获取
order.setActualTotal(shopCartOrderDto.getActualTotal());
// 设置订单状态为未支付状态(通过枚举值表示)
order.setStatus(OrderStatus.UNPAY.value());
order.setUpdateTime(now);
order.setCreateTime(now);
@ -206,6 +250,7 @@ public class SubmitOrderListener {
order.setDeleteStatus(0);
order.setProductNums(shopCartOrderDto.getTotalCount());
order.setAddrOrderId(addrOrderId);
// 计算并设置优惠金额属性,通过商品总额、运费和实际总额进行计算
order.setReduceAmount(Arith.sub(Arith.add(shopCartOrderDto.getTotal(), shopCartOrderDto.getTransfee()), shopCartOrderDto.getActualTotal()));
order.setFreightAmount(shopCartOrderDto.getTransfee());
order.setRemarks(shopCartOrderDto.getRemarks());
@ -214,90 +259,14 @@ public class SubmitOrderListener {
return order;
}
private OrderItem getOrderItem(Date now, String userId, String orderNumber, Long shopId, StringBuilder orderProdName, ShopCartItemDto shopCartItem, Sku sku, Product product) {
OrderItem orderItem = new OrderItem();
orderItem.setShopId(shopId);
orderItem.setOrderNumber(orderNumber);
orderItem.setProdId(sku.getProdId());
orderItem.setSkuId(sku.getSkuId());
orderItem.setSkuName(sku.getSkuName());
orderItem.setProdCount(shopCartItem.getProdCount());
orderItem.setProdName(sku.getProdName());
orderItem.setPic(StrUtil.isBlank(sku.getPic()) ? product.getPic() : sku.getPic());
orderItem.setPrice(shopCartItem.getPrice());
orderItem.setUserId(userId);
orderItem.setProductTotalAmount(shopCartItem.getProductTotalAmount());
orderItem.setRecTime(now);
orderItem.setCommSts(0);
orderItem.setBasketDate(shopCartItem.getBasketDate());
orderProdName.append(orderItem.getProdName()).append(",");
//推广员卡号
orderItem.setDistributionCardNo(shopCartItem.getDistributionCardNo());
return orderItem;
}
@SuppressWarnings({"Duplicates"})
private Product checkAndGetProd(Long prodId, ShopCartItemDto shopCartItem, Map<Long, Product> prodStocksMap) {
Product product = productService.getProductByProdId(prodId);
if (product == null) {
throw new YamiShopBindException("购物车包含无法识别的商品");
}
if (product.getStatus() != 1) {
throw new YamiShopBindException("商品[" + product.getProdName() + "]已下架");
}
// 商品需要改变的库存
Product mapProduct = prodStocksMap.get(prodId);
if (mapProduct == null) {
mapProduct = new Product();
mapProduct.setTotalStocks(0);
mapProduct.setProdId(prodId);
mapProduct.setProdName(product.getProdName());
}
if (product.getTotalStocks() != -1) {
mapProduct.setTotalStocks(mapProduct.getTotalStocks() + shopCartItem.getProdCount());
prodStocksMap.put(product.getProdId(), mapProduct);
}
// -1为无限库存
if (product.getTotalStocks() != -1 && mapProduct.getTotalStocks() > product.getTotalStocks()) {
throw new YamiShopBindException("商品:[" + product.getProdName() + "]库存不足");
}
return product;
}
@SuppressWarnings({"Duplicates"})
private Sku checkAndGetSku(Long skuId, ShopCartItemDto shopCartItem, Map<Long, Sku> skuStocksMap) {
// 获取sku信息
Sku sku = skuService.getSkuBySkuId(skuId);
if (sku == null) {
throw new YamiShopBindException("购物车包含无法识别的商品");
}
if (sku.getStatus() != 1) {
throw new YamiShopBindException("商品[" + sku.getProdName() + "]已下架");
}
// -1为无限库存
if (sku.getStocks() != -1 && shopCartItem.getProdCount() > sku.getStocks()) {
throw new YamiShopBindException("商品:[" + sku.getProdName() + "]库存不足");
}
if (sku.getStocks() != -1) {
Sku mapSku = new Sku();
mapSku.setProdId(sku.getProdId());
// 这里的库存是改变的库存
mapSku.setStocks(shopCartItem.getProdCount());
mapSku.setSkuId(sku.getSkuId());
mapSku.setProdName(sku.getProdName());
skuStocksMap.put(sku.getSkuId(), mapSku);
}
return sku;
}
}
/**
*
* IDIDSKU
* IDIDSKU IDSKUID
*
* @param now
* @param userId IDID
* @param orderNumber
* @param shopId IDID
* @param orderProdName
* @param shopCartItem

@ -22,70 +22,77 @@ import cn.hutool.core.collection.CollectionUtil;
/**
* @author lanhai
* `SmsInfoContext` `ThreadLocal` 线线 `SmsInfoBo`
* 便线线
* 线线访
* 便线使线
*/
public class SmsInfoContext {
/**
* The request holder.
* `ThreadLocal` `smsInfoHolder`线 `List<SmsInfoBo>`
* `ThreadLocal` 线线 `smsInfoHolder` 访
* 线线
*/
private static ThreadLocal<List<SmsInfoBo>> smsInfoHolder = new ThreadLocal<List<SmsInfoBo>>();
/**
* The request holder.
* `ThreadLocal` `smsInfoHolder`线 `List<SmsInfoBo>`
* `ThreadLocal` Java线线 `smsInfoHolder` 访
* 线线Web线
*/
private static ThreadLocal<List<SmsInfoBo>> smsInfoHolder = new ThreadLocal<List<SmsInfoBo>>();
/**
* 线
* `smsInfoHolder.get()` 线使 `CollectionUtil.isEmpty(list)`
* 线 `ArrayList`使
* 线 `smsInfoHolder` 便
*
* @return 线线
*/
public static List<SmsInfoBo> get() {
List<SmsInfoBo> list = smsInfoHolder.get();
if (CollectionUtil.isEmpty(list)) {
return new ArrayList<>();
}
return smsInfoHolder.get();
}
/**
* 线
* `smsInfoHolder.get()` 线 `ThreadLocal` 线
* 使 `CollectionUtil.isEmpty(list)` `CollectionUtil.isEmpty` `hutool` 便 `null`
* 线 `ArrayList`使
* 线 `smsInfoHolder` 便使
*
* @return 线线
*/
public static List<SmsInfoBo> get() {
List<SmsInfoBo> list = smsInfoHolder.get();
if (CollectionUtil.isEmpty(list)) {
return new ArrayList<>();
}
return smsInfoHolder.get();
}
/**
* 线
* `List<SmsInfoBo>` `smsInfoBos` `smsInfoHolder.set(smsInfoBos)` 线 `ThreadLocal`
* 线 `get` 便线使
*
* @param smsInfoBos 线
*/
public static void set(List<SmsInfoBo> smsInfoBos) {
smsInfoHolder.set(smsInfoBos);
}
/**
* 线
* `List<SmsInfoBo>` `smsInfoBos`线
* `smsInfoHolder.set(smsInfoBos)` 线 `ThreadLocal` `ThreadLocal` `set` 线
* 线 `get` 便线使
*
* @param smsInfoBos 线
*/
public static void set(List<SmsInfoBo> smsInfoBos) {
smsInfoHolder.set(smsInfoBos);
}
/**
* 线`SmsInfoBo`
* `smsInfoHolder.get()` 线使 `CollectionUtil.isEmpty(smsInfoBos)`
* 线 `ArrayList` `smsInfoBo`
* `smsInfoHolder.set(smsInfoBos)` 线 `ThreadLocal` 线便
*
* @param smsInfoBo 线
*/
public static void put(SmsInfoBo smsInfoBo) {
List<SmsInfoBo> smsInfoBos = smsInfoHolder.get();
if (CollectionUtil.isEmpty(smsInfoBos)) {
smsInfoBos = new ArrayList<>();
}
smsInfoBos.add(smsInfoBo);
smsInfoHolder.set(smsInfoBos);
}
/**
* 线`SmsInfoBo`
* `smsInfoHolder.get()` 线线便
* 使 `CollectionUtil.isEmpty(smsInfoBos)` `hutool`
* 线 `ArrayList`
* `smsInfoBo` `smsInfoBos.add(smsInfoBo)`
* `smsInfoHolder.set(smsInfoBos)` 线 `ThreadLocal` 线便
*
* @param smsInfoBo 线
*/
public static void put(SmsInfoBo smsInfoBo) {
List<SmsInfoBo> smsInfoBos = smsInfoHolder.get();
if (CollectionUtil.isEmpty(smsInfoBos)) {
smsInfoBos = new ArrayList<>();
}
smsInfoBos.add(smsInfoBo);
smsInfoHolder.set(smsInfoBos);
}
/**
* 线
* `smsInfoHolder.get()` 线 `null` `null` `smsInfoHolder.remove()` 线 `ThreadLocal`
* 线 `get` 线
*/
public static void clean() {
if (smsInfoHolder.get()!= null) {
smsInfoHolder.remove();
}
}
/**
* 线
* `smsInfoHolder.get()` 线 `null` `ThreadLocal`
* `null` `smsInfoHolder.remove()` 线 `ThreadLocal` `ThreadLocal` `remove` 线
* 线 `get` 线
* 线线
*/
public static void clean() {
if (smsInfoHolder.get()!= null) {
smsInfoHolder.remove();
}
}
}

@ -11,28 +11,37 @@
package com.yami.shop.bean.order;
/**
*
*
* 便
*
* @author LGH
*/
public interface ConfirmOrderOrder {
/**
*
* 0
*
*/
int DEFAULT = 0;
/**
* DEFAULT
* 100 DEFAULT
*
*
*/
int DISCOUNT = 100;
/**
* DISCOUNT
* 使200 DISCOUNT
* 使
*
*/
int COUPON = 200;
/**
* COUPON
* 300 COUPON
* 使
* 使
*/
int DISTRIBUTION = 300;
}
}

@ -11,18 +11,24 @@
package com.yami.shop.bean.order;
/**
*
* `ShopCartEventOrder`
*
*
*
* @author LGH
*/
public interface ShopCartEventOrder {
/**
*
* `0`
*
*/
int DEFAULT = 0;
/**
* DEFAULT
* `DEFAULT``100`
*
*
*/
int DISCOUNT = 100;
}
}

@ -8,26 +8,33 @@
*
*/
// 该类所属的包名表明其位于商城相关的Java Bean的订单order包下这个接口主要用于定义提交订单事件的先后顺序相关的常量以便在处理提交订单业务逻辑中
// 根据不同的业务场景(如是否有优惠券、折扣等活动参与)来确定各个操作环节的执行顺序。
package com.yami.shop.bean.order;
/**
*
* SubmitOrderOrder
* 便
*
* @author LGH
*/
public interface SubmitOrderOrder {
/**
*
* DEFAULT0
*
*/
int DEFAULT = 0;
/**
* DEFAULT
* DISCOUNT100DEFAULT
*
*/
int DISCOUNT = 100;
/**
* DEFAULT
* COUPON200DISCOUNT
*
*/
int COUPON = 200;
}
}

@ -8,52 +8,77 @@
*
*/
// 该类所属的包名表明其位于商城相关的Java Bean的参数param包下通常用于封装前端传递给后端的参数信息方便在业务逻辑中对这些参数进行统一的验证、获取和使用。
package com.yami.shop.bean.param;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
/**
* DeliveryOrderParamJava Bean便
* 使Jakarta Validation@NotBlank使Swagger@SchemaAPI
*
* @author lanhai
*/
public class DeliveryOrderParam {
@NotBlank(message="订单号不能为空")
@Schema(description = "订单号" ,required=true)
private String orderNumber;
@NotBlank(message="快递公司id不能为空")
@Schema(description = "快递公司" ,required=true)
private Long dvyId;
@NotBlank(message="物流单号不能为空")
@Schema(description = "物流单号" ,required=true)
private String dvyFlowId;
// 使用@NotBlank注解对orderNumber字段进行约束表明该字段不能为空字符串当验证不通过时即传入的订单号为空字符串会按照message属性中定义的提示信息“订单号不能为空”进行错误提示。
// 同时使用@Schema注解对该字段在Swagger生成的API文档中进行描述说明其代表“订单号”并且是必填项required=true方便接口使用者了解该参数的含义和要求。
@NotBlank(message = "订单号不能为空")
@Schema(description = "订单号", required = true)
private String orderNumber;
// 类似地对dvyId字段使用@NotBlank注解进行约束要求该字段不能为空验证不通过时提示“快递公司id不能为空”。
// 通过@Schema注解在API文档中描述其代表“快递公司”且为必填项这里的Long类型可能对应着快递公司在系统中的唯一标识符用于明确指定发货所使用的快递公司。
@NotBlank(message = "快递公司id不能为空")
@Schema(description = "快递公司", required = true)
private Long dvyId;
public Long getDvyId() {
return dvyId;
}
// 对dvyFlowId字段同样使用@NotBlank注解确保其不能为空不符合要求时提示“物流单号不能为空”并通过@Schema注解在API文档里说明其代表“物流单号”且是必填项
// 物流单号用于追踪货物运输的具体情况,是发货流程中必不可少的关键信息。
@NotBlank(message = "物流单号不能为空")
@Schema(description = "物流单号", required = true)
private String dvyFlowId;
public void setDvyId(Long dvyId) {
this.dvyId = dvyId;
}
/**
* IDGetter便使ID
*/
public Long getDvyId() {
return dvyId;
}
public String getDvyFlowId() {
return dvyFlowId;
}
/**
* IDSetterdvyIdID便使
*/
public void setDvyId(Long dvyId) {
this.dvyId = dvyId;
}
public void setDvyFlowId(String dvyFlowId) {
this.dvyFlowId = dvyFlowId;
}
/**
* Getter便
*/
public String getDvyFlowId() {
return dvyFlowId;
}
public String getOrderNumber() {
return orderNumber;
}
/**
* SetterdvyFlowId便
*/
public void setDvyFlowId(String dvyFlowId) {
this.dvyFlowId = dvyFlowId;
}
public void setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}
/**
* Getter
*/
public String getOrderNumber() {
return orderNumber;
}
}
/**
* SetterorderNumber便
*/
public void setOrderNumber(String orderNumber) {
this.orderNumber = orderNumber;
}
}

@ -16,40 +16,60 @@ import org.springframework.format.annotation.DateTimeFormat;
import java.util.Date;
/**
*
* ID便
*
* @author lanhai
*/
@Data
// 使用 @Data 注解,由 lombok 自动生成常用的方法如Getter、Setter、toString、equals、hashCode等方法
public class OrderParam {
/**
* id
* id
*
*/
private Long shopId;
/**
* -1 0: 1: 2: 3:
*
*
* - -1
* - 0
* - 1
* - 2
* - 3
*
*/
private Integer status;
/**
* 10
*
*
* - 1
* - 0
*
*/
private Integer isPayed;
/**
*
*
*
*/
private String orderNumber;
/**
*
*
*
* @DateTimeFormat "yyyy-MM-dd HH:mm:ss"
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date startTime;
/**
*
*
*
* @DateTimeFormat "yyyy-MM-dd HH:mm:ss"
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date endTime;
}
}

@ -20,91 +20,114 @@ import jakarta.validation.constraints.Size;
import java.util.List;
/**
* `ProductParam`
*
* `lombok``@Data``getter``setter``toString``hashCode``equals`便使
*
*
* @author lanhai
*/
@Data
public class ProductParam {
/**
* ID
* IDID
* `null`
*/
private Long prodId;
/**
*
*
*
*/
private Integer status;
/**
*
*
* `@NotBlank``@Size``200`
*
*/
@NotBlank(message = "商品名称不能为空")
@Size(max = 200, message = "商品名称长度应该小于{max}")
private String prodName;
/**
*
*
* `@NotNull``null`
*/
@NotNull(message = "请输入商品价格")
private Double price;
/**
*
*
* `@NotNull``null`便
*/
@NotNull(message = "请输入商品原价")
private Double oriPrice;
/**
*
*
* `@NotNull`
*/
@NotNull(message = "请输入商品库存")
private Integer totalStocks;
/**
* ,
* 便
* `@Size``500`
*/
@Size(max = 500, message = "商品卖点长度应该小于{max}")
private String brief;
/**
* `@NotBlank`
*
*/
@NotBlank(message = "请选择图片上传")
private String pic;
/**
*
* `@NotBlank`
* 便
*/
@NotBlank(message = "请选择图片上传")
private String imgs;
/**
*
* 便
* `@NotNull`使便
*/
@NotNull(message = "请选择商品分类")
private Long categoryId;
/**
* sku
* sku`List<Sku>`
* `Sku`便使
*/
private List<Sku> skuList;
/**
* content
* content 使
*
*/
private String content;
/**
*
* `Product.DeliveryModeVO`
*
*/
private Product.DeliveryModeVO deliveryModeVo;
/**
* id
* id
* id
*/
private Long deliveryTemplateId;
/**
*
* 便
* 便
*/
private List<Long> tagList;
}
}

@ -8,215 +8,301 @@
*
*/
// 该类所属的包名表明其位于商城相关的Java Bean的参数param包下主要用于封装与店铺详情相关的参数信息这些参数通常是前端传递给后端以便后端进行店铺详情相关的业务处理操作例如店铺信息的新增、修改等功能。
package com.yami.shop.bean.param;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
/**
* ShopDetailParamJava Bean使Jakarta Validation
* GetterSetter便便使
*
* @author lanhai
*/
public class ShopDetailParam {
private Long shopId;
// 店铺的唯一标识符在整个系统中用于区分不同的店铺在涉及店铺相关的数据库操作如查询、更新、删除等以及业务逻辑处理时依靠这个ID来精准定位到具体的店铺实体。
private Long shopId;
/**
* (())
* @NotBlank
* @Size50
*/
@NotBlank(message="店铺名称不能为空")
@Size(max = 50,message = "商品名称长度应该小于{max}")
@NotBlank(message = "店铺名称不能为空")
@Size(max = 50, message = "商品名称长度应该小于{max}")
private String shopName;
/**
* ()
* @Size200
*/
@Size(max = 200,message = "店铺简介长度应该小于{max}")
@Size(max = 200, message = "店铺简介长度应该小于{max}")
private String intro;
/**
* ()
* @Size50便
*/
@Size(max = 50,message = "店铺公告长度应该小于{max}")
@Size(max = 50, message = "店铺公告长度应该小于{max}")
private String shopNotice;
/**
*
* @NotBlank@Size20便
*/
@NotBlank(message="店铺联系电话不能为空")
@Size(max = 20,message = "店铺公告长度应该小于{max}")
@NotBlank(message = "店铺联系电话不能为空")
@Size(max = 20, message = "店铺公告长度应该小于{max}")
private String tel;
/**
*
* @NotBlank@Size100便便
*/
@NotBlank(message="店铺详细地址不能为空")
@Size(max = 100,message = "店铺公告长度应该小于{max}")
@NotBlank(message = "店铺详细地址不能为空")
@Size(max = 100, message = "店铺公告长度应该小于{max}")
private String shopAddress;
/**
*
* @NotBlank10便
*/
@NotBlank(message="店铺所在省份不能为空")
@Size(max = 10,message = "店铺所在省份长度应该小于{max}")
@NotBlank(message = "店铺所在省份不能为空")
@Size(max = 10, message = "店铺所在省份长度应该小于{max}")
private String province;
/**
*
* @NotBlank10
*/
@NotBlank(message="店铺所在城市不能为空")
@Size(max = 10,message = "店铺所在城市长度应该小于{max}")
@NotBlank(message = "店铺所在城市不能为空")
@Size(max = 10, message = "店铺所在城市长度应该小于{max}")
private String city;
/**
*
* @NotBlank@Size10便
*/
@NotBlank(message="店铺所在区域不能为空")
@Size(max = 10,message = "店铺所在省份区域长度应该小于{max}")
@NotBlank(message = "店铺所在区域不能为空")
@Size(max = 10, message = "店铺所在省份区域长度应该小于{max}")
private String area;
/**
*
* 使@NotBlank20@Size
*/
@NotBlank(message="店铺省市区代码不能为空")
@Size(max = 20,message = "店铺省市区代码长度应该小于{max}")
@NotBlank(message = "店铺省市区代码不能为空")
@Size(max = 20, message = "店铺省市区代码长度应该小于{max}")
private String pcaCode;
/**
* logo()
* logo@NotBlank@Size200Base64logo
*/
@NotBlank(message="店铺logo不能为空")
@Size(max = 200,message = "店铺logo长度应该小于{max}")
@NotBlank(message = "店铺logo不能为空")
@Size(max = 200, message = "店铺logo长度应该小于{max}")
private String shopLogo;
/**
*
* @Size1000便
*/
@Size(max = 1000,message = "店铺相册长度应该小于{max}")
@Size(max = 1000, message = "店铺相册长度应该小于{max}")
private String shopPhotos;
/**
* ()
* @NotBlank@Size100便
*/
@NotBlank(message="每天营业时间段不能为空")
@Size(max = 100,message = "每天营业时间段长度应该小于{max}")
@NotBlank(message = "每天营业时间段不能为空")
@Size(max = 100, message = "每天营业时间段长度应该小于{max}")
private String openTime;
/**
* (-1: 0: 1:)
/**
* -101便
*/
private Integer shopStatus;
public String getShopName() {
return shopName;
}
public void setShopName(String shopName) {
this.shopName = shopName;
}
/**
* Getter便使
*/
public String getShopName() {
return shopName;
}
public String getIntro() {
return intro;
}
/**
* SettershopName便使
*/
public void setShopName(String shopName) {
this.shopName = shopName;
}
public void setIntro(String intro) {
this.intro = intro;
}
/**
* Getter
*/
public String getIntro() {
return intro;
}
public String getShopNotice() {
return shopNotice;
}
/**
* Setterintro便使
*/
public void setIntro(String intro) {
this.intro = intro;
}
public void setShopNotice(String shopNotice) {
this.shopNotice = shopNotice;
}
/**
* Getter便
*/
public String getShopNotice() {
return shopNotice;
}
public String getTel() {
return tel;
}
/**
* SettershopNotice便使
*/
public void setShopNotice(String shopNotice) {
this.shopNotice = shopNotice;
}
public void setTel(String tel) {
this.tel = tel;
}
/**
* Getter
*/
public String getTel() {
return tel;
}
public String getShopAddress() {
return shopAddress;
}
/**
* Settertel便使
*/
public void setTel(String tel) {
this.tel = tel;
}
public void setShopAddress(String shopAddress) {
this.shopAddress = shopAddress;
}
/**
* Getter便
*/
public String getShopAddress() {
return shopAddress;
}
public String getProvince() {
return province;
}
/**
* SettershopAddress便使
*/
public void setShopAddress(String shopAddress) {
this.shopAddress = shopAddress;
}
public void setProvince(String province) {
this.province = province;
}
/**
* Getter
*/
public String getProvince() {
return province;
}
public String getCity() {
return city;
}
/**
* Setterprovince便使
*/
public void setProvince(String province) {
this.province = province;
}
public void setCity(String city) {
this.city = city;
}
/**
* Getter便
*/
public String getCity() {
return city;
}
public String getArea() {
return area;
}
/**
* Settercity便使
*/
public void setCity(String city) {
this.city = city;
}
public void setArea(String area) {
this.area = area;
}
/**
* Getter
*/
public String getArea() {
return area;
}
public String getPcaCode() {
return pcaCode;
}
/**
* Setterarea便使
*/
public void setArea(String area) {
this.area = area;
}
public void setPcaCode(String pcaCode) {
this.pcaCode = pcaCode;
}
/**
* Getter便
*/
public String getPcaCode() {
return pcaCode;
}
public String getShopLogo() {
return shopLogo;
}
/**
* SetterpcaCode便使
*/
public void setPcaCode(String pcaCode) {
this.pcaCode = pcaCode;
}
public void setShopLogo(String shopLogo) {
this.shopLogo = shopLogo;
}
/**
* logoGetterlogologologo
*/
public String getShopLogo() {
return shopLogo;
}
public String getShopPhotos() {
return shopPhotos;
}
/**
* logoSetterlogoshopLogologologo便使
*/
public void setShopLogo(String shopLogo) {
this.shopLogo = shopLogo;
}
public void setShopPhotos(String shopPhotos) {
this.shopPhotos = shopPhotos;
}
/**
* Getter便
*/
public String getShopPhotos() {
return shopPhotos;
}
public String getOpenTime() {
return openTime;
}
/**
* SettershopPhotos便使
*/
public void setShopPhotos(String shopPhotos) {
this.shopPhotos = shopPhotos;
}
public void setOpenTime(String openTime) {
this.openTime = openTime;
}
/**
* Getter
*/
public String getOpenTime() {
return openTime;
}
public Integer getShopStatus() {
return shopStatus;
}
/**
* SetteropenTime便使
*/
public void setOpenTime(String openTime) {
this.openTime = openTime;
}
public void setShopStatus(Integer shopStatus) {
this.shopStatus = shopStatus;
}
/**
* Getter便
*/
public Integer getShopStatus() {
return shopStatus;
}
public Long getShopId() {
return shopId;
}
/**
* SettershopStatus便
*/
public void setShopStatus(Integer shopStatus) {
this.shopStatus = shopStatus;
}
public void setShopId(Long shopId) {
this.shopId = shopId;
}
/**
* IDGetterIDID
*/
public Long getShopId() {
return shopId;
}
}
/**
* IDSetterIDshopIdIDID便使
*/
public void setShopId(Long shopId) {
this.shopId = shop

@ -14,36 +14,86 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
/**
*
* 便
* `io.swagger.v3.oas.annotations.media.Schema``Swagger`便
*
* @author lh
*/
@Data
// 使用 @Data 注解,由 lombok 自动生成常用的方法如Getter、Setter、toString、equals、hashCode等方法
@Schema(description = "设置用户信息")
public class UserRegisterParam {
@Schema(description = "密码" )
private String passWord;
/**
*
* 使
* @Schema `Swagger`
*/
@Schema(description = "密码")
private String passWord;
@Schema(description = "邮箱" )
private String userMail;
/**
*
*
* @Schema `Swagger`
*/
@Schema(description = "邮箱")
private String userMail;
@Schema(description = "昵称" )
private String nickName;
/**
*
* 使便
* @Schema `Swagger`
*/
@Schema(description = "昵称")
private String nickName;
@Schema(description = "用户名" )
private String userName;
/**
*
* 使
* @Schema `Swagger`
*/
@Schema(description = "用户名")
private String userName;
@Schema(description = "手机号" )
private String mobile;
/**
*
* 便
* @Schema `Swagger`
*/
@Schema(description = "手机号")
private String mobile;
@Schema(description = "头像" )
private String img;
/**
*
*
* @Schema `Swagger`
*/
@Schema(description = "头像")
private String img;
@Schema(description = "校验登陆注册验证码成功的标识" )
private String checkRegisterSmsFlag;
/**
*
*
* @Schema `Swagger`
*/
@Schema(description = "校验登陆注册验证码成功的标识")
private String checkRegisterSmsFlag;
@Schema(description = "当账户未绑定时临时的uid" )
private String tempUid;
/**
* uid
* uid便
* @Schema `Swagger` uid
*/
@Schema(description = "当账户未绑定时临时的uid")
private String tempUid;
@Schema(description = "用户id" )
private Long userId;
}
/**
* id
* id
* @Schema `Swagger` id
*/
@Schema(description = "用户id")
private Long userId;
}

@ -13,24 +13,32 @@ package com.yami.shop.bean.pay;
import lombok.Data;
/**
*
* `PayInfoDto`DTOData Transfer Object
* 使`lombok``@Data`访`getter``setter``toString``hashCode``equals`
* 便
*
* @author LGH
*/
@Data
public class PayInfoDto {
/**
*
*
* 便
* P50
*/
private String body;
/**
*
*
* 退
*
*/
private String payNo;
/**
*
*
*
*/
private Double payAmount;
}
}

@ -8,23 +8,33 @@
*
*/
// 该类所属的包名表明其位于商城相关的Java Bean的VOValue Object值对象包下VO类通常用于在不同业务层之间传递数据对特定业务实体的部分属性进行封装方便数据的传递与交互。
// 这里的SysUserVO类主要是对系统用户相关信息进行简单封装用于在系统的不同模块或者层级间传递与系统用户有关的数据。
package com.yami.shop.bean.vo;
import lombok.Data;
/**
* SysUserVOJava Bean便使
* lombok@DataGetterSettertoStringequalshashCode使访便
*
* @author lanhai
*/
@Data
// 使用lombok的@Data注解它会自动为该类生成Getter和Setter方法用于获取和设置类中的成员变量值生成toString方法方便在打印对象时直观展示对象的属性信息
// 生成equals和hashCode方法用于在对象比较以及基于哈希的集合操作如HashMap中作为键使用时等场景中使用减少了手动编写这些重复代码的工作量让代码更加简洁清晰。
public class SysUserVO {
/**
* id
* idID
* ID
*/
private String userId;
/**
*
* 使
*
*/
private String username;
}
}

@ -8,29 +8,36 @@
*
*/
// 该类所属的包名表明其位于商城相关的Java Bean的VOValue Object值对象包下通常用于在不同层之间传递数据封装了与用户相关的部分属性信息方便统一进行数据交互操作。
package com.yami.shop.bean.vo;
import lombok.Data;
/**
* UserVOJava Bean
* 使lombok@DataGetterSettertoStringequalshashCode便访
*
* @author lanhai
*/
@Data
// 使用lombok的@Data注解会自动为该类生成Getter、Setter、toString、equals、hashCode等方法减少了手动编写这些重复代码的工作量使得代码更加简洁方便对类中成员变量的操作和使用。
public class UserVO {
/**
* id
* idID
*/
private String userId;
/**
*
* 便
*/
private String nickName;
// 此处未添加详细注释的userMobile变量推测是用于存储用户的手机号码信息在涉及用户联系方式、账号绑定或者短信验证等业务场景中可能会用到该信息。
private String userMobile;
/**
*
*
*/
private String pic;
}
}

@ -14,35 +14,42 @@ import java.lang.annotation.*;
import java.util.concurrent.TimeUnit;
/**
* 使redis
* `RedisLock`使Redis线访
* 使访便
* Redis
*
* @author lanhai
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target(ElementType.METHOD) // 表明该注解可以应用在方法上,意味着只能将这个注解标记在方法声明处,用于对方法添加分布式锁相关的配置。
@Retention(RetentionPolicy.RUNTIME) // 指定注解的保留策略为运行时,即在运行时可以通过反射等机制获取到该注解的相关信息,从而根据注解配置来实现分布式锁的逻辑。
@Documented // 表示该注解会被包含在生成的JavaDoc文档中方便开发人员查看和了解该注解的相关信息。
public @interface RedisLock {
/**
* redis
*/
String lockName() default "";
/**
* redisRedis便Redis
* 使使 "orderService_createOrder_lock"
*/
String lockName() default "";
/**
* redis key spel
*/
String key() default "";
/**
* rediskeyspelkeyRedisspelSpring Expression Language
* 使ID "#orderId" spelID使
*/
String key() default "";
/**
* ,5
*
* @return
*/
int expire() default 5000;
/**
* Redis `expire` `TimeUnit` 50005
*
*
* @return 便使
*/
int expire() default 5000;
/**
*
*
* @return
*/
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}
/**
* `expire` `TimeUnit.MILLISECONDS` `expire`
* `expire`
*
* @return `TimeUnit` `expire` `expire` 使
*/
TimeUnit timeUnit() default TimeUnit.MILLISECONDS;
}

@ -8,6 +8,8 @@
*
*/
// 该类所属的包名表明其位于商城项目的通用common注解annotation包下通常用于存放一些项目中自定义的、具有通用性的注解方便在多个地方复用以实现特定的功能需求。
// 这里的这个注解可能用于记录系统相关操作的日志信息,在对应的方法执行时进行日志记录等相关操作。
package com.yami.shop.common.annotation;
import java.lang.annotation.Documented;
@ -17,12 +19,24 @@ import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* SysLogJava便
* 便
*
* @author lanhai
*/
// @Target注解用于指定该自定义注解可以应用的目标元素类型这里ElementType.METHOD表示该SysLog注解只能应用在方法上
// 即可以在方法声明前添加该注解来标记这个方法需要进行相关的系统日志记录操作。
@Target(ElementType.METHOD)
// @Retention注解用于指定注解的保留策略RetentionPolicy.RUNTIME表示该注解在运行时仍然保留可以通过反射机制在运行时获取到该注解的相关信息
// 这样就能在程序运行期间根据注解的配置来决定是否以及如何进行系统日志记录等操作。
@Retention(RetentionPolicy.RUNTIME)
// @Documented注解用于表示该注解在生成JavaDoc文档时会被包含进去使得在生成的文档中能够看到该注解的相关信息方便其他开发人员了解该注解的存在和作用。
@Documented
// 使用@interface关键字定义了一个名为SysLog的自定义注解它可以像其他Java内置注解一样被应用在相应的目标元素这里是方法上。
public @interface SysLog {
String value() default "";
}
// 定义了一个名为value的注解元素它是一个字符串类型并且默认值为空字符串。
// 在使用该注解时如果没有为value元素指定具体的值就会使用这个默认的空字符串。
// 这个元素可能用于传递一些与日志记录相关的额外信息,比如日志的简要描述等内容,具体用途取决于在代码中对该注解的解析和使用逻辑。
String value() default "";
}

@ -27,48 +27,82 @@ import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* Redis
* Spring AOPRedis
* `@RedisLock`线
*
* @author lgh
*/
@Aspect
// 使用 @Aspect 注解声明该类是一个切面类,用于定义切点和增强逻辑(如前置通知、后置通知、环绕通知等)
@Component
// 使用 @Component 注解将该类注册为Spring容器中的一个组件方便进行依赖注入等操作
public class RedisLockAspect {
@Autowired
private RedissonClient redissonClient;
@Autowired
// 通过依赖注入获取RedissonClient对象用于与Redis进行交互操作Redis锁相关的功能
private RedissonClient redissonClient;
// 定义Redis锁的键名前缀用于在Redis中区分不同的锁方便管理和识别
private static final String REDISSON_LOCK_PREFIX = "redisson_lock:";
private static final String REDISSON_LOCK_PREFIX = "redisson_lock:";
/**
* `@RedisLock`
* Redis线
*
* @param joinPoint
* @param redisLock `@RedisLock`
* @return `@RedisLock`
* @throws Throwable
*/
@Around("@annotation(redisLock)")
// 使用 @Around 注解定义环绕通知,该通知会在目标方法执行前后进行额外的逻辑处理,这里就是围绕着被标注`@RedisLock`注解的方法进行加锁和解锁操作
public Object around(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {
// 获取`@RedisLock`注解中定义的用于生成锁键的SPEL表达式SPEL表达式可以根据方法参数等动态生成锁键
String spel = redisLock.key();
// 获取`@RedisLock`注解中定义的锁名称,用于更直观地标识锁的用途等信息
String lockName = redisLock.lockName();
@Around("@annotation(redisLock)")
public Object around(ProceedingJoinPoint joinPoint, RedisLock redisLock) throws Throwable {
String spel = redisLock.key();
String lockName = redisLock.lockName();
// 根据连接点、锁名称以及SPEL表达式生成最终的Redis锁的键名并通过RedissonClient获取对应的Redis锁对象
RLock rLock = redissonClient.getLock(getRedisKey(joinPoint, lockName, spel));
RLock rLock = redissonClient.getLock(getRedisKey(joinPoint,lockName,spel));
// 使用获取到的Redis锁对象进行加锁操作设置锁的过期时间和时间单位按照`@RedisLock`注解中定义的配置来进行
rLock.lock(redisLock.expire(), redisLock.timeUnit());
rLock.lock(redisLock.expire(),redisLock.timeUnit());
Object result = null;
try {
// 执行被标注`@RedisLock`注解的目标方法,即让原本的业务逻辑正常执行
result = joinPoint.proceed();
Object result = null;
try {
//执行方法
result = joinPoint.proceed();
} finally {
// 无论目标方法执行是否成功,最终都要释放锁,确保锁资源能够被正确释放,避免死锁等问题
rLock.unlock();
}
return result;
}
} finally {
rLock.unlock();
}
return result;
}
/**
* SPELRedis
* SPELSPELRedis
*
* @param joinPoint SPEL
* @param lockName Redis
* @param spel SPEL
* @return Redis"redisson_lock:锁名称:具体解析后的表达式内容"Redis
*/
private String getRedisKey(ProceedingJoinPoint joinPoint, String lockName, String spel) {
// 获取连接点对应的方法签名信息,用于后续获取目标方法对象
Signature signature = joinPoint.getSignature();
// 将方法签名信息转换为方法签名的具体实现类对象,方便获取目标方法的详细信息
MethodSignature methodSignature = (MethodSignature) signature;
// 获取目标方法对象后续可用于解析SPEL表达式等操作
Method targetMethod = methodSignature.getMethod();
// 获取目标对象即被代理的实际业务对象同样可用于解析SPEL表达式等操作
Object target = joinPoint.getTarget();
// 获取目标方法的参数数组这些参数在解析SPEL表达式时可能会作为变量参与计算
Object[] arguments = joinPoint.getArgs();
/**
* spel
* @param joinPoint
* @return redisKey
*/
private String getRedisKey(ProceedingJoinPoint joinPoint,String lockName,String spel) {
Signature signature = joinPoint.getSignature();
MethodSignature methodSignature = (MethodSignature) signature;
Method targetMethod = methodSignature.getMethod();
Object target = joinPoint.getTarget();
Object[] arguments = joinPoint.getArgs();
return REDISSON_LOCK_PREFIX + lockName + StrUtil.COLON + SpelUtil.parse(target,spel, targetMethod, arguments);
}
}
// 通过工具类方法解析SPEL表达式结合锁名称等信息生成最终的Redis锁键字符串并添加前缀形成完整的用于在Redis中标识锁的键名
return REDISSON_LOCK_PREFIX + lockName + StrUtil.COLON + SpelUtil.parse(target, spel, targetMethod, arguments);
}
}

@ -13,15 +13,30 @@ package com.yami.shop.common.bean;
import lombok.Data;
/**
*
* 便使
* 使
* 便
*
* @author LGH
*/
@Data
public class AliDaYu {
private String accessKeyId;
private String accessKeySecret;
private String signName;
}
/**
* 访 ID API
* ID 访accessKeySecret使
*/
private String accessKeyId;
/**
* 访访 IDaccessKeyId
* API 访 ID
*/
private String accessKeySecret;
/**
* 使
* 使
*/
private String signName;
}

@ -13,24 +13,33 @@ package com.yami.shop.common.bean;
import lombok.Data;
/**
*
* `ImgUpload`便
* 访
* `lombok``@Data`访`getter``setter``toString``hashCode``equals`便使
*
* @author lgh
*/
@Data
public class ImgUpload {
/**
*
*/
private String imagePath;
/**
*
*
* Windows "C:/uploads/images/"Linux "/var/www/uploads/images/" 便访
*/
private String imagePath;
/**
* 1. 2.
*/
private Integer uploadType;
/**
* 1.2.
* 使便
*
*/
private Integer uploadType;
/**
* url
*/
private String resourceUrl;
}
/**
* url访 "https://www.example.com/"
* 访访
* 使访
*/
private String resourceUrl;
}

@ -8,25 +8,35 @@
*
*/
// 该类所属的包名表明其位于商城项目的通用commonJava Beanbean包下通常用于存放一些通用的数据模型类这些类用于封装特定业务相关的数据信息方便在不同的业务逻辑层之间传递和使用。
// 这里的Qiniu类主要用于封装七牛云存储相关的配置信息以便在项目中对七牛云存储进行操作时可以方便地获取和使用这些配置参数。
package com.yami.shop.common.bean;
import com.yami.shop.common.enums.QiniuZone;
import lombok.Data;
/**
*
* QiniuJava Bean使
* 访lombok@Data便
*
* @author lgh
*/
@Data
// 使用lombok的@Data注解会自动为该类生成Getter、Setter、toString、equals、hashCode等方法减少了手动编写这些重复代码的工作量使得代码更加简洁方便对类中成员变量进行访问和操作。
public class Qiniu {
private String accessKey;
// 七牛云的访问密钥Access Key用于在与七牛云存储服务进行交互时进行身份验证相当于用户名的作用具有访问权限控制的功能只有拥有正确的访问密钥才能对七牛云存储资源进行相应的操作。
private String accessKey;
private String secretKey;
// 七牛云的秘密密钥Secret Key与访问密钥配合使用用于对请求进行签名等安全验证操作确保请求的合法性和安全性是保证七牛云存储数据安全的重要配置参数之一。
private String secretKey;
private String bucket;
// 七牛云存储中的存储桶Bucket名称存储桶是七牛云存储中用于存放文件等资源的容器类似于文件夹的概念不同的项目或者业务模块可以使用不同的存储桶来进行资源的分类存储和管理。
private String bucket;
private String resourcesUrl;
// 资源访问的URL地址用于在项目中访问存储在七牛云存储桶中的资源时使用通过这个地址可以拼接具体的资源路径从而在前端或者其他需要使用资源的地方获取到对应的文件内容例如图片资源的展示等。
private String resourcesUrl;
private QiniuZone zone;
}
// 七牛云存储的区域Zone信息通过引用QiniuZone枚举类型来指定不同的区域对应着不同的数据中心位置选择合适的区域可以优化资源的存储和访问速度等性能同时也可能涉及到不同的网络配置等因素。
private QiniuZone zone;
}

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

Loading…
Cancel
Save