Java --- 云尚优选项目

目录

一、项目工程搭建

二、配置项目相关maven依赖

2.1、cjc.ssyx父工程依赖 

 2.2、common模块pom依赖

2.3、common-util模块pom依赖

2.4、service-util模块pom依赖

2.5、model模块pom依赖

2.6、service模块pom依赖

三、编写相关工具类

3.1、mybatis-plus分页查询配置类 

3.2、统一返回结果类

3.3、统一异常处理类

3.4、编写Swagger配置类

四、尚上优先前端平台管理

4.1、node.js安装

4.2、安装vscode及相关插件 

 4.3、测试vue框架是否正常运行

4.4、导入前端项目并测试运行

五、后端登录功能

5.1、修改application.yml文件

5.2、修改application-dev.yml文件

5.3、后端登录功能接口编写

5.4、前端修改

5.5、运行报错解决

5.6、前后端跨域问题

5.7、测试访问

六、角色管理功能开发

6.1、分页条件查询 

6.2、增删改查功能

七、用户管理模块

7.1、用户的crud功能 

7.2、用户分配角色功能

八、菜单管理模块

8.1、查询菜单功能 

8.2、递归删除

8.3、添加与修改功能

8.4、为角色分配菜单

九、开通区域功能模块

9.1、使用代码生成器

9.2、查询区域开通列表功能

 9.3、添加开通区域及查询区域功能

9.4、取消与删除开通区域功能

十、配置nginx反向代理

十一、商品信息管理模块 

11.1、商品分类信息模块功能 

11.2、平台属性分组模块功能

11.3、平台属性管理模块功能

11.4、SKU列表模块 

11.4.1、SKU信息分页条件查询功能

11.4.2、开通阿里云对象存储OSS

11.4.2.1、创建自己的Bucket

11.4.2.2、创建自己的AccessKey

11.4.3、SKU图片上传功能

11.4.4、SKU信息添加功能

 11.4.4.1、文件上传时前后端端口不一致问题

11.4.5、SKU商品的修改与删除功能

11.4.6、SKU商品信息的审核、上下架、新人专享功能 

十二、Nacos下载与安装 

12.1、修改springboot配置文件

十三、整合ES+MQ实现商品上下架

13.1、安装Elasticsearch

13.2、安装Kibana

13.3、整合Elasticsearch

13.4、完成远程调用接口功能

13.4.1、发送消息功能:

 13.4.2、远程调用接口

 13.4.3、接收消息功能:

13.5、在docker安装rabbitMQ

13.6、整合rabbitmq

13.7、完善商品上下架功能

十四、营销活动管理

14.1、活动列表的crud功能

14.2、查询活动规则功能

14.3、添加活动规则功能

14.4、优惠卷功能 

 十五、整合Gateway网关

十六、微信小程序开发环境搭建

 16.1、注册微信程序开发测试号

16.2、安装微信开发者工具

 16.3、安装hbuilderx

16.3.1、配置 hbuilderx

16.4、开通内网穿透

十七、用户登录

17.1、整合JWT

17.1.1、jwt简介

17.1.2、jwt原理

17.1.3、springboot整合Jwt

17.2、整合redis

17.3、springboot整合微信小程序配置

17.4、添加相关工具类

17.5、用户登录功能

十八、首页数据显示

18.1、拦截器 

18.1.1、实现步骤 

18.2、首页数据显示 

 十九、商品分类与搜索

二十、商品详情 

20.1、CompletableFuture的使用测试

20.1.1、创建异步对象

 20.1.2、计数完成回调

 20.1.3、串行化

20.1.4、多任务组合


一、项目工程搭建

Java --- 云尚优选项目_第1张图片

二、配置项目相关maven依赖

2.1、cjc.ssyx父工程依赖 


        8
        8
        true
        1.8
        Hoxton.SR8
        2.2.2.RELEASE
        3.4.1
        8.0.30
        0.7.0
        2.0.21
        4.5.6
        2.1.6
        4.4.1
        3.9.1
        2.0.8
        2.10.10
        2.3.0
    
    
        org.springframework.boot
        spring-boot-starter-parent
        2.3.6.RELEASE
    

    
    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${cloud.version}
                pom
                import
            
            
                com.alibaba.cloud
                spring-cloud-alibaba-dependencies
                ${alibaba.version}
                pom
                import
            
            
            
                com.baomidou
                mybatis-plus-boot-starter
                ${mybatis-plus.version}
            
            
                mysql
                mysql-connector-java
                ${mysql.version}
            
            
                com.github.xiaoymin
                knife4j-spring-boot-starter
                2.0.8
            
            
                io.jsonwebtoken
                jjwt
                ${jwt.version}
            
            
                org.apache.httpcomponents
                httpclient
                ${httpclient.version}
            
            
                com.alibaba
                fastjson
                ${fastjson.version}
            
            
                com.alibaba
                easyexcel
                ${easyexcel.version}
            
            
                com.aliyun
                aliyun-java-sdk-core
                ${aliyun.version}
            
            
            
                joda-time
                joda-time
                ${jodatime.version}
            
            
                com.xuxueli
                xxl-job-core
                ${xxl-job.version}
            
        
    
  

 2.2、common模块pom依赖

 
        
            org.springframework.boot
            spring-boot-starter-web
            provided 
        

        
        
            org.projectlombok
            lombok
        
        
        
            com.github.xiaoymin
            knife4j-spring-boot-starter
        

        
        
            com.alibaba
            fastjson
        

        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
            provided 
        
    

2.3、common-util模块pom依赖


        
            org.apache.httpcomponents
            httpclient
        

        
            io.jsonwebtoken
            jjwt
        

        
            joda-time
            joda-time
        

        
            com.cjc
            model
            1.0-SNAPSHOT
            provided
        
    

2.4、service-util模块pom依赖


        
            org.springframework.boot
            spring-boot-starter-web
            provided 
        

        
        
            org.projectlombok
            lombok
        
        
        
            com.github.xiaoymin
            knife4j-spring-boot-starter
        

        
        
            com.alibaba
            fastjson
        

        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
            provided 
        
    

2.5、model模块pom依赖


        
            org.projectlombok
            lombok
        

        
        
            com.baomidou
            mybatis-plus-boot-starter
            provided 
        

        
            com.github.xiaoymin
            knife4j-spring-boot-starter
            
            provided
        

        
            org.springframework.boot
            spring-boot-starter-data-mongodb
            provided 
        

        
            com.alibaba
            fastjson
            provided 
        

        
        
            org.springframework.boot
            spring-boot-starter-data-elasticsearch
            provided 
        
    

2.6、service模块pom依赖

   
        
        
            com.cjc
            service-util
            1.0-SNAPSHOT
        
        
        
            com.cjc
            model
            1.0-SNAPSHOT
        

        
        
            org.springframework.boot
            spring-boot-starter-web
        

        
        
            com.baomidou
            mybatis-plus-boot-starter
        

        
        
            mysql
            mysql-connector-java
        

        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        

        
        
            org.springframework.cloud
            spring-cloud-starter-openfeign
        

        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-sentinel
        

        
        
            org.springframework.boot
            spring-boot-devtools
            true
        
    

三、编写相关工具类

3.1、mybatis-plus分页查询配置类 

@Configuration
@MapperScan("com.cjc.ssyx.*.mapper")
public class MybatisPlusConfig {
    /**
     * 分页插件
     */
    @Bean
    public MybatisPlusInterceptor optimisticLockerInnerInterceptor(){
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //向Mybatis过滤器链中添加分页拦截器
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
    @Bean
    public ConfigurationCustomizer configurationCustomizer(){
        return configuration -> configuration.setUseDeprecatedExecutor(false);
    }
}

3.2、统一返回结果类

@Getter
public enum ResultCodeEnum {

    SUCCESS(200,"成功"),
    FAIL(201, "失败"),
    SERVICE_ERROR(2012, "服务异常"),
    DATA_ERROR(204, "数据异常"),
    ILLEGAL_REQUEST(205, "非法请求"),
    REPEAT_SUBMIT(206, "重复提交"),

    LOGIN_AUTH(208, "未登陆"),
    PERMISSION(209, "没有权限"),

    ORDER_PRICE_ERROR(210, "订单商品价格变化"),
    ORDER_STOCK_FALL(204, "订单库存锁定失败"),
    CREATE_ORDER_FAIL(210, "创建订单失败"),

    COUPON_GET(220, "优惠券已经领取"),
    COUPON_LIMIT_GET(221, "优惠券已发放完毕"),

    URL_ENCODE_ERROR( 216, "URL编码失败"),
    ILLEGAL_CALLBACK_REQUEST_ERROR( 217, "非法回调请求"),
    FETCH_ACCESSTOKEN_FAILD( 218, "获取accessToken失败"),
    FETCH_USERINFO_ERROR( 219, "获取用户信息失败"),


    SKU_LIMIT_ERROR(230, "购买个数不能大于限购个数"),
    REGION_OPEN(240, "该区域已开通"),
    REGION_NO_OPEN(240, "该区域未开通"),
    ;

    private Integer code;

    private String message;

    private ResultCodeEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }
}
@Data
public class Result {
    //状态码
    private Integer code;
    //信息
    private String message;
    //返回数据
    private T data;
    //构造方法私有化
    private Result(){

    }
    //统一返回信息方法
    public static Result build(T data,ResultCodeEnum resultCodeEnum){
        //设置值
        Result result = new Result<>();
        //判断是否返回数据
        if(data != null){
            result.setData(data);
        }
        //设置状态码与信息
        result.setCode(resultCodeEnum.getCode());
        result.setMessage(resultCodeEnum.getMessage());
        return result;
    }
    //成功的方法
    public static Result ok(T data){
        Result result = build(data, ResultCodeEnum.SUCCESS);
        return result;
    }
    //失败方法
    public static Result fail(T data){
        Result result = build(data, ResultCodeEnum.FAIL);
        return result;
    }
}

3.3、统一异常处理类

//自定义异常信息
@Data
@ToString
public class CustomException extends RuntimeException{
    //异常状态码
    private Integer code;
    //通过状态码和错误消息创建异常对象
    public CustomException(String message,Integer code){
        super(message);
        this.code = code;
    }
    /**
     * 接收枚举类型对象
     * @param resultCodeEnum
     */
    public CustomException(ResultCodeEnum resultCodeEnum) {
        super(resultCodeEnum.getMessage());
        this.code = resultCodeEnum.getCode();
    }
    
}
@ControllerAdvice
public class GlobalExceptionHandler {
    //全局异常处理
    @ExceptionHandler(Exception.class)//异常处理器
    @ResponseBody//返回json数据
    public Result error(Exception e){
        e.printStackTrace();
        return Result.fail(null);
    }
    //自定义异常处理
    @ExceptionHandler(CustomException.class)
    @ResponseBody
   public Result error(CustomException e){
       return Result.fail(null);
   }
}

3.4、编写Swagger配置类

@Configuration
@EnableSwagger2WebMvc
public class Swagger2Config {

    @Bean
    public Docket webApiConfig(){
        List pars = new ArrayList<>();
        ParameterBuilder tokenPar = new ParameterBuilder();
        tokenPar.name("userId")
                .description("用户token")
                //.defaultValue(JwtHelper.createToken(1L, "admin"))
                .defaultValue("1")
                .modelRef(new ModelRef("string"))
                .parameterType("header")
                .required(false)
                .build();
        pars.add(tokenPar.build());

        Docket webApi = new Docket(DocumentationType.SWAGGER_2)
                .groupName("webApi")
                .apiInfo(webApiInfo())
                .select()
                //只显示api路径下的页面
                .apis(RequestHandlerSelectors.basePackage("com.cjc.ssyx"))
                .paths(PathSelectors.regex("/api/.*"))
                .build()
                .globalOperationParameters(pars);
        return webApi;
    }

    @Bean
    public Docket adminApiConfig(){
        List pars = new ArrayList<>();
        ParameterBuilder tokenPar = new ParameterBuilder();
        tokenPar.name("adminId")
                .description("用户token")
                .defaultValue("1")
                .modelRef(new ModelRef("string"))
                .parameterType("header")
                .required(false)
                .build();
        pars.add(tokenPar.build());

        Docket adminApi = new Docket(DocumentationType.SWAGGER_2)
                .groupName("adminApi")
                .apiInfo(adminApiInfo())
                .select()
                //只显示admin路径下的页面
                .apis(RequestHandlerSelectors.basePackage("com.cjc.ssyx"))
                .paths(PathSelectors.regex("/admin/.*"))
                .build()
                .globalOperationParameters(pars);
        return adminApi;
    }

    private ApiInfo webApiInfo(){
        return new ApiInfoBuilder()
                .title("网站-API文档")
                .description("本文档描述了尚上优选网站微服务接口定义")
                .version("1.0")
                .contact(new Contact("cjc", "http://cjc.com", "cjc"))
                .build();
    }

    private ApiInfo adminApiInfo(){
        return new ApiInfoBuilder()
                .title("后台管理系统-API文档")
                .description("本文档描述了尚上优选后台系统服务接口定义")
                .version("1.0")
                .contact(new Contact("cjc", "http://cjc.com", "cjc"))
                .build();
    }
}

四、尚上优先前端平台管理

4.1、node.js安装

Java --- 云尚优选项目_第2张图片

4.2、安装vscode及相关插件 

 Java --- 云尚优选项目_第3张图片

 4.3、测试vue框架是否正常运行

#全局安装命令行工具
npm install --location=global @vue/cli
#创建一个项目
vue create vue-test #选择vue2
#进入到项目目录
cd vue-test
#启动程序
npm run serve

PS D:\vue\cjc-ssyx\test> vue create vue-test
vue : 无法加载文件 D:\tools\nodejs\node_global\vue.ps1。未对文件 D:\tools\nodejs\node_global\vue.ps1 进行数字签名。无法在当前系统上运行该脚本。有关运行脚本和设置
执行策略的详细信息,请参阅 https:/go.microsoft.com/fwlink/?LinkID=135170 中的 about_Execution_Policies。
所在位置 行:1 字符: 1
+ vue create vue-test
+ ~~~
    + CategoryInfo          : SecurityError: (:) [],PSSecurityException
    + FullyQualifiedErrorId : UnauthorizedAccess 

解决办法:以管理员的方式运行即可

 最终效果Java --- 云尚优选项目_第4张图片

4.4、导入前端项目并测试运行

运行指令:npm run dev

Java --- 云尚优选项目_第5张图片

五、后端登录功能

5.1、修改application.yml文件

spring:
  application:
    name: service-acl
  profiles:
    active: dev

5.2、修改application-dev.yml文件

server:
  port: 8081

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/shequ-acl?characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456

  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8

5.3、后端登录功能接口编写

@Api(tags = "登录功能模块")
@RestController
@RequestMapping("/admin/acl/index")
public class IndexController {
    /**
     * 登录功能
     * @return map
     */
    @ApiOperation("登录功能")
    @PostMapping("/login")
    public Result login(){
        //返回token值
        HashMap map = new HashMap<>();
        map.put("token","token-test");
        return Result.ok(map);
    }

    /**
     * 获取信息
     * @return
     */
    @ApiOperation("获取信息")
    @GetMapping("/info")
    public Result info(){
        HashMap map = new HashMap<>();
        map.put("name","admin");
        map.put("avatar","https://wpimg.wallstcn.com/f778738c-e4f8-4870-b634-56703b4acafe.gif");
        return Result.ok(map);
    }

    /**
     * 退出
     * @return
     */
    @ApiOperation("退出")
    @PostMapping("/logout")
    public Result logout(){
       return Result.ok(null);
    }
}

5.4、前端修改

5.5、运行报错解决

com.alibaba.nacos.api.exception.NacosException: failed to req API:/nacos/v1/ns/instance after all servers([localhost:8848]) tried: java.net.ConnectException: Connection refused: connect
    at com.alibaba.nacos.client.naming.net.NamingProxy.reqApi(NamingProxy.java:552) ~[nacos-client-1.3.2.jar:na]
    at com.alibaba.nacos.client.naming.net.NamingProxy.reqApi(NamingProxy.java:491) ~[nacos-client-1.3.2.jar:na]
    at com.alibaba.nacos.client.naming.net.NamingProxy.reqApi(NamingProxy.java:486) ~[nacos-client-1.3.2.jar:na]
    at com.alibaba.nacos.client.naming.net.NamingProxy.registerService(NamingProxy.java:239) ~[nacos-client-1.3.2.jar:na]
    at com.alibaba.nacos.client.naming.NacosNamingService.registerInstance(NacosNamingService.java:200) ~[nacos-client-1.3.2.jar:na] 

解决办法:注销相关依赖

 Java --- 云尚优选项目_第6张图片

5.6、前后端跨域问题

Access to XMLHttpRequest at 'http://localhost:8081/admin/acl/index/login' from origin 'http://localhost:9528' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. 

Java --- 云尚优选项目_第7张图片

 解决方法:

Java --- 云尚优选项目_第8张图片

5.7、测试访问

Java --- 云尚优选项目_第9张图片

六、角色管理功能开发

6.1、分页条件查询 

 mapper接口

@Mapper
public interface RoleMapper extends BaseMapper {
}

 service接口

public interface RoleService extends IService {
    IPage selectRolePage(Page page, RoleQueryVo roleQueryVo);
}

service接口实现类

@Service
public class RoleServiceImpl extends ServiceImpl implements RoleService {
    //分页条件查询
    @Override
    public IPage selectRolePage(Page page, RoleQueryVo roleQueryVo) {
        //获取查询条件值
        String roleName = roleQueryVo.getRoleName();
        //条件封装
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        //判断是否为空
        if (!StringUtils.isEmpty(roleName)){
            wrapper.like(Role::getRoleName,roleName);
        }
        //调用方法查询
        Page rolePage = baseMapper.selectPage(page, wrapper);
        return rolePage;
    }
}

controller层

@Api(tags = "角色管理模块")
@RestController
@RequestMapping("/admin/acl/role")
@CrossOrigin
public class RoleController {
    @Autowired
    private RoleService roleService;

    /**
     *
     * @param current 当前页
     * @param limit 每页显示记录数
     * @param roleQueryVo 条件对象
     * @return
     */
    @ApiOperation("分页查询")
    @GetMapping("/{current}/{limit}")
    public Result pageList(@PathVariable("current") Long current,
                           @PathVariable("limit") Long limit,
                           RoleQueryVo roleQueryVo){
        //创建分页对象
        Page page = new Page<>(current,limit);
        //调用方法实现分页查询
        IPage roleIPage = roleService.selectRolePage(page,roleQueryVo);
        return Result.ok(roleIPage);
    }
}

测试结果:

Java --- 云尚优选项目_第10张图片

6.2、增删改查功能

@ApiOperation("根据id查询")
    @GetMapping("/get/{id}")
    public Result selectRoleById(@PathVariable("id") Long id){
        Role role = roleService.getById(id);
        return Result.ok(role);
    }
    
    @ApiOperation("添加角色")
    @PostMapping("/save")
    public Result saveRole(@RequestBody Role role){
        boolean save = roleService.save(role);
        if (save){
            return Result.ok(null); 
        }else {
            return Result.fail(null);
        }
    }
    @ApiOperation("修改角色")
    @PutMapping("/update")
    public Result updateRole(@RequestBody Role role){
        boolean update = roleService.updateById(role);
        if (update){
            return Result.ok(null);
        }else {
            return Result.fail(null);
        }
    }
    @ApiOperation("根据id删除")
    @DeleteMapping("/remove/{id}")
    public Result removeRoleById(@PathVariable("id") Long id){
        boolean remove = roleService.removeById(id);
        if (remove){
            return Result.ok(null);
        }else {
            return Result.fail(null);
        }
    }
    @ApiOperation("根据id批量删除")
    @DeleteMapping("/batchRemove")
    public Result removeByList(@RequestBody List idList){
        boolean removeByIds = roleService.removeByIds(idList);
        if (removeByIds){
            return Result.ok(null);
        }else {
            return Result.fail(null);
        }
    }

 后端接口测试以添加为例:

Java --- 云尚优选项目_第11张图片

七、用户管理模块

7.1、用户的crud功能 

 service实现类层

@Service
public class AdminServiceImpl extends ServiceImpl implements AdminService {
    //分页条件查询
    @Override
    public IPage selectAdminPage(Page page, AdminQueryVo adminQueryVo) {
        String name = adminQueryVo.getName();
        String username = adminQueryVo.getUsername();
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        if (!StringUtils.isEmpty(name)){
            wrapper.like(Admin::getName,name);
        }
        if (!StringUtils.isEmpty(username)){
            wrapper.like(Admin::getUsername,username);
        }
        Page adminPage = baseMapper.selectPage(page, wrapper);
        return adminPage;
    }
}

controller层

@Api(tags = "用户管理模块")
@RestController
@RequestMapping("/admin/acl/user")
@CrossOrigin
public class AdminController {
    @Autowired
    private AdminService adminService;

    @ApiOperation("分页查询用户")
    @GetMapping("/{current}/{limit}")
    public Result getAdmin(@PathVariable("current") Long current,
                           @PathVariable("limit") Long limit,
                           AdminQueryVo adminQueryVo){
        Page page = new Page<>(current,limit);
        IPage adminIPage = adminService.selectAdminPage(page,adminQueryVo);
        return Result.ok(adminIPage);
    }
    @ApiOperation("根据id查询")
    @GetMapping("/get/{id}")
    public Result getAdminById(@PathVariable("id") Long id){
        Admin admin = adminService.getById(id);
        return Result.ok(admin);
    }
    @ApiOperation("添加用户")
    @PostMapping("/save")
    public Result saveAdmin(@RequestBody Admin admin){
       //获取用户密码并进行加密处理
        admin.setPassword(MD5.encrypt(admin.getPassword()));
        boolean save = adminService.save(admin);
        if (save){
            return Result.ok(null);
        }else {
            return Result.fail(null);
        }
    }
    @ApiOperation("修改用户")
    @PutMapping("/update")
    public Result updateAdmin(@RequestBody Admin admin){
        //获取用户密码并进行加密处理
        admin.setPassword(MD5.encrypt(admin.getPassword()));
        boolean update = adminService.updateById(admin);
        if (update){
            return Result.ok(null);
        }else {
            return Result.fail(null);
        }
    }
    @ApiOperation("根据id删除用户")
    @DeleteMapping("/delete/{id}")
    public Result removeById(@PathVariable("id") Long id){
        boolean remove = adminService.removeById(id);
        if (remove){
            return Result.ok(null);
        }else {
            return Result.fail(null);
        }
    }
    @ApiOperation("根据id批量删除用户")
    @DeleteMapping("/batchRemove")
    public Result removeByBatch(@RequestBody List idList){
        boolean removeByIds = adminService.removeByIds(idList);
        if (removeByIds){
            return Result.ok(null);
        }else {
            return Result.fail(null);
        }
    }
}

接口测试以添加为例:

Java --- 云尚优选项目_第12张图片

前端界面效果:

Java --- 云尚优选项目_第13张图片

7.2、用户分配角色功能

 功能分析图:

Java --- 云尚优选项目_第14张图片

功能实现:

service实现类 

 //查询所有角色和根据用户id查询分配角色
    @Override
    public Map getRoleByAdminId(Long adminId) {
        HashMap map = new HashMap<>();
        //查询所有角色
        List roles = baseMapper.selectList(null);
        //根据用户id查询用户分配角色列表
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AdminRole::getAdminId,adminId);
        List adminRoleList = adminRoleService.list(wrapper);
        //获取角色关系表里的所有角色id,封装为list集合
        List roleIdList = adminRoleList.stream().map(c -> c.getRoleId()).collect(Collectors.toList());
        //创建list集合,用于存储用户分配角色
        ArrayList roleList = new ArrayList<>();
        //判断所有角色中已经分配了的角色id
        for (Role role : roles) {
            //判断是否存在
            if(roleIdList.contains(role.getId())){
                roleList.add(role);
            }
        }
        //封装数据并返回
        map.put("allRolesList",roles);
        map.put("assignRoles",roleList);
        return map;
    }
    //为用户分配角色
    @Override
    public void saveAdminRole(Long adminId, Long[] roleIds) {
        //删除用户已经分配过的角色id
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(AdminRole::getAdminId,adminId);
        adminRoleService.remove(wrapper);
        ArrayList arrayList = new ArrayList<>();
        //重新分配
        for (Long roleId : roleIds) {
            AdminRole adminRole = new AdminRole();
            adminRole.setAdminId(adminId);
            adminRole.setRoleId(roleId);
            arrayList.add(adminRole);
        }
        //批量添加
        adminRoleService.saveBatch(arrayList);
    }

controller层

@ApiOperation("获取用户角色")
    @GetMapping("/toAssign/{adminId}")
    public Result toAssign(@PathVariable("adminId") Long adminId){
        Map map = roleService.getRoleByAdminId(adminId);
        return Result.ok(map);
    }
    @ApiOperation("为用户分配角色")
    @PostMapping("/doAssign")
    public Result doAssign(@RequestParam Long adminId,
                           @RequestParam Long[] roleIds){
       roleService.saveAdminRole(adminId,roleIds);
        return Result.ok(null);
    }

前端效果图:

Java --- 云尚优选项目_第15张图片

八、菜单管理模块

8.1、查询菜单功能 

数据格式工具类

public class PermissionHelper {
    //构建树形格式
    public static List buildPermission(List permissionList) {
        //存储最终数据格式
        List trees = new ArrayList<>();
        for (Permission permission : permissionList) {
            //判断是否为第一层
            if (permission.getPid() == 0){
                permission.setLevel(1);
                //调用递归方法,并封装存放数据
                trees.add(findChildren(permission,permissionList));
            }
        }
        return trees;
    }

    /**
     *
     * @param permission 上级菜单
     * @param permissionList 所有菜单
     * @return
     */
    //递归查找子菜单
    private static Permission findChildren(Permission permission, List permissionList) {
       permission.setChildren(new ArrayList());
       //遍历所有菜单数据
        for (Permission p : permissionList) {
            //判断是否有下一级菜单
            if (permission.getId().equals(p.getPid())){
                int level = permission.getLevel() + 1;
                p.setLevel(level);
                if (permission.getChildren() == null){
                    permission.setChildren(new ArrayList<>());
                }
                //封装下一层数据,并递归查找
                permission.getChildren().add( findChildren(p,permissionList));

            }
        }
        return permission;
    }
}

 service实现类

 //查询所有菜单
    @Override
    public List queryAllPermission() {
        //查询所有菜单
        List permissionList = baseMapper.selectList(null);
        //转换数据格式
        List result = PermissionHelper.buildPermission(permissionList);
        return result;
    }

controller层:

 @ApiOperation("查询所有菜单")
    @GetMapping()
    public Result queryAll(){
        List list = permissionService.queryAllPermission();
        return Result.ok(list);
    }

接口测试:

Java --- 云尚优选项目_第16张图片

8.2、递归删除

/**
     * 递归删除菜单
     * @param id 菜单id
     */
    @Override
    public void removeChildById(Long id) {
        //存放需要删除的菜单id
        ArrayList idList = new ArrayList<>();
        //递归查找子菜单
        this.getAllPermissionId(id,idList);
        //设置当前菜单id
        idList.add(id);
        //批量删除
        baseMapper.deleteBatchIds(idList);
    }

    /**
     * 递归查询当前菜单下的子菜单
     * @param id 当前菜单id
     * @param idList 所有菜单id集合
     */
    public void getAllPermissionId(Long id,List idList){
        //根据当前菜单id查询下面的子菜单
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Permission::getPid,id);
        List childList = baseMapper.selectList(wrapper);
        //递归查询子菜单
        childList.stream().forEach(c -> {
            //封装菜单id
            idList.add(c.getId());
            //递归查询
            this.getAllPermissionId(c.getId(),idList);
        });
    }
@ApiOperation("删除菜单(递归删除)")
    @DeleteMapping("/remove/{id}")
    public Result deletePermission(@PathVariable("id") Long id){
        permissionService.removeChildById(id);
        return Result.ok(null);
    }

8.3、添加与修改功能

 @ApiOperation("添加菜单")
    @PostMapping("/save")
    public Result savePermission(@RequestBody Permission permission){
         permissionService.save(permission);
        return Result.ok(null);
    }
    @ApiOperation("修改菜单")
    @PutMapping("/update")
    public Result updatePermission(@RequestBody Permission permission){
        permissionService.updateById(permission);
        return Result.ok(null);
    }

前端效果图:

Java --- 云尚优选项目_第17张图片

8.4、为角色分配菜单

service实现类

/**
     * 查询角色及所属菜单
     * @param roleId 角色id
     * @return
     */
    @Override
    public List getPermissionByRoleId(Long roleId) {
        //查询所有菜单
        List permissions = baseMapper.selectList(null);
        //根绝角色id查询菜单id
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RolePermission::getRoleId,roleId);
        List rolePermissionList = rolePermissionService.list(wrapper);
        //获取到菜单id
        List permissionIdList = rolePermissionList.stream().map(c -> c.getPermissionId()).collect(Collectors.toList());
        permissions.stream().forEach(c->{
            if (permissionIdList.contains(c.getId())){
                c.setSelect(true);
            }else {
                c.setSelect(false);
            }
        });
        //封装为树形结构
        List permissionList = PermissionHelper.buildPermission(permissions);
        return permissionList;
    }

    /**
     * 为角色分配菜单
     * @param roleId 角色id
     * @param permissionId 菜单id集合
     */
    @Override
    public void doAssign(Long roleId, Long[] permissionId) {
        //根据角色id删除菜单角色表里数据
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(RolePermission::getRoleId,roleId);
        rolePermissionService.remove(wrapper);
        ArrayList list = new ArrayList<>();
        //获取角色分配的菜单id,并存入
        for (Long id : permissionId) {
            if (id == null){
                continue;
            }
            RolePermission rolePermission = new RolePermission();
            rolePermission.setRoleId(roleId);
            rolePermission.setPermissionId(id);
            list.add(rolePermission);
        }
        //添加到表中
        rolePermissionService.saveBatch(list);
    }

controller层

@ApiOperation("获取菜单列表")
    @GetMapping("/toAssign/{roleId}")
    public Result toAssign(@PathVariable("roleId") Long roleId){
        List list = permissionService.getPermissionByRoleId(roleId);
        return Result.ok(list);
    }
    @ApiOperation("角色分配菜单")
    @PostMapping("/doAssign")
    public Result doAssign(@RequestParam("roleId") Long roleId,
                           @RequestParam("permissionId") Long[] permissionId){

        permissionService.doAssign(roleId,permissionId);
        return Result.ok(null);
    }

前端效果图:

Java --- 云尚优选项目_第18张图片

九、开通区域功能模块

需要的表

 Java --- 云尚优选项目_第19张图片

9.1、使用代码生成器

 导入pom依赖

	
		com.baomidou
		mybatis-plus-generator
		3.4.1
	

	
		org.apache.velocity
		velocity-engine-core
		2.0
	
public class CodeGet {

    public static void main(String[] args) {

        // 1、创建代码生成器
        AutoGenerator mpg = new AutoGenerator();

        // 2、全局配置
        // 全局配置
        GlobalConfig gc = new GlobalConfig();
        gc.setOutputDir("D:\\java17\\cjc-ssyx\\service\\service-sys"+"/src/main/java");

        gc.setServiceName("%sService");	//去掉Service接口的首字母I
        gc.setAuthor("cjc");
        gc.setOpen(false);
        mpg.setGlobalConfig(gc);

        // 3、数据源配置
        DataSourceConfig dsc = new DataSourceConfig();
        dsc.setUrl("jdbc:mysql://localhost:3306/shequ-sys?serverTimezone=GMT%2B8&useSSL=false");
        dsc.setDriverName("com.mysql.cj.jdbc.Driver");
        dsc.setUsername("root");
        dsc.setPassword("123456");
        dsc.setDbType(DbType.MYSQL);
        mpg.setDataSource(dsc);

        // 4、包配置
        PackageConfig pc = new PackageConfig();
        pc.setParent("com.cjc.ssyx");
        pc.setModuleName("sys"); //模块名
        pc.setController("controller");
        pc.setService("service");
        pc.setMapper("mapper");
        mpg.setPackageInfo(pc);

        // 5、策略配置
        StrategyConfig strategy = new StrategyConfig();

        strategy.setInclude("region","ware","region_ware");

        strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略

        strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
        strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作

        strategy.setRestControllerStyle(true); //restful api风格控制器
        strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符

        mpg.setStrategy(strategy);

        // 6、执行
        mpg.execute();
    }
}

9.2、查询区域开通列表功能

service实现类

/**
     * 查询开通区域列表
     * @param warePage 分页条件
     * @param regionWareQueryVo  条件搜索
     * @return
     */
    @Override
    public IPage selectAllRegionWare(Page warePage, RegionWareQueryVo regionWareQueryVo) {
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        String keyword = regionWareQueryVo.getKeyword();
        //判断输入条件是否为空
        if (!StringUtils.isEmpty(keyword)){
            //封装条件 or()表示或者
            wrapper.like(RegionWare::getRegionName,keyword).or().like(RegionWare::getWareName,keyword);
        }
        //分页查询
        Page regionWarePage = baseMapper.selectPage(warePage, wrapper);
        return regionWarePage;
    }

 controller层

 /**
     *
     * @param page 页数
     * @param limit 条数
     * @param regionWareQueryVo 搜索条件
     * @return
     */
    @ApiOperation("分页查询开通区域列表")
    @GetMapping("/{page}/{limit}")
    public Result getAll(@PathVariable("page") Long page,
                         @PathVariable("limit") Long limit,
                         RegionWareQueryVo regionWareQueryVo){
        Page warePage = new Page<>(page,limit);
        IPage list = regionWareService.selectAllRegionWare(warePage,regionWareQueryVo);
        return Result.ok(list);
    }

 接口测试:

Java --- 云尚优选项目_第20张图片

 9.3、添加开通区域及查询区域功能

 service实现类

 /**
     * 添加开通区域
     * @param regionWare
     */
    @Override
    public void saveRegionWare(RegionWare regionWare) {
        //判断区域是否开通
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        //一个区域可以开通多个仓库
        wrapper.eq(RegionWare::getWareId,regionWare.getWareId()).eq(RegionWare::getRegionId,regionWare.getRegionId());
        Integer count = baseMapper.selectCount(wrapper);
        //已经开通
        if (count > 0){
            //抛出自定义异常
          throw new CustomException(ResultCodeEnum.REGION_OPEN);
        }
        //报存信息
        baseMapper.insert(regionWare);
    }
/**
     * 根据条件查询区域
     * @param keyword
     * @return
     */
    @Override
    public List findRegionByKeyword(String keyword) {
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        wrapper.like(Region::getName,keyword);
        List regionList = baseMapper.selectList(wrapper);
        return regionList;
    }

controller层

@ApiOperation("根据条件查询区域")
    @GetMapping("/findRegionByKeyword/{keyword}")
    public Result findRegionByKeyword(@PathVariable("keyword") String keyword){
        List regionList =  regionService.findRegionByKeyword(keyword);
        return Result.ok(regionList);
    }
 @ApiOperation("添加开通区域")
    @PostMapping("/save")
    public Result saveRegionWare(@RequestBody RegionWare regionWare){
        regionWareService.saveRegionWare(regionWare);
        return Result.ok(null);
    }
 @ApiOperation("查询所有仓库")
    @GetMapping("/findAllList")
    public Result allWare(){
        List list = wareService.list();
        return Result.ok(list);
    }

接口测试:

Java --- 云尚优选项目_第21张图片

9.4、取消与删除开通区域功能

/**
     * 取消开通区域
     * @param id
     * @param status
     */
    @Override
    public void updateStatus(Long id, Integer status) {
        RegionWare regionWare = baseMapper.selectById(id);
        regionWare.setStatus(status);
        baseMapper.updateById(regionWare);
    }
@ApiOperation("删除开通区域")
    @DeleteMapping("/remove/{id}")
    public Result removeRegionWare(@PathVariable("id") Long id){
        regionWareService.removeById(id);
        return Result.ok(null);
    }
    @ApiOperation("取消开通区域")
    @PostMapping("/updateStatus/{id}/{status}")
    public Result updateStatus(@PathVariable("id") Long id,
                               @PathVariable("status") Integer status){
        regionWareService.updateStatus(id,status);
        return Result.ok(null);
    }

十、配置nginx反向代理

在配置文件中配置

 文件内容:

server {
        listen       9001;
        server_name  192.168.200.110;

        location ~ /acl/ {
            proxy_pass http://192.168.200.1:8081;
        }

        location ~ /sys/ {
            proxy_pass http://192.168.200.1:8082;
        }
    }

修改前端:

Java --- 云尚优选项目_第22张图片

测试效果:

Java --- 云尚优选项目_第23张图片

十一、商品信息管理模块 

11.1、商品分类信息模块功能 

 service实现类:

/**
     * 查询商品分类列表
     * @param categoryPage 分页条件
     * @param categoryQueryVo 查询条件
     * @return
     */
    @Override
    public IPage selectCategoryPage(Page categoryPage, CategoryQueryVo categoryQueryVo) {
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        String name = categoryQueryVo.getName();
        if (!StringUtils.isEmpty(name)){
            wrapper.like(Category::getName,name);
        }
        Page category = baseMapper.selectPage(categoryPage, wrapper);
        return category;
    }

controller层 

@Api(tags = "商品信息分类管理模块")
@RestController
@RequestMapping("admin/product/category")
@CrossOrigin
public class CategoryController {
    @Autowired
    private CategoryService categoryService;

    @ApiOperation("商品分类列表")
    @GetMapping("/{page}/{limit}")
    public Result allCategoryPage(@PathVariable("page") Long page,
                                  @PathVariable("limit") Long limit,
                                  CategoryQueryVo categoryQueryVo){
        Page categoryPage = new Page<>(page,limit);
        IPage categoryIPage = categoryService.selectCategoryPage(categoryPage,categoryQueryVo);
        return Result.ok(categoryIPage);
    }
    @ApiOperation("根据id查询商品分类信息")
    @GetMapping("/get/{id}")
    public Result getById(@PathVariable("id") Long id){
        Category category = categoryService.getById(id);
        return Result.ok(category);
    }
    @ApiOperation("添加商品分类")
    @PostMapping("/save")
    public Result saveCategory(@RequestBody Category category){
        categoryService.save(category);
        return Result.ok(null);
    }
    @ApiOperation("修改商品分类")
    @PutMapping("/update")
    public Result updateById(@RequestBody Category category){
        categoryService.updateById(category);
        return Result.ok(null);
    }
    @ApiOperation("删除商品分类")
    @DeleteMapping("/remove/{id}")
    public Result removeById(@PathVariable("id") Long id){
        categoryService.removeById(id);
        return Result.ok(null);
    }
    @ApiOperation("根据id批量删除")
    @DeleteMapping("/batchRemove")
    public Result batchRemove(@RequestBody List idList){
        categoryService.removeByIds(idList);
        return Result.ok(null);
    }
    @ApiOperation("查询所有商品分类")
    @GetMapping("/findAllList")
    public Result findAllList(){
        List list = categoryService.list();
        return Result.ok(list);
    }
}

配置nginx反向代理

Java --- 云尚优选项目_第24张图片

前端测试效果:

Java --- 云尚优选项目_第25张图片

11.2、平台属性分组模块功能

service实现类

/**
     * 分页查询平台属性分组
     * @param groupPage
     * @param attrGroupQueryVo
     * @return
     */
    @Override
    public IPage selectAttrGroupPage(Page groupPage, AttrGroupQueryVo attrGroupQueryVo) {
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        String name = attrGroupQueryVo.getName();
        if (!StringUtils.isEmpty(name)){
            wrapper.like(AttrGroup::getName,name);
        }
        Page attrGroupPage = baseMapper.selectPage(groupPage, wrapper);
        return attrGroupPage;
    }

    /**
     * 根据id降序查询所有平台属性分组
     * @return
     */
    @Override
    public List findAllList() {
        QueryWrapper wrapper = new QueryWrapper<>();
        //根据id降序查询
        wrapper.orderByAsc("id");
        List attrGroupList = baseMapper.selectList(wrapper);
        return attrGroupList;
    }

controller层

@Api(tags = "平台属性分组模块")
@RestController
@RequestMapping("admin/product/attrGroup")
@CrossOrigin
public class AttrGroupController {
    @Autowired
    private AttrGroupService attrGroupService;
    @ApiOperation("分页查询平台属性分组")
    @GetMapping("/{page}/{limit}")
    public Result allAttrGroupPage(@PathVariable("page") Long page,
                                   @PathVariable("limit") Long limit,
                                   AttrGroupQueryVo attrGroupQueryVo){
        Page groupPage = new Page<>(page,limit);
        IPage attrGroupPage = attrGroupService.selectAttrGroupPage(groupPage,attrGroupQueryVo);
        return Result.ok(attrGroupPage);
    }
    @ApiOperation("查询所有平台属性")
    @GetMapping("/findAllList")
    public Result findAllList(){
        List list = attrGroupService.findAllList();
        return Result.ok(list);
    }
    @ApiOperation("根据id查询")
    @GetMapping("/get/{id}")
    public Result getById(@PathVariable("id") Long id){
        AttrGroup attrGroup = attrGroupService.getById(id);
        return Result.ok(attrGroup);
    }
    @ApiOperation("添加平台属性分组")
    @PostMapping("/save")
    public Result saveAttrGroup(@RequestBody AttrGroup attrGroup){
        attrGroupService.save(attrGroup);
        return Result.ok(null);
    }
    @ApiOperation("修改平台属性分组")
    @PutMapping("/update")
    public Result updateAttrGroup(@RequestBody AttrGroup attrGroup){
        attrGroupService.updateById(attrGroup);
        return Result.ok(null);
    }
    @ApiOperation("删除平台属性分组")
    @DeleteMapping("/remove/{id}")
    public Result remove(@PathVariable("id") Long id){
        attrGroupService.removeById(id);
        return Result.ok(null);
    }
    @ApiOperation("批量删除平台属性分组")
    @DeleteMapping("/batchRemove")
    public Result batchRemove(@RequestBody List idList){
        attrGroupService.removeByIds(idList);
        return Result.ok(null);
    }
}

前端效果图:

Java --- 云尚优选项目_第26张图片

11.3、平台属性管理模块功能

service实现类

/**
     * 查询平台属性列表
     * @param groupId 平台属性分组id
     * @return
     */
    @Override
    public List selectAttrByGroupId(Long groupId) {
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Attr::getAttrGroupId, groupId);
        List attrList = baseMapper.selectList(wrapper);
        return attrList;
    }

 controller层

@Api(tags = "平台属性管理模块")
@RestController
@RequestMapping("admin/product/attr")
@CrossOrigin
public class AttrController {
    @Autowired
    private AttrService attrService;

    @ApiOperation("根据平台属性分组id查询")
    @GetMapping("/{groupId}")
    public Result queryAttrByGroupId(@PathVariable("groupId") Long groupId){
        List list = attrService.selectAttrByGroupId(groupId);
        return Result.ok(list);
    }
    @ApiOperation("根据id查询平台属性")
    @GetMapping("/get/{id}")
    public Result getById(@PathVariable("id") Long id){
        Attr attr = attrService.getById(id);
        return Result.ok(attr);
    }
    @ApiOperation("添加平台属性")
    @PostMapping("/save")
    public Result saveAttr(@RequestBody Attr attr){
        attrService.save(attr);
        return Result.ok(null);
    }
    @ApiOperation("修改平台属性")
    @PutMapping("/update")
    public Result updateAttr(@RequestBody Attr attr){
        attrService.updateById(attr);
        return Result.ok(null);
    }
    @ApiOperation("删除平台属性")
    @DeleteMapping("/remove/{id}")
    public Result removeById(@PathVariable("id") Long id){
        attrService.removeById(id);
        return Result.ok(null);
    }
    @ApiOperation("批量删除平台属性")
    @DeleteMapping("/batchRemove")
    public Result batchRemove(@RequestBody List id){
        attrService.removeByIds(id);
        return Result.ok(null);
    }
}

前端效果图:

Java --- 云尚优选项目_第27张图片

11.4、SKU列表模块 

11.4.1、SKU信息分页条件查询功能

service实现类 

/**
     * 分页条件查询SKU信息
     * @param skuInfoPage
     * @param skuInfoQueryVo
     * @return
     */
    @Override
    public IPage selectAllSKUPage(Page skuInfoPage, SkuInfoQueryVo skuInfoQueryVo) {
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        Long categoryId = skuInfoQueryVo.getCategoryId();
        String skuType = skuInfoQueryVo.getSkuType();
        String keyword = skuInfoQueryVo.getKeyword();
        //封装查询条件
        if (!StringUtils.isEmpty(categoryId)){
            wrapper.like(SkuInfo::getCategoryId,categoryId);
        }
        if (!StringUtils.isEmpty(skuType)){
            wrapper.like(SkuInfo::getSkuType,skuType);
        }
        if (!StringUtils.isEmpty(keyword)){
            wrapper.like(SkuInfo::getSkuName,keyword);
        }
        Page skuInfoPageList = baseMapper.selectPage(skuInfoPage, wrapper);
        return skuInfoPageList;
    }

 controller层

@ApiOperation("sku信息分页条件查询")
    @GetMapping("/{page}/{limit}")
    public Result allSKUPage(@PathVariable Long page,
                             @PathVariable Long limit,
                             SkuInfoQueryVo skuInfoQueryVo){
        Page skuInfoPage = new Page<>(page,limit);
        IPage skuInfoIPage = skuInfoService.selectAllSKUPage(skuInfoPage,skuInfoQueryVo);
        return Result.ok(skuInfoIPage);
    }

前端效果图:

Java --- 云尚优选项目_第28张图片

11.4.2、开通阿里云对象存储OSS

11.4.2.1、创建自己的Bucket

Java --- 云尚优选项目_第29张图片

11.4.2.2、创建自己的AccessKey

Java --- 云尚优选项目_第30张图片

11.4.3、SKU图片上传功能

导入pom依赖


        
            com.aliyun.oss
            aliyun-sdk-oss
            3.10.2
        
        
        
            joda-time
            joda-time
            2.10.3
        

 修改yml配置文件

aliyun:
  endpoint: 自己bucket地域节点
  keyid: 自己的AccessKey
  keysecret: 自己的AccessKey的secret
  bucketname: 自己的bucket名字

service实现类

@Service
public class FileUploadServiceImpl implements FileUploadService {
    @Value("${aliyun.endpoint}")
    private String endpoint;
    @Value("${aliyun.keyid}")
    private String accessKeyId;
    @Value("${aliyun.keysecret}")
    private String secreKey;
    @Value("${aliyun.bucketname}")
    private String bucketName;
    /**
     * 图片上传
     * @param file
     * @return
     */
    @Override
    public String fileUploads(MultipartFile file) {
        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint,accessKeyId,secreKey);
        try {
            //上传文件输入流
            InputStream inputStream = file.getInputStream();
            //获取文件实际名称
            String filename = file.getOriginalFilename();
            String uuid = UUID.randomUUID().toString().replaceAll("-", "");
            filename = uuid + filename;
            //跟据当前时间对上传文件进行分组
            String currentDateTime = new DateTime().toString("yyyy/MM/dd");
            filename = currentDateTime +"/"+ filename;
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, filename, inputStream);
            putObjectRequest.setProcess("true");
            PutObjectResult result = ossClient.putObject(putObjectRequest);
            //文件上传成功信息
            System.out.println(result.getResponse().getStatusCode());
            //返回文件路径
            String uri = result.getResponse().getUri();
            System.out.println(uri);
            return uri;
        } catch (Exception ce) {
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return null;
    }
}

controller层

@Api(tags = "文件上传模块")
@RestController
@RequestMapping("admin/product")
@CrossOrigin
public class FileUploadController {
    @Autowired
    private FileUploadService fileUploadService;
    @ApiOperation("图片上传")
    @PostMapping("/fileUpload")
    public Result fileUpload(MultipartFile file){
        String url = fileUploadService.fileUploads(file);
        return Result.ok(url);
    }
}

11.4.4、SKU信息添加功能

service实现类

/**
     * 添加商品SKU信息
     * @param skuInfoVo
     */
    @Override
    @Transactional
    public void saveSKUInfo(SkuInfoVo skuInfoVo) {
        //保存SKU基本信息
        SkuInfo skuInfo = new SkuInfo();
        //将SkuInfoVo中的值赋值给SkuInfo
        BeanUtils.copyProperties(skuInfoVo,skuInfo);
        baseMapper.insert(skuInfo);
        //保存SKU海报信息
        List skuPosterList = skuInfoVo.getSkuPosterList();
        //判断集合是否为空
        if (!CollectionUtils.isEmpty(skuPosterList)){
            //向每个海报表里添加商品SKUid
            for (SkuPoster skuPoster : skuPosterList) {
                skuPoster.setSkuId(skuInfo.getId());
            }
            skuPosterService.saveBatch(skuPosterList);
        }
        //保存SKU图片
        List skuImagesList = skuInfoVo.getSkuImagesList();
        if (!CollectionUtils.isEmpty(skuImagesList)){
            for (SkuImage skuImage : skuImagesList) {
                skuImage.setSkuId(skuInfo.getId());
            }
            skuImageService.saveBatch(skuImagesList);
        }
        //保存SKU的属性
        List skuAttrValueList = skuInfoVo.getSkuAttrValueList();
        if (!CollectionUtils.isEmpty(skuAttrValueList)){
            for (SkuAttrValue skuAttrValue : skuAttrValueList) {
                skuAttrValue.setSkuId(skuInfo.getId());
            }
            skuAttrValueService.saveBatch(skuAttrValueList);
        }
    }

 controller层

@ApiOperation("添加商品SKU信息")
    @PostMapping("/save")
    public Result saveSKU(@RequestBody SkuInfoVo skuInfoVo){
        skuInfoService.saveSKUInfo(skuInfoVo);
        return Result.ok(null);
    }

 11.4.4.1、文件上传时前后端端口不一致问题

Java --- 云尚优选项目_第31张图片

前端效果:

Java --- 云尚优选项目_第32张图片

11.4.5、SKU商品的修改与删除功能

service实现类

/**
     * 根据id查询
     * @param id
     * @return
     */
    @Override
    public SkuInfoVo selectSkuById(Long id) {
        SkuInfoVo skuInfoVo = new SkuInfoVo();
        //查询Sku基本信息
        SkuInfo skuInfo = baseMapper.selectById(id);
        //将信息拷贝到skuInfoVo
        BeanUtils.copyProperties(skuInfo,skuInfoVo);
        LambdaQueryWrapper skuImageWrapper = new LambdaQueryWrapper<>();
        skuImageWrapper.eq(SkuImage::getSkuId,id);
        //查询商品图片
        List skuImageList = skuImageService.list(skuImageWrapper);
        skuInfoVo.setSkuImagesList(skuImageList);
        //查询商品海报信息
        LambdaQueryWrapper skuPosterWrapper = new LambdaQueryWrapper<>();
        skuPosterWrapper.eq(SkuPoster::getSkuId,id);
        List skuPosterList = skuPosterService.list(skuPosterWrapper);
        skuInfoVo.setSkuPosterList(skuPosterList);
        //查询商品属性信息
        LambdaQueryWrapper skuAttrValueWrapper = new LambdaQueryWrapper<>();
        skuAttrValueWrapper.eq(SkuAttrValue::getSkuId,id);
        List skuAttrValueList = skuAttrValueService.list(skuAttrValueWrapper);
        skuInfoVo.setSkuAttrValueList(skuAttrValueList);
        return skuInfoVo;
    }

    /**
     * 修改SKU信息
     * @param skuInfoVo
     */
    @Override
    @Transactional
    public void updateSkuInfo(SkuInfoVo skuInfoVo) {
        //修改SKU基本信息
        SkuInfo skuInfo = new SkuInfo();
        BeanUtils.copyProperties(skuInfoVo,skuInfo);
        baseMapper.updateById(skuInfo);
        //修改海报信息
        Long skuId = skuInfoVo.getId();
        LambdaQueryWrapper skuPosterWrapper = new LambdaQueryWrapper<>();
        skuPosterWrapper.eq(SkuPoster::getSkuId,skuId);
        boolean removeSkuPoster = skuPosterService.remove(skuPosterWrapper);
        if (removeSkuPoster){
            List skuPosterList = skuInfoVo.getSkuPosterList();
            //判断集合是否为空
            if (!CollectionUtils.isEmpty(skuPosterList)){
                //向每个海报表里添加商品SKUid
                for (SkuPoster skuPoster : skuPosterList) {
                    skuPoster.setSkuId(skuId);
                }
                skuPosterService.saveBatch(skuPosterList);
            }
        }
        //修改sku商品图片
        LambdaQueryWrapper skuImageWrapper = new LambdaQueryWrapper<>();
        skuImageWrapper.eq(SkuImage::getSkuId,skuId);
        boolean removeSkuImage = skuImageService.remove(skuImageWrapper);
        if (removeSkuImage){
            List skuImagesList = skuInfoVo.getSkuImagesList();
            if (!CollectionUtils.isEmpty(skuImagesList)){
                for (SkuImage skuImage : skuImagesList) {
                    skuImage.setSkuId(skuInfo.getId());
                }
                skuImageService.saveBatch(skuImagesList);
            }
        }
        //修改商品属性信息
        LambdaQueryWrapper skuAttrValueWrapper = new LambdaQueryWrapper<>();
        skuAttrValueWrapper.eq(SkuAttrValue::getSkuId,skuId);
        boolean removeSkuAttrValue = skuAttrValueService.remove(skuAttrValueWrapper);
        if (removeSkuAttrValue){
            //保存SKU的属性
            List skuAttrValueList = skuInfoVo.getSkuAttrValueList();
            if (!CollectionUtils.isEmpty(skuAttrValueList)){
                for (SkuAttrValue skuAttrValue : skuAttrValueList) {
                    skuAttrValue.setSkuId(skuInfo.getId());
                }
                skuAttrValueService.saveBatch(skuAttrValueList);
            }
        }
    }

 controller层

 @ApiOperation("根据id查询商品SKU信息")
    @GetMapping("/get/{id}")
    public Result getSKUById(@PathVariable("id") Long id){
        SkuInfoVo skuInfoVo = skuInfoService.selectSkuById(id);
        return Result.ok(skuInfoVo);
    }
    @ApiOperation("修改商品SKU信息")
    @PutMapping("/update")
    public Result updateSku(@RequestBody SkuInfoVo skuInfoVo){
        skuInfoService.updateSkuInfo(skuInfoVo);
        return Result.ok(null);
    }
    @ApiOperation("删除商品SKU信息")
    @DeleteMapping("/remove/{id}")
    public Result removeSKUById(@PathVariable("id") Long id){
        skuInfoService.removeById(id);
        return Result.ok(null);
    }
    @ApiOperation("根据id批量删除")
    @DeleteMapping("/batchRemove")
    public Result batchRemove(@RequestBody List idList){
        skuInfoService.removeByIds(idList);
        return Result.ok(null);
    }

前端效果:

Java --- 云尚优选项目_第33张图片

11.4.6、SKU商品信息的审核、上下架、新人专享功能 

service实现类

//商品审核
    @Override
    public void checkSku(Long skuId, Integer status) {
        SkuInfo skuInfo = baseMapper.selectById(skuId);
        skuInfo.setCheckStatus(status);
        baseMapper.updateById(skuInfo);
    }

    /**
     * 商品上下架
     * @param skuId
     * @param status
     */
    @Override
    public void publishSku(Long skuId, Integer status) {
        SkuInfo skuInfo = baseMapper.selectById(skuId);
        //1为上架
        if (status == 1){
            skuInfo.setPublishStatus(status);
            baseMapper.updateById(skuInfo);
            //TODO 后面整合mq将数据同步到es里面
        }else { //下架
            skuInfo.setPublishStatus(status);
            baseMapper.updateById(skuInfo);
        }
    }

    /**
     * 新人专享
     * @param skuId
     * @param status
     */
    @Override
    public void isNewPerson(Long skuId, Integer status) {
        SkuInfo skuInfo = baseMapper.selectById(skuId);
        skuInfo.setIsNewPerson(status);
        baseMapper.updateById(skuInfo);
    }

 controller层

@ApiOperation("商品审核")
    @GetMapping("/check/{skuId}/{status}")
    public Result checkSku(@PathVariable("skuId") Long skuId,
                           @PathVariable("status") Integer status){
        skuInfoService.checkSku(skuId,status);
        return Result.ok(null);
    }
    @ApiOperation("商品上下架")
    @GetMapping("/publish/{skuId}/{status}")
    public Result publishSku(@PathVariable("skuId") Long skuId,
                             @PathVariable("status") Integer status){
        skuInfoService.publishSku(skuId,status);
        return Result.ok(null);
    }
    @ApiOperation("新人专享")
    @GetMapping("/isNewPerson/{skuId}/{status}")
    public Result isNewPerson(@PathVariable("skuId") Long skuId,
                              @PathVariable("status") Integer status){
        skuInfoService.isNewPerson(skuId,status);
        return Result.ok(null);

    }

前端效果图:

Java --- 云尚优选项目_第34张图片

十二、Nacos下载与安装 

下载地址:Releases · alibaba/nacos (github.com) 

下载完解压即可

Java --- 云尚优选项目_第35张图片

运行命令:startup.cmd -m standalone

测试访问:

Java --- 云尚优选项目_第36张图片

12.1、修改springboot配置文件

cloud:
  nacos:
    discovery:
      server-addr: localhost:8848

Java --- 云尚优选项目_第37张图片

在启动类上添加注解@EnableDiscoveryClient

Java --- 云尚优选项目_第38张图片

测试效果:

Java --- 云尚优选项目_第39张图片

十三、整合ES+MQ实现商品上下架

功能架构图:

Java --- 云尚优选项目_第40张图片

13.1、安装Elasticsearch

核心:提高搜索的性能 

 下载解压即可:

测试访问:

Java --- 云尚优选项目_第41张图片

13.2、安装Kibana

 修改kibana.yml

Java --- 云尚优选项目_第42张图片

Java --- 云尚优选项目_第43张图片

Java --- 云尚优选项目_第44张图片

测试访问:

Java --- 云尚优选项目_第45张图片

13.3、整合Elasticsearch

导入pom依赖


            org.springframework.boot
            spring-boot-starter-data-elasticsearch
        

 修改yml配置文件

server:
  port: 8084
feign:
  sentinel:
    enabled: true
  client:
    config:
      default:   #配置全局的feign的调用超时时间  如果 有指定的服务配置 默认的配置不会生效
        connectTimeout: 30000 # 指定的是 消费者 连接服务提供者的连接超时时间 是否能连接  单位是毫秒
        readTimeout: 50000  # 指定的是调用服务提供者的 服务 的超时时间()  单位是毫秒
spring:
  main:
    allow-bean-definition-overriding: true #当遇到同样名字的时候,是否允许覆盖注册
  elasticsearch:
    rest:
      uris: http://localhost:9200

修改主启动类

//exclude = DataSourceAutoConfiguration.class取消数据源自动配置
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
@EnableDiscoveryClient
@EnableFeignClients
public class ServiceSearchApplication {
    public static void main(String[] args) {
        SpringApplication.run(ServiceSearchApplication.class,args);
    }
}

接口类:

public interface SkuRepository extends ElasticsearchRepository {
}

13.4、完成远程调用接口功能

13.4.1、发送消息功能:

@RestController
@RequestMapping("/api/product")
public class ProductRepositoryController {
    @Autowired
    private CategoryService categoryService;
    @Autowired
    private SkuInfoService skuInfoService;
    @ApiOperation("根据id获取分类信息")
    @GetMapping("/inner/getCategory/{categoryId}")
    public Category getCategory(@PathVariable("categoryId") Long categoryId){
        Category category = categoryService.getById(categoryId);
        return category;
    }
    @ApiOperation("根据skuid查询")
    @GetMapping("/inner/getSkuInfo/{skuId}")
    public SkuInfo getSkuInfo(@PathVariable("skuId") Long skuId){
        SkuInfo skuInfo = skuInfoService.getById(skuId);
        return skuInfo;
    }
}

 13.4.2、远程调用接口

@FeignClient(value = "service-product")
public interface ProduceFeignClient {
    @GetMapping("/api/product/inner/getCategory/{categoryId}")
    public Category getCategory(@PathVariable("categoryId") Long categoryId);

    @GetMapping("/api/product/inner/getSkuInfo/{skuId}")
    public SkuInfo getSkuInfo(@PathVariable("skuId") Long skuId);
}

 13.4.3、接收消息功能:

service实现类

@Service
public class SkuServiceImpl implements SkuService {
    @Autowired
    private SkuRepository skuRepository;
    @Autowired
    private ProduceFeignClient produceFeignClient;

    //上架
    @Override
    public void upperSku(Long skuId) {
        //通过远程调用获取相关信息
        SkuInfo skuInfo = produceFeignClient.getSkuInfo(skuId);
        if (skuInfo == null){
            throw new CustomException("查询商品信息不存在",205);
        }
        Category category = produceFeignClient.getCategory(skuInfo.getCategoryId());
        SkuEs skuEs = new SkuEs();
        //封装分类
        if (category != null){
            skuEs.setCategoryId(category.getId());
            skuEs.setCategoryName(category.getName());
        }
        //封装sku信息
        skuEs.setId(skuInfo.getId());
        skuEs.setKeyword(skuInfo.getSkuName()+","+skuEs.getCategoryName());
        skuEs.setWareId(skuInfo.getWareId());
        skuEs.setIsNewPerson(skuInfo.getIsNewPerson());
        skuEs.setImgUrl(skuInfo.getImgUrl());
        skuEs.setTitle(skuInfo.getSkuName());
        if(skuInfo.getSkuType() == SkuType.COMMON.getCode()) {//普通商品
            skuEs.setSkuType(0);
            skuEs.setPrice(skuInfo.getPrice().doubleValue());
            skuEs.setStock(skuInfo.getStock());
            skuEs.setSale(skuInfo.getSale());
            skuEs.setPerLimit(skuInfo.getPerLimit());
        } else {
            //TODO 待完善-秒杀商品
        }
        //添加SkuEs
        skuRepository.save(skuEs);
    }
    //下架
    @Override
    public void lowerSku(Long skuId) {
        skuRepository.deleteById(skuId);
    }
}

controller层

@RestController
@RequestMapping("api/search/sku")
public class SkuApiController {
    @Autowired
    private SkuService skuService;
    @ApiOperation("商品上架")
    @GetMapping("/inner/upperSku/{skuId}")
    public Result upperSku(@PathVariable("skuId") Long skuId){
        skuService.upperSku(skuId);
        return Result.ok(null);
    }
    @ApiOperation("商品下架")
    @GetMapping("/inner/lowerSku/{skuId}")
    public Result lowerSku(@PathVariable("skuId") Long skuId){
        skuService.lowerSku(skuId);
        return Result.ok(null);
    }
}

13.5、在docker安装rabbitMQ

 拉取镜像:docker pull rabbitmq:3.8-management

 创建启动容器:

docker run -d --restart=always -p 5672:5672 -p 15672:15672 --name rabbitmq rabbitmq:3.8-management

Java --- 云尚优选项目_第46张图片

Java --- 云尚优选项目_第47张图片

13.6、整合rabbitmq

导入pom依赖


        
            org.springframework.cloud
            spring-cloud-starter-bus-amqp
        

编写相关配置类

@Service
public class RabbitService {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 发送消息
     * @param exchange 交换机
     * @param routingKey 路由key
     * @param message 消息
     * @return
     */
    public boolean sendMessage(String exchange,String routingKey,Object message){
       rabbitTemplate.convertAndSend(exchange, routingKey, message);
       return true;
    }
}
//消息转换器
@Configuration
public class MQConfig {
    @Bean
    public MessageConverter messageConverter(){
        return new Jackson2JsonMessageConverter();
    }
}
@Component
public class MQProducerAckConfig implements RabbitTemplate.ReturnCallback,RabbitTemplate.ConfirmCallback {

    //  我们发送消息使用的是 private RabbitTemplate rabbitTemplate; 对象
    //  如果不做设置的话 当前的rabbitTemplate 与当前的配置类没有任何关系!
    @Autowired
    private RabbitTemplate rabbitTemplate;

    //  设置 表示修饰一个非静态的void方法,在服务器加载Servlet的时候运行。并且只执行一次!
    @PostConstruct
    public void init(){
        rabbitTemplate.setReturnCallback(this);
        rabbitTemplate.setConfirmCallback(this);
    }

    /**
     * 表示消息是否正确发送到了交换机上
     * @param correlationData   消息的载体
     * @param ack   判断是否发送到交换机上
     * @param cause 原因
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {
        if(ack){
            System.out.println("消息发送成功!");
        }else {
            System.out.println("消息发送失败!"+cause);
        }
    }

    /**
     * 消息如果没有正确发送到队列中,则会走这个方法!如果消息被正常处理,则这个方法不会走!
     * @param message
     * @param replyCode
     * @param replyText
     * @param exchange
     * @param routingKey
     */
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
        System.out.println("消息主体: " + new String(message.getBody()));
        System.out.println("应答码: " + replyCode);
        System.out.println("描述:" + replyText);
        System.out.println("消息使用的交换器 exchange : " + exchange);
        System.out.println("消息使用的路由键 routing : " + routingKey);
    }
}
public class MqConst {
    /**
     * 消息补偿
     */
    public static final String MQ_KEY_PREFIX = "ssyx.mq:list";
    public static final int RETRY_COUNT = 3;

    /**
     * 商品上下架
     */
    public static final String EXCHANGE_GOODS_DIRECT = "ssyx.goods.direct";
    public static final String ROUTING_GOODS_UPPER = "ssyx.goods.upper";
    public static final String ROUTING_GOODS_LOWER = "ssyx.goods.lower";
    //队列
    public static final String QUEUE_GOODS_UPPER  = "ssyx.goods.upper";
    public static final String QUEUE_GOODS_LOWER  = "ssyx.goods.lower";

    /**
     * 团长上下线
     */
    public static final String EXCHANGE_LEADER_DIRECT = "ssyx.leader.direct";
    public static final String ROUTING_LEADER_UPPER = "ssyx.leader.upper";
    public static final String ROUTING_LEADER_LOWER = "ssyx.leader.lower";
    //队列
    public static final String QUEUE_LEADER_UPPER  = "ssyx.leader.upper";
    public static final String QUEUE_LEADER_LOWER  = "ssyx.leader.lower";

    //订单
    public static final String EXCHANGE_ORDER_DIRECT = "ssyx.order.direct";
    public static final String ROUTING_ROLLBACK_STOCK = "ssyx.rollback.stock";
    public static final String ROUTING_MINUS_STOCK = "ssyx.minus.stock";

    public static final String ROUTING_DELETE_CART = "ssyx.delete.cart";
    //解锁普通商品库存
    public static final String QUEUE_ROLLBACK_STOCK = "ssyx.rollback.stock";
    public static final String QUEUE_SECKILL_ROLLBACK_STOCK = "ssyx.seckill.rollback.stock";
    public static final String QUEUE_MINUS_STOCK = "ssyx.minus.stock";
    public static final String QUEUE_DELETE_CART = "ssyx.delete.cart";

    //支付
    public static final String EXCHANGE_PAY_DIRECT = "ssyx.pay.direct";
    public static final String ROUTING_PAY_SUCCESS = "ssyx.pay.success";
    public static final String QUEUE_ORDER_PAY  = "ssyx.order.pay";
    public static final String QUEUE_LEADER_BILL  = "ssyx.leader.bill";

    //取消订单
    public static final String EXCHANGE_CANCEL_ORDER_DIRECT = "ssyx.cancel.order.direct";
    public static final String ROUTING_CANCEL_ORDER = "ssyx.cancel.order";
    //延迟取消订单队列
    public static final String QUEUE_CANCEL_ORDER  = "ssyx.cancel.order";

    /**
     * 定时任务
     */
    public static final String EXCHANGE_DIRECT_TASK = "ssyx.exchange.direct.task";
    public static final String ROUTING_TASK_23 = "ssyx.task.23";
    //队列
    public static final String QUEUE_TASK_23  = "ssyx.queue.task.23";
}

13.7、完善商品上下架功能

 修改相关yml配置文件

rabbitmq:
    host: 192.168.200.110
    port: 5672
    username: guest
    password: guest
    publisher-confirm-type: CORRELATED  #发布确认模式,消息是否被成功发送到交换机
    publisher-returns: true
    listener:
      simple:
        prefetch: 1
        concurrency: 3
        acknowledge-mode: manual   #消费端手动确认

完善商品上架 

/**
     * 商品上下架
     * @param skuId
     * @param status
     */
    @Override
    public void publishSku(Long skuId, Integer status) {
        SkuInfo skuInfo = baseMapper.selectById(skuId);
        //1为上架
        if (status == 1){
            skuInfo.setPublishStatus(status);
            baseMapper.updateById(skuInfo);
            //整合mq将数据同步到es里面
            rabbitService.sendMessage(MqConst.EXCHANGE_GOODS_DIRECT,
                                      MqConst.ROUTING_GOODS_UPPER,skuId);
        }else { //下架
            skuInfo.setPublishStatus(status);
            baseMapper.updateById(skuInfo);
            //整合mq将数据同步到es里面
            rabbitService.sendMessage(MqConst.EXCHANGE_GOODS_DIRECT,
                                      MqConst.ROUTING_GOODS_LOWER,skuId);
        }
    }

完善商品删除:

@ApiOperation("删除商品SKU信息")
    @DeleteMapping("/remove/{id}")
    public Result removeSKUById(@PathVariable("id") Long id){
        skuInfoService.removeById(id);
        //删除商品同时同步到es中
        rabbitService.sendMessage(MqConst.EXCHANGE_GOODS_DIRECT,
                MqConst.ROUTING_GOODS_LOWER,id);
        return Result.ok(null);
    }
    @ApiOperation("根据id批量删除")
    @DeleteMapping("/batchRemove")
    public Result batchRemove(@RequestBody List idList){
        skuInfoService.removeByIds(idList);
        for (Long skuId : idList) {
            //删除商品同时同步到es中
            rabbitService.sendMessage(MqConst.EXCHANGE_GOODS_DIRECT,
                    MqConst.ROUTING_GOODS_LOWER,skuId);
        }
        return Result.ok(null);
    }

消息接收:

@Component
public class SkuReceiver {
    @Autowired
    private SkuService skuService;

    /**
     * 商品上架
     * @param skuId
     * @param message
     * @param channel
     * @throws IOException
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_GOODS_UPPER,durable = "true"),
            exchange = @Exchange(value = MqConst.EXCHANGE_GOODS_DIRECT),
            key = {MqConst.ROUTING_GOODS_UPPER}))
    public void upperSku(Long skuId, Message message, Channel channel) throws IOException {
        if (!StringUtils.isEmpty(skuId)){
            //调用商品上架方法
            skuService.upperSku(skuId);
        }
        //手动确认
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

    /**
     * 商品下架
     * @param skuId
     * @param message
     * @param channel
     * @throws IOException
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_GOODS_LOWER,durable = "true"),
            exchange = @Exchange(value = MqConst.EXCHANGE_GOODS_DIRECT),
            key = {MqConst.ROUTING_GOODS_LOWER}))
    public void lowerSku(Long skuId, Message message, Channel channel) throws IOException {
        if (!StringUtils.isEmpty(skuId)){
            skuService.lowerSku(skuId);
        }
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }
}

测试结果:

Java --- 云尚优选项目_第48张图片

 Java --- 云尚优选项目_第49张图片

点击下架或者删除都应该清空es

十四、营销活动管理

数据库表

Java --- 云尚优选项目_第50张图片

14.1、活动列表的crud功能

service实现类:

//分页查询活动列表
    @Override
    public IPage selectActivityInfoPage(Page pageParam) {
        Page activityInfoPage = baseMapper.selectPage(pageParam, null);
        //获取列表数据
        List activityInfoList = activityInfoPage.getRecords();
        //将活动类型进行封装
        activityInfoList.stream().forEach(c-> {
            c.setActivityTypeString(c.getActivityType().getComment());
        });
        return activityInfoPage;
    }

controller层 

@Autowired
    private ActivityInfoService activityInfoService;
    @ApiOperation("分页查询活动列表")
    @GetMapping("/{page}/{limit}")
    public Result allActivityInfoPage(@PathVariable("page") Long page,
                                      @PathVariable("limit") Long limit){
        Page pageParam = new Page<>(page,limit);
        IPage activityInfoList = activityInfoService.selectActivityInfoPage(pageParam);
        return Result.ok(activityInfoList);
    }
    @ApiOperation("添加活动列表")
    @PostMapping("/save")
    public Result saveActivityInfo(@RequestBody ActivityInfo activityInfo){
        activityInfoService.save(activityInfo);
        return Result.ok(null);
    }
    @ApiOperation("根据id查询")
    @GetMapping("/get/{id}")
    public Result getById(@PathVariable("id") Long id){
        ActivityInfo activityInfo = activityInfoService.getById(id);
        activityInfo.setActivityTypeString(activityInfo.getActivityType().getComment());
        return Result.ok(activityInfo);
    }
    @ApiOperation("修改活动列表")
    @PutMapping("/update")
    public Result updateActivityInfo(@RequestBody ActivityInfo activityInfo){
        activityInfoService.updateById(activityInfo);
        return Result.ok(null);
    }
    @ApiOperation("删除活动列表")
    @DeleteMapping("/remove/{id}")
    public Result removeById(@PathVariable("id") Long id){
        activityInfoService.removeById(id);
        return Result.ok(null);
    }
    @ApiOperation("批量删除活动列表")
    @DeleteMapping("/batchRemove")
    public Result batchRemove(@RequestBody List idList){
        activityInfoService.removeByIds(idList);
        return Result.ok(null);
    }

测试报错:

org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.executor.result.ResultMapException: Error attempting to get column 'activity_type' from result set.  Cause: java.lang.IllegalArgumentException: No enum constant com.cjc.ssyx.enums.ActivityType.1
    at org.mybatis.spring.MyBatisExceptionTranslator.translateExceptionIfPossible(MyBatisExceptionTranslator.java:92)
    at org.mybatis.spring.SqlSessionTemplate$SqlSessionInterceptor.invoke(SqlSessionTemplate.java:440)
    at com.sun.proxy.$Proxy114.selectList(Unknown Source)
    at org.mybatis.spring.SqlSessionTemplate.selectList(SqlSessionTemplate.java:223)
    at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.executeForIPage(MybatisMapperMethod.java:122)
    at com.baomidou.mybatisplus.core.override.MybatisMapperMethod.execute(MybatisMapperMethod.java:87)
    at com.baomidou.mybatisplus.core.override.MybatisMapperProxy$PlainMethodInvoker.invoke(MybatisMapperProxy.java:148)
    at com.baomidou.mybatisplus.core.override.MybatisMapperProxy.invoke(MybatisMapperProxy.java:89)
    at com.sun.proxy.$Proxy115.selectPage(Unknown Source)
    at com.cjc.ssyx.activity.service.impl.ActivityInfoServiceImpl.selectActivityInfoPage(ActivityInfoServiceImpl.java:47)
    at com.cjc.ssyx.activity.service.impl.ActivityInfoServiceImpl$$FastClassBySpringCGLIB$$1f407332.invoke()
    at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)

解决:

Java --- 云尚优选项目_第51张图片

页面效果:

Java --- 云尚优选项目_第52张图片

14.2、查询活动规则功能

远程调用接口及方法

 @ApiOperation("根据skuid")
    @PostMapping ("/inner/findSkuInfoList")
    public List findSkuInfoList(@RequestBody List skuIds){
        List skuInfos = skuInfoService.listByIds(skuIds);
        return skuInfos;
    }
 @PostMapping("/api/product/inner/findSkuInfoList")
    public List findSkuInfoList(@RequestBody List skuIds);

service实现类

//根据id查询活动规则列表
    @Override
    public Map findActivityRuleList(Long id) {
        HashMap map = new HashMap<>();
        //查询活动规则表
        LambdaQueryWrapper activityRuleWrapper = new LambdaQueryWrapper<>();
        activityRuleWrapper.eq(ActivityRule::getActivityId,id);
        List activityRules = activityRuleMapper.selectList(activityRuleWrapper);
        map.put("activityRuleList",activityRules);
        //查询商品列表
        LambdaQueryWrapper activitySkuWrapper = new LambdaQueryWrapper<>();
        activitySkuWrapper.eq(ActivitySku::getActivityId,id);
        List activitySkus = activitySkuMapper.selectList(activitySkuWrapper);
        //获取所有的skuid
        List skuidList = activitySkus.stream().map(ActivitySku::getSkuId).collect(Collectors.toList());
        //远程调用查询商品
        List skuInfoList = null;
        //判断集合是否为空
        if (!CollectionUtils.isEmpty(skuidList)){
            skuInfoList = produceFeignClient.findSkuInfoList(skuidList);
            map.put("skuInfoList",skuInfoList);
        }else {
            map.put("skuInfoList",skuidList);
        }
        return map;
    }

controller层:

 @ApiOperation("根据活动id查询活动规则")
    @GetMapping("/findActivityRuleList/{id}")
    public Result findActivityRuleList(@PathVariable("id") Long id){
        Map map = activityInfoService.findActivityRuleList(id);
        return Result.ok(map);
    }

14.3、添加活动规则功能

远程接口:

//根据关键字查询
    @Override
    public List findListSkuInfo(String keyword) {
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        if (!StringUtils.isEmpty(keyword)){
            wrapper.like(SkuInfo::getSkuName,keyword);
        }
        List list = baseMapper.selectList(wrapper);
        return list;
    }
 @ApiOperation("根据条件查询")
    @GetMapping("/inner/findListSkuInfo/{keyword}")
    public List findListSkuInfo(@PathVariable("keyword") String keyword){
        List list = skuInfoService.findListSkuInfo(keyword);
        return list;
    }

service实现类

//添加活动规则
    @Override
    public void saveActivityRule(ActivityRuleVo activityRuleVo) {
        Long activityId = activityRuleVo.getActivityId();
        LambdaQueryWrapper activityRuleWrapper = new LambdaQueryWrapper<>();
        //删除之前添加的营销活动规则数据
        activityRuleWrapper.eq(ActivityRule::getActivityId,activityId);
        activityRuleMapper.delete(activityRuleWrapper);
        //删除之前添加的营销活动商品数据
        LambdaQueryWrapper activitySkuWrapper = new LambdaQueryWrapper<>();
        activitySkuWrapper.eq(ActivitySku::getActivityId,activityId);
        activitySkuMapper.delete(activitySkuWrapper);
        //获取规则列表数据并添加
        List activityRuleList = activityRuleVo.getActivityRuleList();
        ActivityInfo activityInfo = baseMapper.selectById(activityId);
        //遍历添加
        activityRuleList.stream().forEach(c->{
            c.setActivityId(activityId);//活动id
            c.setActivityType(activityInfo.getActivityType());//活动类型
            activityRuleMapper.insert(c);
        });
        //获取活动商品数据并添加
        List activitySkuList = activityRuleVo.getActivitySkuList();
        activitySkuList.stream().forEach(c->{
            c.setActivityId(activityId);
            activitySkuMapper.insert(c);
        });
    }
    //关键子查询不在活动中的商品
    @Override
    public List findSkuInfoByKeyword(String keyword) {
        //远程调用查询商品信息
        List skuInfoList = produceFeignClient.findListSkuInfo(keyword);
        //判断集合是否为空
        if (CollectionUtils.isEmpty(skuInfoList)){
            return skuInfoList;
        }
        //获取所有skuId
        List skuIdList = skuInfoList.stream().map(SkuInfo::getId).collect(Collectors.toList());
        //排除正在活动中的商品
        List existSkuIdList = baseMapper.selectExistSku(skuIdList);
        //存在不在活动中的商品
        ArrayList noExistskuInfoList = new ArrayList<>();
        //比较是否已经在活动中
        skuInfoList.stream().forEach(skuInfo->{
            if (!existSkuIdList.contains(skuInfo.getId())){
                noExistskuInfoList.add(skuInfo);
            }
        });
        return noExistskuInfoList;
    }
}

controller:

@ApiOperation("添加活动规则")
    @PostMapping("/saveActivityRule")
    public Result saveActivityRule(@RequestBody ActivityRuleVo activityRuleVo){
         activityInfoService.saveActivityRule(activityRuleVo);
         return Result.ok(null);
    }
    @ApiOperation("根据关键字查询SKU信息")
    @GetMapping("/findSkuInfoByKeyword/{keyword}")
    public Result findSkuInfoByKeyword(@PathVariable("keyword") String keyword){
        List list = activityInfoService.findSkuInfoByKeyword(keyword);
        return Result.ok(list);
    }

前端效果图:

Java --- 云尚优选项目_第53张图片

14.4、优惠卷功能 

远程接口业务代码:

 @ApiOperation("根据分类id查询")
    @PostMapping("/inner/findCategoryList")
    public List findCategoryList(@RequestBody List categoryIds){
        List categoryList = categoryService.listByIds(categoryIds);
        return categoryList;
    }

service实现类:

@Service
public class CouponInfoServiceImpl extends ServiceImpl implements CouponInfoService {
    @Autowired
    private CouponRangeMapper couponRangeMapper;
    @Autowired
    private ProduceFeignClient produceFeignClient;
    //分页查询优惠卷信息
    @Override
    public IPage allCouponInfoPage(Page couponInfoPage) {
        Page couponInfoIPage = baseMapper.selectPage(couponInfoPage, null);
        List couponInfos = couponInfoIPage.getRecords();
        couponInfos.stream().forEach(couponInfo -> {
            couponInfo.setCouponTypeString(couponInfo.getCouponType().getComment());
            if (!StringUtils.isEmpty(couponInfo.getRangeType())){
                couponInfo.setRangeTypeString(couponInfo.getRangeType().getComment());
            }
        });
        return couponInfoIPage;
    }
    //根据id查询
    @Override
    public CouponInfo getCouponInfoById(Long id) {
        CouponInfo couponInfo = baseMapper.selectById(id);
        if (!StringUtils.isEmpty(couponInfo)){
            couponInfo.setCouponTypeString(couponInfo.getCouponType().getComment());
            if (!StringUtils.isEmpty(couponInfo.getRangeType())){
                couponInfo.setRangeTypeString(couponInfo.getRangeType().getComment());
            }
        }
        return couponInfo;
    }
    //根据id查询优惠卷规则
    @Override
    public Map findCouponRuleList(Long id) {
        //查询优惠卷基本信息
        CouponInfo couponInfo = baseMapper.selectById(id);
        //根据优惠卷id查询couponRang
        LambdaQueryWrapper couponRangeWrapper = new LambdaQueryWrapper<>();
        couponRangeWrapper.eq(CouponRange::getCouponId,id);
        List couponRangeList = couponRangeMapper.selectList(couponRangeWrapper);
        //获取rang_id
        List rangIdList = couponRangeList.stream().map(CouponRange::getRangeId).collect(Collectors.toList());
        //根据优惠卷不同类型进行封装
        HashMap map = new HashMap<>();
        if (!CollectionUtils.isEmpty(rangIdList)){
            //商品分类
            if (couponInfo.getRangeType() == CouponRangeType.SKU){
                List skuInfoList = produceFeignClient.findSkuInfoList(rangIdList);
                map.put("skuInfoList",skuInfoList);
            }else if (couponInfo.getRangeType() == CouponRangeType.CATEGORY){
                //商品类型分类
                List categoryList =produceFeignClient.findCategoryList(rangIdList);
                map.put("categoryList",categoryList);
            }
        }
        return map;
    }
    //添加优惠卷规则信息
    @Override
    public void saveCouponRule(CouponRuleVo couponRuleVo) {
        //根据优惠卷id删除规则数据
        LambdaQueryWrapper couponRangeWrapper = new LambdaQueryWrapper<>();
        couponRangeWrapper.eq(CouponRange::getCouponId,couponRuleVo.getCouponId());
        couponRangeMapper.delete(couponRangeWrapper);
        //更新优惠卷基本信息
        CouponInfo couponInfo = baseMapper.selectById(couponRuleVo.getCouponId());
        couponInfo.setRangeType(couponRuleVo.getRangeType());
        couponInfo.setConditionAmount(couponRuleVo.getConditionAmount());
        couponInfo.setAmount(couponRuleVo.getAmount());
        couponInfo.setRangeDesc(couponRuleVo.getRangeDesc());
        baseMapper.insert(couponInfo);
        //添加优惠卷新规则数据
        List couponRangeList = couponRuleVo.getCouponRangeList();
        couponRangeList.stream().forEach(couponRange -> {
            //设置优惠卷id
            couponRange.setCouponId(couponRuleVo.getCouponId());
            couponRangeMapper.insert(couponRange);
        });
    }
}

controller层:

@ApiOperation("优惠卷分页查询")
    @GetMapping("/{page}/{limit}")
    public Result allCouponInfoPage(@PathVariable("page") Long page,
                                    @PathVariable("limit") Long limit){
        Page couponInfoPage = new Page<>(page,limit);
        IPage couponInfoIPage = couponInfoService.allCouponInfoPage(couponInfoPage);
        return Result.ok(couponInfoIPage);
    }
    @ApiOperation("添加优惠卷")
    @PostMapping("/save")
    public Result saveCouponInfo(@RequestBody CouponInfo couponInfo){
        couponInfoService.save(couponInfo);
        return Result.ok(null);
    }
    @ApiOperation("修改优惠卷")
    @PutMapping("/update")
    public Result update(@RequestBody CouponInfo couponInfo){
        couponInfoService.updateById(couponInfo);
        return Result.ok(null);
    }
    @ApiOperation("根据id删除")
    @DeleteMapping("/remove/{id}")
    public Result removeById(@PathVariable("id") Long id){
        couponInfoService.removeById(id);
        return Result.ok(null);
    }
    @ApiOperation("批量删除")
    @DeleteMapping("/batchRemove")
    public Result batchRemove(@RequestBody List idList){
        couponInfoService.removeByIds(idList);
        return Result.ok(null);
    }
    @ApiOperation("根据id查询")
    @GetMapping("/get/{id}")
    public Result getCouponInfoById(@PathVariable("id") Long id){
        CouponInfo couponInfo = couponInfoService.getCouponInfoById(id);
        return Result.ok(couponInfo);
    }
    @ApiOperation("根据id查询优惠卷规则")
    @GetMapping("findCouponRuleList/{id}")
    public Result findCouponRuleList(@PathVariable("id") Long id){
        Map map =couponInfoService.findCouponRuleList(id);
        return Result.ok(map);
    }
    @ApiOperation("添加优惠卷规则信息")
    @PostMapping("/saveCouponRule")
    public Result saveCouponRule(@RequestBody CouponRuleVo couponRuleVo){
        couponInfoService.saveCouponRule(couponRuleVo);
        return Result.ok(null);
    }

页面效果:

Java --- 云尚优选项目_第54张图片

优惠卷规则:

Java --- 云尚优选项目_第55张图片

 十五、整合Gateway网关

导入pom依赖

 
            org.springframework.cloud
            spring-cloud-starter-gateway
        
        
        
            com.alibaba.cloud
            spring-cloud-starter-alibaba-nacos-discovery
        

 修改yml配置文件

server:
  port: 8000

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: service-acl
          uri: lb://service-acl
          predicates:
            - Path=/*/acl/**

        - id: service-sys
          uri: lb://service-sys
          predicates:
            - Path=/*/sys/**

        - id: service-product
          uri: lb://service-product
          predicates:
            - Path=/*/product/**

        - id: service-activity
          uri: lb://service-activity
          predicates:
            - Path=/*/activity/**

        - id: service-order
          uri: lb://service-order
          predicates:
            - Path=/*/order/**

        - id: service-payment
          uri: lb://service-payment
          predicates:
            - Path=/*/payment/**

        - id: service-user
          uri: lb://service-user
          predicates:
            - Path=/*/user/**

        - id: service-search
          uri: lb://service-search
          predicates:
            - Path=/*/search/**

        - id: service-home
          uri: lb://service-home
          predicates:
            - Path=/*/home/**

        - id: service-cart
          uri: lb://service-cart
          predicates:
            - Path=/*/cart/**

配置跨域:

@Configuration
public class CorsConfig {

    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);
        return new CorsWebFilter(source);
    }
}

添加启动类添加注解:

Java --- 云尚优选项目_第56张图片

十六、微信小程序开发环境搭建

 16.1、注册微信程序开发测试号

 地址:微信公众平台

Java --- 云尚优选项目_第57张图片

16.2、安装微信开发者工具

地址:微信开发者工具下载地址与更新日志 | 微信开放文档 

Java --- 云尚优选项目_第58张图片

 16.3、安装hbuilderx

下载地址:HBuilderX-高效极客技巧 

Java --- 云尚优选项目_第59张图片

16.3.1、配置 hbuilderx

 第一步Java --- 云尚优选项目_第60张图片

 Java --- 云尚优选项目_第61张图片

16.4、开通内网穿透

地址:会员登陆 

十七、用户登录

1、调用 wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。

2、调用 auth.code2Session 接口,换取 用户唯一标识 OpenID 、 用户在微信开放平台帐号下的唯一标识UnionID(若当前小程序已绑定到微信开放平台帐号) 和 会话密钥 session_key。

3、之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。

17.1、整合JWT

17.1.1、jwt简介

JWT(Json Web Token)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。

JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。比如用在用户登录上

JWT最重要的作用就是对 token信息的防伪作用。

17.1.2、jwt原理

一个JWT由三个部分组成:公共部分、私有部分、签名部分。最后由这三者组合进行base64编码得到JWT。

 Java --- 云尚优选项目_第62张图片

 1、 公共部分

主要是该JWT的相关配置参数,比如签名的加密算法、格式类型、过期时间等等。

Key=ATGUIGU

2、 私有部分

用户自定义的内容,根据实际需要真正要封装的信息。

userInfo{用户的Id,用户的昵称nickName}

3、 签名部分

SaltiP: 当前服务器的Ip地址!{linux 中配置代理服务器的ip}

主要用户对JWT生成字符串的时候,进行加密{盐值}

最终组成 key+salt+userInfo è token!

17.1.3、springboot整合Jwt

导入pom依赖

 
            io.jsonwebtoken
            jjwt
        

 编写配置类

public class JwtHelper {
    private static long tokenExpiration = 365*24*60*60*1000;
    private static String tokenSignKey = "ssyx";

    public static String createToken(Long userId, String userName) {
        String token = Jwts.builder()
                .setSubject("ssyx-USER")//分组
                .setExpiration(new Date(System.currentTimeMillis() + tokenExpiration))//过期时间
                .claim("userId", userId)
                .claim("userName", userName)
                .signWith(SignatureAlgorithm.HS512, tokenSignKey)//密钥
                .compressWith(CompressionCodecs.GZIP)//压缩方式
                .compact();
        return token;
    }
    //解密
    public static Long getUserId(String token) {
        if(StringUtils.isEmpty(token)) return null;

        Jws claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
        Claims claims = claimsJws.getBody();
        Integer userId = (Integer)claims.get("userId");
        return userId.longValue();
        // return 1L;
    }

    public static String getUserName(String token) {
        if(StringUtils.isEmpty(token)) return "";

        Jws claimsJws = Jwts.parser().setSigningKey(tokenSignKey).parseClaimsJws(token);
        Claims claims = claimsJws.getBody();
        return (String)claims.get("userName");
    }

    public static void removeToken(String token) {
        //jwttoken无需删除,客户端扔掉即可。
    }

    public static void main(String[] args) {
        String token = JwtHelper.createToken(1L, "admin");
        System.out.println(token);
        System.out.println(JwtHelper.getUserId(token));
        System.out.println(JwtHelper.getUserName(token));
    }
}

17.2、整合redis

@Configuration
@EnableCaching
public class RedisConfig {

    // 使用默认标签做缓存
    @Bean
    public KeyGenerator wiselyKeyGenerator() {
        return new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append(method.getName());
                for (Object obj : params) {
                    sb.append(obj.toString());
                }
                return sb.toString();
            }
        };
    }

    // 声明模板
    /*
    ref = 表示引用
    value = 具体的值
    
        
    
     */
    //  工具类:
    @Bean
    public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
        //  将Redis 中 string ,hash 数据类型,自动序列化!
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
        RedisSerializer redisSerializer = new StringRedisSerializer();
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

        //解决查询缓存转换异常的问题
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        // 配置序列化(解决乱码的问题),过期时间600秒
        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofDays(365))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();

        RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
        return cacheManager;
    }
}

修改yml配置文件

redis:
    host: localhost
    port: 6379
    database: 0
    timeout: 1800000
    password:
    lettuce:
      pool:
        max-active: 20 #最大连接数
        max-wait: -1    #最大阻塞等待时间(负数表示没限制)
        max-idle: 5    #最大空闲
        min-idle: 0     #最小空闲

17.3、springboot整合微信小程序配置

修改yml配置文件

wx:
  open:
    # 小程序微信公众平台appId
    app_id: wx413e45d362013456
    # 小程序微信公众平台api秘钥
    app_secret: cc618fe7ce22a68bcb7777777

 编写工具类获取配置文件内容

@Component
public class ConstantPropertiesUtil implements InitializingBean {
    @Value("${wx.open.app_id}")
    private String appId;

    @Value("${wx.open.app_secret}")
    private String appSecret;

    public static String WX_OPEN_APP_ID;
    public static String WX_OPEN_APP_SECRET;

    @Override
    public void afterPropertiesSet() throws Exception {
        WX_OPEN_APP_ID = appId;
        WX_OPEN_APP_SECRET = appSecret;
    }
}

17.4、添加相关工具类

public class HttpClientUtils {

    public static final int connTimeout=10000;
    public static final int readTimeout=10000;
    public static final String charset="UTF-8";
    private static HttpClient client = null;

    static {
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
        cm.setMaxTotal(128);
        cm.setDefaultMaxPerRoute(128);
        client = HttpClients.custom().setConnectionManager(cm).build();
    }

    public static String postParameters(String url, String parameterStr) throws ConnectTimeoutException, SocketTimeoutException, Exception{
        return post(url,parameterStr,"application/x-www-form-urlencoded",charset,connTimeout,readTimeout);
    }

    public static String postParameters(String url, String parameterStr,String charset, Integer connTimeout, Integer readTimeout) throws ConnectTimeoutException, SocketTimeoutException, Exception{
        return post(url,parameterStr,"application/x-www-form-urlencoded",charset,connTimeout,readTimeout);
    }

    public static String postParameters(String url, Map params) throws ConnectTimeoutException,
            SocketTimeoutException, Exception {
        return postForm(url, params, null, connTimeout, readTimeout);
    }

    public static String postParameters(String url, Map params, Integer connTimeout,Integer readTimeout) throws ConnectTimeoutException,
            SocketTimeoutException, Exception {
        return postForm(url, params, null, connTimeout, readTimeout);
    }

    public static String get(String url) throws Exception {
        return get(url, charset, null, null);
    }

    public static String get(String url, String charset) throws Exception {
        return get(url, charset, connTimeout, readTimeout);
    }

    /**
     * 发送一个 Post 请求, 使用指定的字符集编码.
     *
     * @param url
     * @param body RequestBody
     * @param mimeType 例如 application/xml "application/x-www-form-urlencoded" a=1&b=2&c=3
     * @param charset 编码
     * @param connTimeout 建立链接超时时间,毫秒.
     * @param readTimeout 响应超时时间,毫秒.
     * @return ResponseBody, 使用指定的字符集编码.
     * @throws ConnectTimeoutException 建立链接超时异常
     * @throws SocketTimeoutException  响应超时
     * @throws Exception
     */
    public static String post(String url, String body, String mimeType,String charset, Integer connTimeout, Integer readTimeout)
            throws ConnectTimeoutException, SocketTimeoutException, Exception {
        HttpClient client = null;
        HttpPost post = new HttpPost(url);
        String result = "";
        try {
            if (StringUtils.isNotBlank(body)) {
                HttpEntity entity = new StringEntity(body, ContentType.create(mimeType, charset));
                post.setEntity(entity);
            }
            // 设置参数
            Builder customReqConf = RequestConfig.custom();
            if (connTimeout != null) {
                customReqConf.setConnectTimeout(connTimeout);
            }
            if (readTimeout != null) {
                customReqConf.setSocketTimeout(readTimeout);
            }
            post.setConfig(customReqConf.build());

            HttpResponse res;
            if (url.startsWith("https")) {
                // 执行 Https 请求.
                client = createSSLInsecureClient();
                res = client.execute(post);
            } else {
                // 执行 Http 请求.
                client = HttpClientUtils.client;
                res = client.execute(post);
            }
            result = IOUtils.toString(res.getEntity().getContent(), charset);
        } finally {
            post.releaseConnection();
            if (url.startsWith("https") && client != null&& client instanceof CloseableHttpClient) {
                ((CloseableHttpClient) client).close();
            }
        }
        return result;
    }

    /**
     * 提交form表单
     *
     * @param url
     * @param params
     * @param connTimeout
     * @param readTimeout
     * @return
     * @throws ConnectTimeoutException
     * @throws SocketTimeoutException
     * @throws Exception
     */
    public static String postForm(String url, Map params, Map headers, Integer connTimeout,Integer readTimeout) throws ConnectTimeoutException,
            SocketTimeoutException, Exception {

        HttpClient client = null;
        HttpPost post = new HttpPost(url);
        try {
            if (params != null && !params.isEmpty()) {
                List formParams = new ArrayList();
                Set> entrySet = params.entrySet();
                for (Entry entry : entrySet) {
                    formParams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
                }
                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(formParams, Consts.UTF_8);
                post.setEntity(entity);
            }

            if (headers != null && !headers.isEmpty()) {
                for (Entry entry : headers.entrySet()) {
                    post.addHeader(entry.getKey(), entry.getValue());
                }
            }
            // 设置参数
            Builder customReqConf = RequestConfig.custom();
            if (connTimeout != null) {
                customReqConf.setConnectTimeout(connTimeout);
            }
            if (readTimeout != null) {
                customReqConf.setSocketTimeout(readTimeout);
            }
            post.setConfig(customReqConf.build());
            HttpResponse res = null;
            if (url.startsWith("https")) {
                // 执行 Https 请求.
                client = createSSLInsecureClient();
                res = client.execute(post);
            } else {
                // 执行 Http 请求.
                client = HttpClientUtils.client;
                res = client.execute(post);
            }
            return IOUtils.toString(res.getEntity().getContent(), "UTF-8");
        } finally {
            post.releaseConnection();
            if (url.startsWith("https") && client != null
                    && client instanceof CloseableHttpClient) {
                ((CloseableHttpClient) client).close();
            }
        }
    }

    /**
     * 发送一个 GET 请求
     *
     * @param url
     * @param charset
     * @param connTimeout  建立链接超时时间,毫秒.
     * @param readTimeout  响应超时时间,毫秒.
     * @return
     * @throws ConnectTimeoutException   建立链接超时
     * @throws SocketTimeoutException   响应超时
     * @throws Exception
     */
    public static String get(String url, String charset, Integer connTimeout,Integer readTimeout)
            throws ConnectTimeoutException,SocketTimeoutException, Exception {

        HttpClient client = null;
        HttpGet get = new HttpGet(url);
        String result = "";
        try {
            // 设置参数
            Builder customReqConf = RequestConfig.custom();
            if (connTimeout != null) {
                customReqConf.setConnectTimeout(connTimeout);
            }
            if (readTimeout != null) {
                customReqConf.setSocketTimeout(readTimeout);
            }
            get.setConfig(customReqConf.build());

            HttpResponse res = null;

            if (url.startsWith("https")) {
                // 执行 Https 请求.
                client = createSSLInsecureClient();
                res = client.execute(get);
            } else {
                // 执行 Http 请求.
                client = HttpClientUtils.client;
                res = client.execute(get);
            }
            result = IOUtils.toString(res.getEntity().getContent(), charset);
        } finally {
            get.releaseConnection();
            if (url.startsWith("https") && client != null && client instanceof CloseableHttpClient) {
                ((CloseableHttpClient) client).close();
            }
        }
        return result;
    }
    /**
     * 从 response 里获取 charset
     *
     * @param ressponse
     * @return
     */
    @SuppressWarnings("unused")
    private static String getCharsetFromResponse(HttpResponse ressponse) {
        // Content-Type:text/html; charset=GBK
        if (ressponse.getEntity() != null  && ressponse.getEntity().getContentType() != null && ressponse.getEntity().getContentType().getValue() != null) {
            String contentType = ressponse.getEntity().getContentType().getValue();
            if (contentType.contains("charset=")) {
                return contentType.substring(contentType.indexOf("charset=") + 8);
            }
        }
        return null;
    }
    /**
     * 创建 SSL连接
     * @return
     * @throws GeneralSecurityException
     */
    private static CloseableHttpClient createSSLInsecureClient() throws GeneralSecurityException {
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                public boolean isTrusted(X509Certificate[] chain,String authType) throws CertificateException {
                    return true;
                }
            }).build();
            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new X509HostnameVerifier() {

                @Override
                public boolean verify(String arg0, SSLSession arg1) {
                    return true;
                }
                @Override
                public void verify(String host, SSLSocket ssl)
                        throws IOException {
                }
                @Override
                public void verify(String host, X509Certificate cert)
                        throws SSLException {
                }
                @Override
                public void verify(String host, String[] cns,
                                   String[] subjectAlts) throws SSLException {
                }
            });
            return HttpClients.custom().setSSLSocketFactory(sslsf).build();
        } catch (GeneralSecurityException e) {
            throw e;
        }
    }
}

17.5、用户登录功能

service实现类

//根据openid查询用户
    @Override
    public User getUserByOpenid(String openid) {
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(User::getOpenId,openid);
        return baseMapper.selectOne(wrapper);
    }
    //根据userid查询提货点和团长信息
    @Override
    public LeaderAddressVo getLeaderAddressByUserId(Long userId) {
        //查询用户提货记录表
        LambdaQueryWrapper userDeliveryWrapper = new LambdaQueryWrapper<>();
        //默认提货点
        userDeliveryWrapper.eq(UserDelivery::getUserId,userId).eq(UserDelivery::getIsDefault,1);
        UserDelivery userDelivery = userDeliveryService.getOne(userDeliveryWrapper);
        if (StringUtils.isEmpty(userDelivery)){
             return null;
        }
        //查询团长表
        LambdaQueryWrapper leaderWrapper = new LambdaQueryWrapper<>();
        leaderWrapper.eq(Leader::getId,userDelivery.getLeaderId());
        Leader leader = leaderService.getOne(leaderWrapper);
        //封装数据
        LeaderAddressVo leaderAddressVo = new LeaderAddressVo();
        BeanUtils.copyProperties(leader, leaderAddressVo);
        leaderAddressVo.setUserId(userId);
        leaderAddressVo.setLeaderId(leader.getId());
        leaderAddressVo.setLeaderName(leader.getName());
        leaderAddressVo.setLeaderPhone(leader.getPhone());
        leaderAddressVo.setWareId(userDelivery.getWareId());
        leaderAddressVo.setStorePath(leader.getStorePath());
        return leaderAddressVo;
    }
    //封装用户登录信息
    @Override
    public UserLoginVo getUserLoginVo(Long userId) {
        //查询用户信息
        User user = baseMapper.selectById(userId);
        UserLoginVo userLoginVo = new UserLoginVo();
        userLoginVo.setNickName(user.getNickName());
        userLoginVo.setUserId(userId);
        userLoginVo.setPhotoUrl(user.getPhotoUrl());
        userLoginVo.setOpenId(user.getOpenId());
        userLoginVo.setIsNew(user.getIsNew());
        //查询用户提货记录表
        LambdaQueryWrapper userDeliveryWrapper = new LambdaQueryWrapper<>();
        //默认提货点
        userDeliveryWrapper.eq(UserDelivery::getUserId,userId).eq(UserDelivery::getIsDefault,1);
        UserDelivery userDelivery = userDeliveryService.getOne(userDeliveryWrapper);
        if (!StringUtils.isEmpty(userDelivery)){
           userLoginVo.setLeaderId(userDelivery.getLeaderId());
           userLoginVo.setWareId(userDelivery.getWareId());
        }else {
            userLoginVo.setLeaderId(1L);
            userLoginVo.setWareId(1L);
        }
        return userLoginVo;
    }
@ApiOperation("用户微信授权登录(获取openid)")
    @GetMapping("/wxLogin/{code}")
    public Result wxLogin(@PathVariable("code") String code){
        //获取小程序id
        String wxOpenAppId = ConstantPropertiesUtil.WX_OPEN_APP_ID;
        //获取小程序密钥
        String wxOpenAppSecret = ConstantPropertiesUtil.WX_OPEN_APP_SECRET;
        //拼接请求地址+参数
        StringBuilder url = new StringBuilder();
        url.append("https://api.weixin.qq.com/sns/jscode2session")
                .append("?appid=%s")
                .append("&secret=%s")
                .append("&js_code=%s")
                .append("&grant_type=authorization_code");
        //授权临时票据
        String tokenUrl = String.format(url.toString(), wxOpenAppId, wxOpenAppSecret, code);
        //使用HttpClient工具类发送get请求
        String result = null;
        try {
             result = HttpClientUtils.get(tokenUrl);
        } catch (Exception e) {
            throw new CustomException(ResultCodeEnum.FETCH_ACCESSTOKEN_FAILD);
        }
        //获取到返回的session_key和openid
        JSONObject jsonObject = JSONObject.parseObject(result);
        String session_key = jsonObject.getString("session_key");
        String openid = jsonObject.getString("openid");
        //查询用户表是否有该用户
        User user = userService.getUserByOpenid(openid);
        if (StringUtils.isEmpty(user)){
            user = new User();
            user.setOpenId(openid);
            user.setNickName(openid);
            user.setPhotoUrl("");
            user.setUserType(UserType.USER);
            user.setIsNew(0);
            userService.save(user);
        }
        //根据userid查询提货点和团长信息
        LeaderAddressVo leaderAddressVo = userService.getLeaderAddressByUserId(user.getId());
        //使用jwt根据用户id和姓名生成token
        String token = JwtHelper.createToken(user.getId(), user.getNickName());
        //将当前登录用户信息存入redis数据库
        UserLoginVo userLoginVo = userService.getUserLoginVo(user.getId());
        redisTemplate.opsForValue().set(RedisConst.USER_LOGIN_KEY_PREFIX+user.getId(),userLoginVo,RedisConst.USERKEY_TIMEOUT, TimeUnit.DAYS);
        //封装数据到map集合
        HashMap map = new HashMap<>();
        map.put("user",user);
        map.put("token",token);
        map.put("leaderAddressVo",leaderAddressVo);
        return Result.ok(map);
    }

测试结果:

十八、首页数据显示

1、微信授权登录,调用后端接口wxLogin,接口生成token,登录用户信息存入Redis,返回token

2、前端接收到微信登录接口返回的token

3、每次发送请求时候,把token放到请求头里面

4、后端从请求头获取token,从token中获取userId,根据userId获取登录的用户信息

18.1、拦截器 

 在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作,拦截是AOP的一种实现策略

18.1.1、实现步骤 

第一步,实现HandlerInterceptor接口或者它的实现子类:HandlerInterceptorAdapter

第二步,实现接口中的方法:

​    preHandle:在业务处理器处理请求之前被调用
​    postHandle:在业务处理器处理请求执行完成后
​    afterCompletion:在完全处理完请求后被调用,可用于清理资源等

第三步,**创建配置类,配置拦截器需要拦截的路径

public class AuthContextHolder {
    //用户id
    private static ThreadLocal userId = new ThreadLocal();
    //用户仓库id
    private static ThreadLocal wareId = new ThreadLocal();
    //用户信息对象
    private static ThreadLocal  userLoginVo= new ThreadLocal();

    public static void setUserId(Long id){
        userId.set(id);
    }
    public static Long getUserId(){
        return userId.get();
    }
    public static void setWareId(Long id){
        wareId.set(id);
    }
    public static Long getWareId(){
        return wareId.get();
    }
    public static void setUserLoginVo(UserLoginVo userLoginVos){
        userLoginVo.set(userLoginVos);
    }
    public static UserLoginVo getUserLoginVo(){
        return userLoginVo.get();
    }
}
public class UserLoginInterceptor implements HandlerInterceptor {

    private RedisTemplate redisTemplate;
    public UserLoginInterceptor(RedisTemplate redisTemplate){
        this.redisTemplate = redisTemplate;
    }
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        this.getUserLogin(request);
        return true;
    }

    private void getUserLogin(HttpServletRequest request) {
        //从请求头里获取token
        String token = request.getHeader("token");
        if (!StringUtils.isEmpty(token)){
            //从token里获取用户id
            Long userId = JwtHelper.getUserId(token);
            //使用用户id查询redis库
            UserLoginVo userLoginVo = (UserLoginVo) redisTemplate.opsForValue().get(RedisConst.USER_LOGIN_KEY_PREFIX + userId);
            if(!StringUtils.isEmpty(userLoginVo)){
                AuthContextHolder.setUserId(userId);
                AuthContextHolder.setWareId(userLoginVo.getWareId());
                AuthContextHolder.setUserLoginVo(userLoginVo);
            }
        }
    }
}
@Configuration
public class LoginMvcConfigurerAdapter extends WebMvcConfigurationSupport {
    @Autowired
    private RedisTemplate redisTemplate;
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new UserLoginInterceptor(redisTemplate))
                .addPathPatterns("/api/**")//需要拦截的路径
                .excludePathPatterns("/api/user/weixin/wxLogin/*");//不需要拦截的路径
        super.addInterceptors(registry);
    }
}
@PostMapping("/auth/updateUser")
    @ApiOperation(value = "更新用户昵称与头像")
    public Result updateUser(@RequestBody User user) {
        User user1 = userService.getById(AuthContextHolder.getUserId());
        //把昵称更新为微信用户
        user1.setNickName(user.getNickName().replaceAll("[ue000-uefff]", "*"));
        user1.setPhotoUrl(user.getPhotoUrl());
        userService.updateById(user1);
        return Result.ok(null);
    }

18.2、首页数据显示 

 远程接口

 @Autowired
    private UserService userService;
    @ApiOperation("查询提货点和团长信息")
    @GetMapping("/inner/getLeaderAddressVo/{userId}")
    public LeaderAddressVo getLeaderAddressVo(@PathVariable("userId") Long userId){
        LeaderAddressVo leaderAddressByUserId = userService.getLeaderAddressByUserId(userId);
        return leaderAddressByUserId;
    }
//查询新人专享商品
    @Override
    public List findNewPersonSkuInfoList() {
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        //上架并是新人专享
        wrapper.eq(SkuInfo::getIsNewPerson,1).eq(SkuInfo::getPublishStatus,1);
        wrapper.orderByDesc(SkuInfo::getSkuCode);
        //新人专享首页显示3条
        Page page = new Page<>(1,3);
        Page skuInfoPage = baseMapper.selectPage(page, wrapper);
        List records = skuInfoPage.getRecords();
        return records;
    }
//查询爆款商品
    @Override
    public List findHotSkuList() {
        Pageable pageable = PageRequest.of(0, 10);
        //关联条件
        Page skuEsPage = skuRepository.findByOrderByHotScoreDesc(pageable);
        //获取数据
        List content = skuEsPage.getContent();
        return content;
    }

service实现类

 @Autowired
    private UserFeignClient userFeignClient;
    @Autowired
    private ProduceFeignClient produceFeignClient;
    @Autowired
    private SkuFeignClient skuFeignClient;
    //首页数据显示
    @Override
    public Map homeDate(Long userId) {
        HashMap map = new HashMap<>();
        //根据用户id查询当前用户的提货地址,远程调用查询
        LeaderAddressVo leaderAddressVo = userFeignClient.getLeaderAddressVo(userId);
        map.put("leaderAddressVo",leaderAddressVo);
        //远程调用查询所有商品分类
        List allCategory = produceFeignClient.findAllCategory();
        map.put("categoryList",allCategory);
        //远程调用查询新人专享商品
        List newPersonSkuInfoList = produceFeignClient.findNewPersonSkuInfoList();
        map.put("newPersonSkuInfoList",newPersonSkuInfoList);
        //远程调用查询爆款商品,根据热门评分降序排序
        List hotSkuList = skuFeignClient.findHotSkuList();
        map.put("hotSkuList",hotSkuList);
        return map;
    }

controller

@ApiOperation("首页数据显示")
    @GetMapping("/index")
    public Result index(HttpServletRequest request){
        Long userId = AuthContextHolder.getUserId();
        Map map = homeService.homeDate(userId);
        return Result.ok(map);
    }

页面效果 

 Java --- 云尚优选项目_第63张图片

 十九、商品分类与搜索

 @ApiOperation("远程调用查询商品分类")
    @GetMapping("/category")
    public Result categoryList(){
        List allCategory = produceFeignClient.findAllCategory();
        return Result.ok(allCategory);
    }
//查询分类的商品信息
    @Override
    public Page search(Pageable pageable, SkuEsQueryVo skuEsQueryVo) {
        Page skuEsPage = null;
        //设置当前用户登录的id
        skuEsQueryVo.setWareId(AuthContextHolder.getWareId());
        String keyword = skuEsQueryVo.getKeyword();
        //使用springData根据条件进行查询
        if (StringUtils.isEmpty(keyword)){
            skuEsPage = skuRepository.findByCategoryIdAndWareId(skuEsQueryVo.getCategoryId(),
                    skuEsQueryVo.getWareId(),pageable);

        }else{
            //搜索关键字不为空
            skuEsPage =  skuRepository.findByKeywordAndWareId(keyword,skuEsQueryVo.getWareId(),pageable);
        }
        //查询商品是否为优惠活动商品
        List skuEsList = skuEsPage.getContent();
        if (!CollectionUtils.isEmpty(skuEsList)){
            List skuIdList = skuEsList.stream().map(SkuEs::getId).collect(Collectors.toList());
            //map存储一个商品可以参加一个活动与对应的多个规则
            //远程调用查询
            Map> map = activityFeignClient.findActivityInfo(skuIdList);
            //封装数据到ruleList中
            if (!CollectionUtils.isEmpty(map)){
                skuEsList.forEach(skuEs -> {
                    skuEs.setRuleList(map.get(skuEs.getId()));
                });
            }
        }
        return skuEsPage;
    }
//根据skuId查询促销信息
    @Override
    public Map> findActivityInfo(List idList) {
        Map> map = new HashMap<>();
        idList.forEach(skuId->{
            //根据skuId查询对应的活动规则表
            List activityRules = baseMapper.findActivityRule(skuId);
            if (!CollectionUtils.isEmpty(activityRules)){
                List ruleList = new ArrayList<>();
                //封装规则名称
                activityRules.stream().forEach(activityRule -> {
                    ruleList.add(this.getRuleDesc(activityRule));
                });
                map.put(skuId,ruleList);
            }
        });
        return map;
    }
    //构造规则名称的方法
    private String getRuleDesc(ActivityRule activityRule) {
        ActivityType activityType = activityRule.getActivityType();
        StringBuffer ruleDesc = new StringBuffer();
        if (activityType == ActivityType.FULL_REDUCTION) {
            ruleDesc
                    .append("满")
                    .append(activityRule.getConditionAmount())
                    .append("元减")
                    .append(activityRule.getBenefitAmount())
                    .append("元");
        } else {
            ruleDesc
                    .append("满")
                    .append(activityRule.getConditionNum())
                    .append("元打")
                    .append(activityRule.getBenefitDiscount())
                    .append("折");
        }
        return ruleDesc.toString();
    }

 @ApiOperation("根据skuId查询促销信息")
    @PostMapping("/inner/findActivityInfo")
    public Map> findActivityInfo(@RequestBody List idList){
        return  activityInfoService.findActivityInfo(idList);
    }
@ApiOperation("查询分类商品")
    @PostMapping("/{page}/{limit}")
    public Result skuList(@PathVariable("page") Integer page,
                          @PathVariable("limit") Integer limit,
                          SkuEsQueryVo skuEsQueryVo){
        //page-1,0表示第一页
        Pageable pageable = PageRequest.of(page-1, limit);
        Page skuEsPage = skuService.search(pageable,skuEsQueryVo);
        return Result.ok(skuEsPage);
    }

页面效果:

Java --- 云尚优选项目_第64张图片

二十、商品详情 

核心:1、查询商品的基本信息(远程调用), 2、查询相关优惠活动信息(远程调用),3、更新商品热度(远程调用)

优化:使用多线程优化远程调用 (技术点)使用CompletableFuture类

创建线程方式:1、继承Thread类,2、实现Runnable接口,3、实现Callable接口,4、使用线程池。

20.1、CompletableFuture的使用测试

20.1.1、创建异步对象

没有返回值

public class FutureTest {
    public static void main(String[] args) {
        //创建线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(4);
        System.out.println("主线程开始执行");
        //runAsync()没有返回值,异步执行
        CompletableFuture completableFuture = CompletableFuture.runAsync(() -> {
            System.out.println("当前执行的线程:"+Thread.currentThread().getName());
            int count = 100;
            System.out.println(count);
        }, threadPool);
        System.out.println("主线程结束执行");
    }
}

 有返回值

public class FutureTest2 {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //创建线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(4);
        System.out.println("主线程开始执行");
        CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前执行的线程:"+Thread.currentThread().getName());
            int count = 100;
            System.out.println(count);
            return count;
        }, threadPool);
        Integer integer = completableFuture.get();
        System.out.println(integer);
        System.out.println("主线程结束执行");
    }
}

 20.1.2、计数完成回调

public class FutureTest3 {
    public static void main(String[] args) {
        //创建线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(4);
        System.out.println("主线程开始执行");
        //whenComplete()计数完成回调
        CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前执行的线程:"+Thread.currentThread().getName());
            int count = 100;
            System.out.println(count);
            return count;
        }, threadPool).whenComplete((rs,ex)->{
            System.out.println("whenComplete:"+rs);
            System.out.println("exception:"+ex);
        });
        System.out.println("主线程结束执行");
    }
}

 20.1.3、串行化

public class FutureTest4 {
    public static void main(String[] args) {
        //创建线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(4);
        System.out.println("主线程开始执行");
        //任务1
        CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前执行的线程:"+Thread.currentThread().getName());
            int count = 100;
            System.out.println("任务1:"+count);
            return count;
        }, threadPool);
        //任务2,获取任务1结果并返回
        completableFuture.thenApplyAsync((res)->{
            System.out.println("任务2:"+res);
            return res;
        },threadPool);
        //任务3:继续执行
        completableFuture.thenRunAsync(()->{
            System.out.println("任务3");
        },threadPool);
    }
}

20.1.4、多任务组合

public class FutureTest5 {
    public static void main(String[] args) {
        //创建线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(4);
        System.out.println("主线程开始执行");
        //任务1
        CompletableFuture completableFuture1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前执行的线程:"+Thread.currentThread().getName());
            int count = 100;
            System.out.println("任务1:"+count);
            return count;
        }, threadPool);
        //任务2
        CompletableFuture completableFuture2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("当前执行的线程:"+Thread.currentThread().getName());
            int count = 200;
            System.out.println("任务2:"+count);
            return count;
        }, threadPool);
         CompletableFuture.allOf(completableFuture1, completableFuture2).join();
        System.out.println("主线程结束执行");
    }
}

20.2、商品详情功能实现

 自定义线程池

@Configuration
public class ThreadPoolConfig {
    //自定义线程池
    @Bean
    public ThreadPoolExecutor threadPoolExecutor(){
        return new ThreadPoolExecutor(2,5,2,
                TimeUnit.SECONDS,new ArrayBlockingQueue<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());
    }
}

查询商品sku信息 

@ApiOperation("查询商品sku信息")
    @GetMapping("/inner/getSkuInfoVo/{skuId}")
    public SkuInfoVo getSkuInfoVo(@PathVariable("skuId") Long skuId){
        return skuInfoService.selectSkuById(skuId);
    }

查询商品相关优惠卷信息

 //根据skuId查询营销活动和优惠卷信息
    @Override
    public Map findActivityAndCoupon(Long skuId, Long userId) {
        //查询sku的营销活动
        Map activityRuleList = this.findActivityRule(skuId);
        //查询优惠卷信息
        List couponInfoList = couponInfoService.findCouponInfoList(skuId,userId);
        //封装数据
        Map map = new HashMap<>();
        map.putAll(activityRuleList);
        map.put("couponInfoList",couponInfoList);
        return map;
    }

    @Override
    public Map findActivityRule(Long skuId) {
        Map map = new HashMap<>();
        List activityRules = baseMapper.findActivityRule(skuId);
        activityRules.stream().forEach(activityRule -> {
            String ruleDesc = this.getRuleDesc(activityRule);
            activityRule.setRuleDesc(ruleDesc);
        });
        map.put("activityRuleList",activityRules);
        return map;
    }

更新商品热度 

//更新商品热度
    @Override
    public Boolean updateHotScore(Long skuId) {
        String key = "hotScore";
        //在redis中存储数据一次就加一
        Double hotScore = redisTemplate.opsForZSet().incrementScore(key, "skuId" + skuId, 1);
        //redis中修改十次,es更新一次
        if (hotScore % 10 == 0){
            Optional optional = skuRepository.findById(skuId);
            SkuEs skuEs = optional.get();
            skuEs.setHotScore(Math.round(hotScore));
            skuRepository.save(skuEs);
        }
        return true;
    }
//商品详情查询
    @Override
    public Map index(Long skuId, Long userId) {
        HashMap map = new HashMap<>();
        ThreadPoolExecutor threadPoolExecutor = threadPoolConfig.threadPoolExecutor();
        CompletableFuture skuInfoVoFuture = CompletableFuture.runAsync(() -> {
            //远程调用查询商品信息
            SkuInfoVo skuInfoVo = produceFeignClient.getSkuInfoVo(skuId);
            map.put("skuInfoVo",skuInfoVo);
        },threadPoolExecutor);
        CompletableFuture activityAndCouponFuture = CompletableFuture.runAsync(() -> {
            //远程调用查询商品能使用的优惠卷信息
            Map activityAndCoupon = activityFeignClient.findActivityAndCoupon(skuId, userId);
            map.putAll(activityAndCoupon);
        }, threadPoolExecutor);
        CompletableFuture HotScoreFuture = CompletableFuture.runAsync(() -> {
            //远程调用更新商品热度
            skuFeignClient.updateHotScore(skuId);
        }, threadPoolExecutor);
        CompletableFuture.allOf(skuInfoVoFuture,activityAndCouponFuture,HotScoreFuture).join();
        return map;
    }
 @ApiOperation("商品详情信息")
    @GetMapping("/item/{id}")
    public Result index(@PathVariable("id") Long id){
        Long userId = AuthContextHolder.getUserId();
        Map map = itemService.index(id,userId);
        return Result.ok(map);
    }

二十一、购物车功能

21.1、添加购物车功能

    //添加商品到购物车
    @Override
    public void addToCart(Long userId, Long skuId, Integer skuNum) {
        //根据key查询redis中是否有数据
        String cartKey = this.getCartKey(userId);
        BoundHashOperations hashOps = redisTemplate.boundHashOps(cartKey);
        //判断key对应的里面是否有skuid,有就更新
        CartInfo cartInfo = null;
        if (hashOps.hasKey(skuId.toString())){
            //根据skuid获取对应的数量
             cartInfo = hashOps.get(skuId.toString());
            //更新购物车的商品数量
            int newSkuNum = cartInfo.getSkuNum() + skuNum;
            if (newSkuNum < 1){
                return;
            }
            //更新CartInfo
            cartInfo.setSkuNum(newSkuNum);
            cartInfo.setCurrentBuyNum(newSkuNum);
            //选中加入购物车的数量不能大于限购数
            if (newSkuNum > cartInfo.getPerLimit()){
                throw new CustomException(ResultCodeEnum.SKU_LIMIT_ERROR);
            }
            //更新是否选中,默认选中
            cartInfo.setIsChecked(1);
            cartInfo.setUpdateTime(new Date());
        }else {
            //第一次添加商品到购物车
            skuNum = 1;

            //远程调用查询sku的基本信息
            SkuInfo skuInfo = produceFeignClient.getSkuInfo(skuId);
            if (StringUtils.isEmpty(skuInfo)){
                throw new CustomException(ResultCodeEnum.DATA_ERROR);
            }
            //封装数据
            cartInfo = new CartInfo();
            cartInfo.setSkuId(skuId);
            cartInfo.setCategoryId(skuInfo.getCategoryId());
            cartInfo.setSkuType(skuInfo.getSkuType());
            cartInfo.setIsNewPerson(skuInfo.getIsNewPerson());
            cartInfo.setUserId(userId);
            cartInfo.setCartPrice(skuInfo.getPrice());
            cartInfo.setSkuNum(skuNum);
            cartInfo.setCurrentBuyNum(skuNum);
            cartInfo.setSkuType(SkuType.COMMON.getCode());
            cartInfo.setPerLimit(skuInfo.getPerLimit());
            cartInfo.setImgUrl(skuInfo.getImgUrl());
            cartInfo.setSkuName(skuInfo.getSkuName());
            cartInfo.setWareId(skuInfo.getWareId());
            cartInfo.setIsChecked(1);
            cartInfo.setStatus(1);
            cartInfo.setCreateTime(new Date());
            cartInfo.setUpdateTime(new Date());
        }
        //更新redis的缓存数据
        hashOps.put(skuId.toString(),cartInfo);
        //设置过期时间
        setCartKey(cartKey);
    }
    //获取购物车的key
    private String getCartKey(Long userId){
        //user:userId :cart
        return RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
    }
    //设置key过期时间规则
    private void setCartKey(String key){
        redisTemplate.expire(key, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
    }
 //获取购物车的key
    private String getCartKey(Long userId){
        //user:userId :cart
        return RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
    }
    //设置key过期时间规则
    private void setCartKey(String key){
        redisTemplate.expire(key, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
    }

22.2、删除购物车列表

  //根据skuId删除购物车商品
    @Override
    public void deleteCart(Long userId, Long skuId) {
        BoundHashOperations operations = redisTemplate.boundHashOps(getCartKey(userId));
        if (operations.hasKey(skuId.toString())){
            operations.delete(skuId.toString());
        }
    }
    //清空购物车
    @Override
    public void deleteAllCart(Long userId) {
        BoundHashOperations operations = redisTemplate.boundHashOps(getCartKey(userId));
        List cartInfos = operations.values();
        cartInfos.stream().forEach(cartInfo -> {
            operations.delete(cartInfo.getSkuId().toString());
        });
    }
    //skuId批量删除购物车商品
    @Override
    public void batchDeleteCart(Long userId, List skuIds) {
        BoundHashOperations operations = redisTemplate.boundHashOps(getCartKey(userId));
        skuIds.forEach(skuId->{
            if (operations.hasKey(skuId.toString())){
                operations.delete(skuId.toString());
            }
        });

    }

22.3、查询带优惠卷的购物车列表

//查询购物车列表
    @Override
    public List cartList(Long userId) {
        List cartInfoList = null;
        if (StringUtils.isEmpty(userId)){
            return cartInfoList;
        }
        BoundHashOperations operations = redisTemplate.boundHashOps(getCartKey(userId));
         cartInfoList = operations.values();
         if (!CollectionUtils.isEmpty(cartInfoList)){
             //根据商品添加时间,降序
             cartInfoList.sort(new Comparator() {
                 @Override
                 public int compare(CartInfo o1, CartInfo o2) {
                     return o1.getCreateTime().compareTo(o2.getCreateTime());
                 }
             });
         }
        return cartInfoList;
    }
    //查询带优惠卷的购物车列表
    @Override
    public OrderConfirmVo activityCartList(Long userId,List cartInfos) {
        OrderConfirmVo cartActivityAndCoupon = activityFeignClient.findCartActivityAndCoupon(userId, cartInfos);
        return cartActivityAndCoupon;
    }
    //查询购物车中满足条件的优惠卷与活动
    @Override
    public OrderConfirmVo findCartActivityAndCoupon(Long userId, List cartInfos) {
        //查询购物车的商品参与的活动,根据活动规则进行分组
        List carInfoVoList = findCartActivityList(cartInfos);
        //计算参与活动之后的金额
        BigDecimal activityReduceAmount = carInfoVoList.stream()
                .filter(cartInfoVo -> cartInfoVo.getActivityRule() != null)
                .map(cartInfoVo -> cartInfoVo.getActivityRule().getReduceAmount())
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        //获取购物车可以使用优惠卷金额
        List couponInfoList = couponInfoService.findCouponInfo(userId,cartInfos);
        //计算商品使用优惠卷的金额,一次只能使用一张优惠卷
        BigDecimal couponReduceAmount = new BigDecimal(0);
        if (!CollectionUtils.isEmpty(couponInfoList)){
             couponReduceAmount = couponInfoList.stream()
                     .filter(couponInfo -> couponInfo.getIsOptimal().intValue() == 1)
                    .map(couponInfo -> couponInfo.getAmount())
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
        }
        //计算没有参与活动与优惠卷原始金额
        BigDecimal originalTotalAmount = cartInfos.stream()
                .filter(cartInfo -> cartInfo.getIsChecked().intValue() == 1)
                .map(cartInfo -> cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum())))
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        //最终总金额
        BigDecimal totalAmount = originalTotalAmount
                .subtract(activityReduceAmount).subtract(couponReduceAmount);
        OrderConfirmVo confirmVo = new OrderConfirmVo();
        confirmVo.setCarInfoVoList(carInfoVoList);
        confirmVo.setActivityReduceAmount(activityReduceAmount);
        confirmVo.setCouponInfoList(couponInfoList);
        confirmVo.setCouponReduceAmount(couponReduceAmount);
        confirmVo.setOriginalTotalAmount(originalTotalAmount);
        confirmVo.setTotalAmount(totalAmount);
        return confirmVo;
    }
    //查询购物车中对应的商品规则
    public List findCartActivityList(List cartInfos){
        //返回的数据类型
        List cartInfoVoList = new ArrayList<>();
        //获取所有商品skuid
        List skuIdList = cartInfos.stream().map(CartInfo::getSkuId).collect(Collectors.toList());
        //根据skuid查询参与对应活动
        List activitySkuList = new ArrayList<>();
        Map> activityIdToSkuIdListMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(skuIdList)){
            activitySkuList = baseMapper.selectActivityRule(skuIdList);
            //根据活动进行分组
            //key是活动id,value对应的商品skuid
             activityIdToSkuIdListMap = activitySkuList.stream()
                    .collect(Collectors.groupingBy(ActivitySku::getActivityId,
                            Collectors.mapping(ActivitySku::getSkuId, Collectors.toSet())));

        }
        //获取所有的活动id
        Set  activityIdSet = activitySkuList.stream().map(ActivitySku::getActivityId)
                .collect(Collectors.toSet());
        //获取活动对应的规则
        Map> activityIdToActivityRuleListMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(activityIdSet)){
            LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
            wrapper.in(ActivityRule::getActivityId,activityIdSet);
            wrapper.orderByAsc(ActivityRule::getConditionAmount,ActivityRule::getConditionNum);
            List activityRuleList = activityRuleMapper.selectList(wrapper);
            //根据活动id进行分组
            activityIdToActivityRuleListMap = activityRuleList.stream().collect(Collectors.groupingBy(activityRule -> activityRule.getActivityId()));
            //参加活动的商品
            Set activitySkuIdSet = new HashSet<>();
            if (!CollectionUtils.isEmpty(activityIdToSkuIdListMap)){
                //遍历map集合
                Iterator>> iterator = activityIdToSkuIdListMap.entrySet().iterator();
                while (iterator.hasNext()){
                    Map.Entry> next = iterator.next();
                    //map的key:活动id
                    Long activityId = next.getKey();
                    //value:活动的skuid
                    Set currentActivitySkuIdSet = next.getValue();
                    //获取当前活动对应的购物项
                    List currentActivityCartInfoList = cartInfos.stream().filter(cartInfo -> currentActivitySkuIdSet.contains(cartInfo.getSkuId())).collect(Collectors.toList());
                    //计数购物项的总金额与总数量
                    BigDecimal activityTotalAmount = computeTotalAmount(currentActivityCartInfoList);
                    int activityTotalNum = computeCartNum(currentActivityCartInfoList);
                    //计算活动对应规则
                    List currentActivityRuleList = activityIdToActivityRuleListMap.get(activityId);
                    ActivityType activityType = currentActivityRuleList.get(0).getActivityType();
                    //判断活动类型
                    ActivityRule optimalActivityRule;
                    if (activityType == ActivityType.FULL_REDUCTION){ //满减:满100减10
                        optimalActivityRule = computeFullReduction(activityTotalAmount, currentActivityRuleList);
                    }else { //折扣
                        optimalActivityRule = computeFullDiscount(activityTotalNum, activityTotalAmount, currentActivityRuleList);
                    }
                    //封装集合
                    CartInfoVo cartInfoVo = new CartInfoVo();
                    cartInfoVo.setActivityRule(optimalActivityRule);
                    cartInfoVo.setCartInfoList(currentActivityCartInfoList);
                    cartInfoVoList.add(cartInfoVo);
                    //记录参加活动的商品
                    activitySkuIdSet.addAll(currentActivitySkuIdSet);
                }
            }
           //未参加活动的商品
            //移除已经参加活动的skuid
            skuIdList.removeAll(activitySkuIdSet);
            if (!CollectionUtils.isEmpty(skuIdList)){
                //获取对应的购物项
                Map skuIdToCartInfoMap = cartInfos.stream()
                        .collect((Collectors.toMap(CartInfo::getSkuId, CartInfo -> CartInfo)));

                skuIdList.forEach(skuId->{
                    CartInfoVo cartInfoVo = new CartInfoVo();
                    cartInfoVo.setActivityRule(null);
                    CartInfo cartInfo = skuIdToCartInfoMap.get(skuId);
                    List cartInfoList = new ArrayList<>();
                    cartInfoList.add(cartInfo);
                    cartInfoVo.setCartInfoList(cartInfoList);
                    cartInfoVoList.add(cartInfoVo);
                });
            }
        }
        return cartInfoVoList;
    }
      /**
       * 计算满量打折最优规则
       * @param totalNum
       * @param activityRuleList //该活动规则skuActivityRuleList数据,已经按照优惠折扣从大到小排序了
       */
      private ActivityRule computeFullDiscount(Integer totalNum, BigDecimal totalAmount, List activityRuleList) {
          ActivityRule optimalActivityRule = null;
          //该活动规则skuActivityRuleList数据,已经按照优惠金额从大到小排序了
          for (ActivityRule activityRule : activityRuleList) {
              //如果订单项购买个数大于等于满减件数,则优化打折
              if (totalNum.intValue() >= activityRule.getConditionNum()) {
                  BigDecimal skuDiscountTotalAmount = totalAmount.multiply(activityRule.getBenefitDiscount().divide(new BigDecimal("10")));
                  BigDecimal reduceAmount = totalAmount.subtract(skuDiscountTotalAmount);
                  activityRule.setReduceAmount(reduceAmount);
                  optimalActivityRule = activityRule;
                  break;
              }
          }
          if(null == optimalActivityRule) {
              //如果没有满足条件的取最小满足条件的一项
              optimalActivityRule = activityRuleList.get(activityRuleList.size()-1);
              optimalActivityRule.setReduceAmount(new BigDecimal("0"));
              optimalActivityRule.setSelectType(1);

              StringBuffer ruleDesc = new StringBuffer()
                      .append("满")
                      .append(optimalActivityRule.getConditionNum())
                      .append("元打")
                      .append(optimalActivityRule.getBenefitDiscount())
                      .append("折,还差")
                      .append(totalNum-optimalActivityRule.getConditionNum())
                      .append("件");
              optimalActivityRule.setRuleDesc(ruleDesc.toString());
          } else {
              StringBuffer ruleDesc = new StringBuffer()
                      .append("满")
                      .append(optimalActivityRule.getConditionNum())
                      .append("元打")
                      .append(optimalActivityRule.getBenefitDiscount())
                      .append("折,已减")
                      .append(optimalActivityRule.getReduceAmount())
                      .append("元");
              optimalActivityRule.setRuleDesc(ruleDesc.toString());
              optimalActivityRule.setSelectType(2);
          }
          return optimalActivityRule;
      }

    /**
     * 计算满减最优规则
     * @param totalAmount
     * @param activityRuleList //该活动规则skuActivityRuleList数据,已经按照优惠金额从大到小排序了
     */
    private ActivityRule computeFullReduction(BigDecimal totalAmount, List activityRuleList) {
        ActivityRule optimalActivityRule = null;
        //该活动规则skuActivityRuleList数据,已经按照优惠金额从大到小排序了
        for (ActivityRule activityRule : activityRuleList) {
            //如果订单项金额大于等于满减金额,则优惠金额
            if (totalAmount.compareTo(activityRule.getConditionAmount()) > -1) {
                //优惠后减少金额
                activityRule.setReduceAmount(activityRule.getBenefitAmount());
                optimalActivityRule = activityRule;
                break;
            }
        }
        if(null == optimalActivityRule) {
            //如果没有满足条件的取最小满足条件的一项
            optimalActivityRule = activityRuleList.get(activityRuleList.size()-1);
            optimalActivityRule.setReduceAmount(new BigDecimal("0"));
            optimalActivityRule.setSelectType(1);

            StringBuffer ruleDesc = new StringBuffer()
                    .append("满")
                    .append(optimalActivityRule.getConditionAmount())
                    .append("元减")
                    .append(optimalActivityRule.getBenefitAmount())
                    .append("元,还差")
                    .append(totalAmount.subtract(optimalActivityRule.getConditionAmount()))
                    .append("元");
            optimalActivityRule.setRuleDesc(ruleDesc.toString());
        } else {
            StringBuffer ruleDesc = new StringBuffer()
                    .append("满")
                    .append(optimalActivityRule.getConditionAmount())
                    .append("元减")
                    .append(optimalActivityRule.getBenefitAmount())
                    .append("元,已减")
                    .append(optimalActivityRule.getReduceAmount())
                    .append("元");
            optimalActivityRule.setRuleDesc(ruleDesc.toString());
            optimalActivityRule.setSelectType(2);
        }
        return optimalActivityRule;
    }

    private BigDecimal computeTotalAmount(List cartInfoList) {
        BigDecimal total = new BigDecimal("0");
        for (CartInfo cartInfo : cartInfoList) {
            //是否选中
            if(cartInfo.getIsChecked().intValue() == 1) {
                BigDecimal itemTotal = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
                total = total.add(itemTotal);
            }
        }
        return total;
    }

    private int computeCartNum(List cartInfoList) {
        int total = 0;
        for (CartInfo cartInfo : cartInfoList) {
            //是否选中
            if(cartInfo.getIsChecked().intValue() == 1) {
                total += cartInfo.getSkuNum();
            }
        }
        return total;
    }
 //获取购物车可以使用优惠卷金额
    @Override
    public List findCouponInfo(Long userId, List cartInfos) {
        //根据用户id查询所有优惠卷
        List userAllCouponInfoList = baseMapper.selectCartCouponInfoList(userId);
        if (CollectionUtils.isEmpty(userAllCouponInfoList)){
            return new ArrayList();
        }
        //获取所有优惠卷id列表
        List  couponInfoIdList = userAllCouponInfoList.stream().map(couponInfo -> couponInfo.getId()).collect(Collectors.toList());
        //查询优惠卷使用范围
        LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>();
        wrapper.in(CouponRange::getCouponId,couponInfoIdList);
        List couponRangeList = couponRangeMapper.selectList(wrapper);
        //获取优惠卷对应的sku
        Map> couponIdAndSkuMap = findCouponIdAndSkuList(cartInfos,couponRangeList);
        //遍历全部优惠卷集合,判断优惠卷类型:全场通用,sku和分类
        BigDecimal reduceAmount = new BigDecimal(0);
        CouponInfo optimalCouponInfo = null;
        for (CouponInfo couponInfo : userAllCouponInfoList) {
            //全场通用
            //判断是否满足优惠使用门槛
            //计算购物车商品的总价
            if (CouponRangeType.ALL == couponInfo.getRangeType()){
                BigDecimal totalAmount = computeTotalAmount(cartInfos);
                if(totalAmount.subtract(couponInfo.getConditionAmount()).doubleValue() >= 0){
                    couponInfo.setIsSelect(1);
                }
            } else {
                //根据优惠卷id获得skuid集合
                List skuIdList = couponIdAndSkuMap.get(couponInfo.getId());
                //满足使用范围的购物项
                List currentCartInfoList = cartInfos.stream().filter(cartInfo -> skuIdList.contains(cartInfo.getSkuId())).collect(Collectors.toList());
                BigDecimal totalAmount = computeTotalAmount(currentCartInfoList);
                if(totalAmount.subtract(couponInfo.getConditionAmount()).doubleValue() >= 0){
                    couponInfo.setIsSelect(1);
                }
            }
            if (couponInfo.getIsSelect().intValue() == 1 && couponInfo.getAmount().subtract(reduceAmount).doubleValue() > 0) {
                reduceAmount = couponInfo.getAmount();
                optimalCouponInfo = couponInfo;
            }
            //
            if(null != optimalCouponInfo){
                optimalCouponInfo.setIsOptimal(1);
            }
        }
        return userAllCouponInfoList;
    }
    private BigDecimal computeTotalAmount(List cartInfoList) {
        BigDecimal total = new BigDecimal("0");
        for (CartInfo cartInfo : cartInfoList) {
            //是否选中
            if(cartInfo.getIsChecked().intValue() == 1) {
                BigDecimal itemTotal = cartInfo.getCartPrice().multiply(new BigDecimal(cartInfo.getSkuNum()));
                total = total.add(itemTotal);
            }
        }
        return total;
    }
    //获取优惠卷对应的sku
    private Map> findCouponIdAndSkuList(List cartInfos, List couponRangeList) {
        Map> map = new HashMap<>();
        //根据优惠卷id进行分组
        Map> couponRangeMap = couponRangeList.stream()
                .collect(Collectors
                        .groupingBy(couponRange -> couponRange.getCouponId()));
        //遍历map集合
        Iterator>> iterator = couponRangeMap.entrySet().iterator();
        while (iterator.hasNext()){
            Map.Entry> entry = iterator.next();
            Long couponId = entry.getKey();
            List couponRanges = entry.getValue();
            Set skuIdSet = new HashSet<>();
            cartInfos.stream().forEach(cartInfo -> {
                couponRanges.stream().forEach(couponRange -> {
                    //判断
                    if (couponRange.getRangeType() == CouponRangeType.SKU
                            && couponRange.getRangeId().longValue() == cartInfo.getSkuId()){
                        skuIdSet.add(cartInfo.getSkuId());
                    } else if(couponRange.getRangeType() == CouponRangeType.CATEGORY
                            && couponRange.getRangeId().longValue() == cartInfo.getCategoryId()){
                        skuIdSet.add(cartInfo.getSkuId());
                    }else {
                    }
                });
            });
            map.put(couponId,new ArrayList<>(skuIdSet));
        }
        return map;
    }

你可能感兴趣的:(项目,java)