目录
一、项目工程搭建
二、配置项目相关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、多任务组合
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}
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
org.apache.httpcomponents
httpclient
io.jsonwebtoken
jjwt
joda-time
joda-time
com.cjc
model
1.0-SNAPSHOT
provided
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
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
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
@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);
}
}
@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;
}
}
//自定义异常信息
@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);
}
}
@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();
}
}
#全局安装命令行工具
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解决办法:以管理员的方式运行即可
运行指令:npm run dev
spring:
application:
name: service-acl
profiles:
active: dev
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
@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);
}
}
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]
解决办法:注销相关依赖
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.
解决方法:
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);
}
}
测试结果:
@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);
}
}
后端接口测试以添加为例:
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);
}
}
}
接口测试以添加为例:
前端界面效果:
功能分析图:
功能实现:
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);
}
前端效果图:
数据格式工具类
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);
}
接口测试:
/**
* 递归删除菜单
* @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);
}
@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);
}
前端效果图:
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);
}
前端效果图:
需要的表
导入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();
}
}
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);
}
接口测试:
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);
}
接口测试:
/**
* 取消开通区域
* @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);
}
在配置文件中配置
文件内容:
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;
}
}
修改前端:
测试效果:
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反向代理
前端测试效果:
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);
}
}
前端效果图:
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);
}
}
前端效果图:
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);
}
前端效果图:
导入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);
}
}
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);
}
前端效果:
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);
}
前端效果:
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);
}
前端效果图:
下载地址:Releases · alibaba/nacos (github.com)
下载完解压即可
运行命令:startup.cmd -m standalone
测试访问:
cloud: nacos: discovery: server-addr: localhost:8848
在启动类上添加注解@EnableDiscoveryClient
测试效果:
功能架构图:
核心:提高搜索的性能
下载解压即可:
测试访问:
修改kibana.yml
测试访问:
导入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 {
}
@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;
}
}
@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);
}
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);
}
}
拉取镜像:docker pull rabbitmq:3.8-management
创建启动容器:
docker run -d --restart=always -p 5672:5672 -p 15672:15672 --name rabbitmq rabbitmq:3.8-management
导入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";
}
修改相关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);
}
}
测试结果:
点击下架或者删除都应该清空es
数据库表
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)
解决:
页面效果:
远程调用接口及方法
@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);
}
远程接口:
//根据关键字查询
@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);
}
前端效果图:
远程接口业务代码:
@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);
}
页面效果:
优惠卷规则:
导入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);
}
}
添加启动类添加注解:
地址:微信公众平台
地址:微信开发者工具下载地址与更新日志 | 微信开放文档
下载地址:HBuilderX-高效极客技巧
地址:会员登陆
1、调用 wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
2、调用 auth.code2Session 接口,换取 用户唯一标识 OpenID 、 用户在微信开放平台帐号下的唯一标识UnionID(若当前小程序已绑定到微信开放平台帐号) 和 会话密钥 session_key。
3、之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。
JWT(Json Web Token)是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准。
JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源。比如用在用户登录上
JWT最重要的作用就是对 token信息的防伪作用。
一个JWT由三个部分组成:公共部分、私有部分、签名部分。最后由这三者组合进行base64编码得到JWT。
1、 公共部分
主要是该JWT的相关配置参数,比如签名的加密算法、格式类型、过期时间等等。
Key=ATGUIGU
2、 私有部分
用户自定义的内容,根据实际需要真正要封装的信息。
userInfo{用户的Id,用户的昵称nickName}
3、 签名部分
SaltiP: 当前服务器的Ip地址!{linux 中配置代理服务器的ip}
主要用户对JWT生成字符串的时候,进行加密{盐值}
最终组成 key+salt+userInfo è token!
导入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));
}
}
@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
修改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 #最小空闲
修改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;
}
}
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;
}
}
}
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获取登录的用户信息
在AOP(Aspect-Oriented Programming)中用于在某个方法或字段被访问之前,进行拦截然后在之前或之后加入某些操作,拦截是AOP的一种实现策略
第一步,实现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);
}
远程接口
@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);
}
页面效果
@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);
}
页面效果:
核心:1、查询商品的基本信息(远程调用), 2、查询相关优惠活动信息(远程调用),3、更新商品热度(远程调用)
优化:使用多线程优化远程调用 (技术点)使用CompletableFuture类
创建线程方式:1、继承Thread类,2、实现Runnable接口,3、实现Callable接口,4、使用线程池。
没有返回值
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("主线程结束执行");
}
}
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("主线程结束执行");
}
}
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);
}
}
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("主线程结束执行");
}
}
自定义线程池
@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);
}
//添加商品到购物车
@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);
}
//根据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());
}
});
}
//查询购物车列表
@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;
}