cloud_mall-notes01

1、登录

1.1 获取token令牌

登录时的ajax请求:
在这里插入图片描述
.

后端路由配置处理:

登录的路由配置
作用:把oAuth2.0颁发的token存储到redis中

package com.powernode.config;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.powernode.constant.GatewayConstant;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.StringRedisTemplate;
import reactor.core.publisher.Mono;

import java.time.Duration;

/**
 * 登录的路由配置
 * 作用:把oAuth2.0颁发的token存储到redis中
 */
@Configuration
public class LoginRouteConfig {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 代码方式的路由存储token
     *
     * @param builder
     * @return
     */
    @Bean
    public RouteLocator loginRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                .route("auth-server-route", r -> r.path("/oauth/token").filters(
                        f -> f.modifyResponseBody(String.class, String.class, (exchange, s) -> {
                            //s为响应的结果,类型为json,结构为{"access_token","expires_in"}
                            //将响应的json数据转换为json对象
                            JSONObject jsonObject = JSON.parseObject(s);
                            //查看是否包含access_token
                            if (jsonObject.containsKey("access_token")) {
                                //有:存放到redis中
                                //获取token值和过期时间
                                String access_token = jsonObject.getString("access_token");
                                Long expires_in = jsonObject.getLong("expires_in");
                                //将获取的值存放到redis中
                                stringRedisTemplate.opsForValue().set(GatewayConstant.TOKEN_PREFIX+access_token,"", Duration.ofSeconds(expires_in));
                            }
                            return Mono.just(s);
                            //uri是路由的目的地,(lb://auth-server是授权中心服务名称)
                        })).uri("lb://auth-server"))
                .build();
    }
}

.

Redis拿到的缓存:
在这里插入图片描述

前端返回响应的JSON数据,与Redis一致:
在这里插入图片描述
解析部分JSON数据:
在这里插入图片描述
.

前端代码处理token,放进cookie中:在这里插入图片描述

1.2 根据用户标识获取菜单和权限集合

前端发出的 ajax 请求:
在这里插入图片描述

.

利用mybatis的一个插件,生成相应的代码:
在这里插入图片描述
在这里插入图片描述
.

会生成domain实体类,service接口及实现类,mapper接口及实现xml文件:
在这里插入图片描述
在这里插入图片描述
.
创建一个controller类:controller.SysMenuController

package com.powernode.controller;

import com.powernode.domain.SysMenu;
import com.powernode.service.SysMenuService;
import com.powernode.utils.AuthUtil;
import com.powernode.vo.MenuAndAuth;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Api(tags = "菜单权限接口管理")
@RequestMapping("sys/menu")
@RestController
public class SysMenuController {

    @Autowired
    private SysMenuService sysMenuService;

    @ApiOperation("根据用户标识查询菜单和权限集合")
    @GetMapping("nav")
    public ResponseEntity<MenuAndAuth> loadUserMenuAndAuth() {
        //获取用户标识
//        String userId = SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString();
        String userId = AuthUtil.getLoginUserId();

        //根据用户标识查询菜单和权限集合
        //获取权限集合
        Collection<? extends GrantedAuthority> authorities = SecurityContextHolder.getContext().getAuthentication().getAuthorities();
        List<String> auths = authorities.stream().map(Objects::toString).collect(Collectors.toList());
        //根据用户id查询菜单集合
        List<SysMenu> sysMenuList = sysMenuService.selectSysMenuListByUid(userId);

        //成功,并没有数据返回
//        return ResponseEntity.ok().build();
        //成功,有数据返回
//        return ResponseEntity.ok(数据);
        MenuAndAuth menuAndAuth = new MenuAndAuth(sysMenuList,auths);
        return ResponseEntity.ok(menuAndAuth);
    }

//    sys/menu/table
    @ApiOperation("查询系统权限集合")
    @GetMapping("table")
    @PreAuthorize("hasAuthority('sys:menu:list')")
    public ResponseEntity<List<SysMenu>> loadSysMenuList() {
        List<SysMenu> list = sysMenuService.list();
        return ResponseEntity.ok(list);
    }

}

其中:
1、创建一个vo.MenuAndAuth类:
(用于返回菜单和权限的集合对象)

package com.powernode.vo;

import com.powernode.domain.SysMenu;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@ApiModel("菜单和权限对象")
public class MenuAndAuth {

    @ApiModelProperty("菜单集合")
    private List<SysMenu> menuList;

    @ApiModelProperty("权限集合")
    private List<String> authorities;

}

2、前端代码,响应的数据属性名,也要和后端封装返回的菜单和权限属性名相同:

3、封装获取用户标识工具类:

package com.powernode.utils;

import org.springframework.security.core.context.SecurityContextHolder;

public class AuthUtil {

    public static String getLoginUserId() {
        return SecurityContextHolder.getContext().getAuthentication().getPrincipal().toString();
    }
}

4、业务层实现方法:
业务接口:

package com.powernode.service;

import com.powernode.domain.SysMenu;
import com.baomidou.mybatisplus.extension.service.IService;

import java.util.List;

public interface SysMenuService extends IService<SysMenu>{

    /**
     * 根据用户id查询菜单集合
     * @param userId
     * @return
     */
    List<SysMenu> selectSysMenuListByUid(String userId);
}

业务层实现类:
a.查询菜单和权限(权限一时半会变不了,使用redis缓存)
b.展开菜单,利用“树的深度”,采用递归方法

package com.powernode.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.powernode.constant.ManagerConstant;
import com.powernode.domain.SysMenu;
import com.powernode.mapper.SysMenuMapper;
import com.powernode.service.SysMenuService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService{

    @Autowired
    private SysMenuMapper sysMenuMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Override
    public List<SysMenu> selectSysMenuListByUid(String userId) {
        //先从redis中查询用户的菜单集合
        String jsonStr = stringRedisTemplate.opsForValue().get(ManagerConstant.PREFIX_MENU + userId); // "menu:" + userId
        //判断是否有值
        List<SysMenu> sysMenuList = null;
        if (StringUtils.hasText(jsonStr)) {
            //有:直接使用,将json格式的字符串转换为list集合
            sysMenuList = JSONObject.parseArray(jsonStr, SysMenu.class);
        } else {
            //没有:去数据库查询,并存放到redis缓存中
            //根据用户id查询菜单集合
            sysMenuList = sysMenuMapper.selectSysMenuListByUid(userId);
            //存放到redis缓存中(7天)
            stringRedisTemplate.opsForValue().set(ManagerConstant.PREFIX_MENU + userId, JSON.toJSONString(sysMenuList), Duration.ofDays(7));
        }
        //将集合转换为树结构
        return transformTree(sysMenuList,0L);
    }

    /**
     * 集合转换为树结构,一般分为2种情况:
     *
     * 1.已知菜单深度,深度<=2
     * 2.未知菜单深度:使用递归
     *
     * @param sysMenuList
     * @param pid
     * @return
     */
    private List<SysMenu> transformTree(List<SysMenu> sysMenuList, Long pid) {
        /*//获取菜单的根节点
        List<SysMenu> root = sysMenuList.stream()
                .filter(sysMenu -> sysMenu.getParentId().equals(pid))
                .collect(Collectors.toList());
        //循环遍历根节点(获取每一个根节点的子节点集合)
        root.forEach(r -> {
            //从节点集合中来过滤出节点的父节点id与当前根节点的节点id值一致的节点集合
            List<SysMenu> child = sysMenuList.stream()
                    .filter(sysMenu -> sysMenu.getParentId().equals(r.getMenuId()))
                    .collect(Collectors.toList());
            r.setList(child);
        });*/
        //第2种情况,菜单深度未知
        //获取菜单的根节点
        List<SysMenu> root = sysMenuList.stream()
                .filter(sysMenu -> sysMenu.getParentId().equals(pid))
                .collect(Collectors.toList());
        root.forEach(r -> r.setList(transformTree(sysMenuList,r.getMenuId())));
        return root;
    }
}

涉及到的,1-实体类(菜单管理类):

package com.powernode.domain;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;
import java.util.List;

/**
 * 菜单管理
 */
@ApiModel(value="com-powernode-domain-SysMenu")
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "sys_menu")
public class SysMenu implements Serializable {
    @TableId(value = "menu_id", type = IdType.AUTO)
    @ApiModelProperty(value="")
    private Long menuId;

    /**
     * 父菜单ID,一级菜单为0
     */
    @TableField(value = "parent_id")
    @ApiModelProperty(value="父菜单ID,一级菜单为0")
    private Long parentId;

    /**
     * 菜单名称
     */
    @TableField(value = "name")
    @ApiModelProperty(value="菜单名称")
    private String name;

    /**
     * 菜单URL
     */
    @TableField(value = "url")
    @ApiModelProperty(value="菜单URL")
    private String url;

    /**
     * 授权(多个用逗号分隔,如:user:list,user:create)
     */
    @TableField(value = "perms")
    @ApiModelProperty(value="授权(多个用逗号分隔,如:user:list,user:create)")
    private String perms;

    /**
     * 类型   0:目录   1:菜单   2:按钮
     */
    @TableField(value = "type")
    @ApiModelProperty(value="类型   0:目录   1:菜单   2:按钮")
    private Integer type;

    /**
     * 菜单图标
     */
    @TableField(value = "icon")
    @ApiModelProperty(value="菜单图标")
    private String icon;

    /**
     * 排序
     */
    @TableField(value = "order_num")
    @ApiModelProperty(value="排序")
    private Integer orderNum;

    //当前属性并没有对应表中的一个字段名
    @TableField(exist = false)
    @ApiModelProperty("子菜单集合")
    private List<SysMenu> list;

    private static final long serialVersionUID = 1L;
}

涉及到的,2-常量(redis缓存需要的常量):

package com.powernode.constant;

/**
 * 读取redis缓存需要的常量
 */
public interface ManagerConstant {

    String PREFIX_MENU = "menu:";

    String ROLE_LIST = "'role:list'";
}

涉及到的,3-mapper(根据用户id查询菜单集合的SQL语句):

package com.powernode.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.powernode.domain.SysMenu;
import org.apache.ibatis.annotations.Select;

import java.util.List;

public interface SysMenuMapper extends BaseMapper<SysMenu> {

    @Select("select DISTINCT t1.* from sys_menu t1 join sys_role_menu t2 join sys_user_role t3\n" +
            "on (t1.menu_id = t2.menu_id and t2.role_id = t3.role_id)\n" +
            "where t3.user_id = #{userId} and (t1.type = 0 or t1.type = 1)")
    List<SysMenu> selectSysMenuListByUid(String userId);

}

1.3 查询管理员信息

前端发出的 ajax 请求:
在这里插入图片描述
.

创建一个controller类,关于用户的SysUserController:
在这里插入图片描述

1.4 管理员退出

创建一个controller类,关于退出的的LogoutController,删除缓存中的token

package com.powernode.controller;

import com.powernode.constant.GatewayConstant;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;

@Api(tags = "用户退出")
@RestController
public class LogoutController {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @ApiOperation("管理员退出")
    @PostMapping("sys/logout")
    public ResponseEntity<Void> logout(HttpServletRequest request) {
        //获取令牌:bearer jwt
        String authorization = request.getHeader(GatewayConstant.AUTHORIZATION);
        //获取jwt
        String jwt = authorization.replaceAll(GatewayConstant.BEARER, "");
        //将缓存中的token删除
        stringRedisTemplate.delete(GatewayConstant.TOKEN_PREFIX+jwt);

        return ResponseEntity.ok().build();
    }
}

2、管理员功能

2.1 多条件分页查询管理员列表

controller类,关于用户的SysUserController,添加分页查询方法
(利用mybatis的page对象,以及条件构造器LambdaQueryWrapperlike()orderByDesc()方法对分页进行模糊查询和排序):

在这里插入图片描述
其中: @PreAuthorize获取权限控制(根据数据库的表中的字段上的值)

2.2 查询系统角色集合

创建一个controller类,关于用户的SysRoleController:
在这里插入图片描述
.

业务层实现list方法:
(开启缓存@Cacheable(key = ManagerConstant.ROLE_LIST))
在这里插入图片描述

2.3 新增管理员

前端发出的 ajax 请求:
在这里插入图片描述
在这里插入图片描述
需要在userdomain实体类添加roleList这个属性:
在这里插入图片描述
.
controller类,关于用户的SysUserController,添加新增管理员的方法
(重写业务层的save方法):

在这里插入图片描述
业务层重写save方法:在这里插入图片描述
在这里插入图片描述
其中在Application启动类注入加密器BCryptPasswordEncoder

package com.powernode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

@SpringBootApplication
@EnableEurekaClient
@EnableCaching//开启注解式缓存(默认使用的缓存中间件是redis)
public class ManagerServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(ManagerServiceApplication.class,args);
    }

    // 注入加密器
    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }
}

2.4 查询管理员详情

前端发出的 ajax 请求:
在这里插入图片描述

.

controller类,关于用户的SysUserController,添加查询管理员详情的方法
(重写业务层的getById方法):

在这里插入图片描述
业务层重写getById方法:
1.利用条件构造器LambdaQueryWrapper的eq()方法查询
2.利用stream流获取查询对象的集合
在这里插入图片描述

2.5 修改管理员信息

前端发出的 ajax 请求:

在这里插入图片描述

.

controller类,关于用户的SysUserController,添加修改管理员信息的方法:
在这里插入图片描述

业务层重写updateById方法:
1.利用条件构造器LambdaQueryWrapper的eq()方法查询
2.密码处理。加密器passwordEncoder.encode()
3.添加事务 @Transactional(rollbackFor = RuntimeException.class)

在这里插入图片描述
在这里插入图片描述
其中,要更改mybatis更新策略:
在这里插入图片描述

2.6 批量删除管理员

前端发出的 ajax 请求:

在这里插入图片描述

.

controller类,关于用户的SysUserController,添加修改管理员信息的方法:
在这里插入图片描述

业务层重写updateById方法:
1.利用条件构造器LambdaQueryWrapper的in()方法查询
2.添加事务 @Transactional(rollbackFor = RuntimeException.class)

在这里插入图片描述

在这里插入图片描述

3、角色功能

各层之间增删改查起名:
//service:add,remove,modify,query
//dao:insert,delete,update,select

3.1 多条件分页查询角色列表

controller:
在这里插入图片描述

3.2 查询系统权限集合

controller:
在这里插入图片描述

3.3 新增角色

controller:
在这里插入图片描述

业务层serviceimpl:
在这里插入图片描述

3.4 查询角色详情

controller:
在这里插入图片描述

业务层serviceimpl:
在这里插入图片描述

3.5 修改角色信息

controller:
在这里插入图片描述

业务层serviceimpl:
在这里插入图片描述

3.6 批量删除系统角色

controller:
在这里插入图片描述

业务层serviceimpl:
在这里插入图片描述

4、日志管理

4.1 使用aop和自定义注解实现记录操作为日志

4.1.1 自定义日志注解

package com.powernode.anno;

import java.lang.annotation.*;

@Target(ElementType.METHOD)//注解使用在方法上
@Retention(RetentionPolicy.RUNTIME)//元注解,指定运行时生效
@Documented
@Inherited
public @interface Log {
    //String属性值的类型
    //operation()->属性名称
    //default "" -> 属性的默认值
    String operation() default "";
}

4.1.2 使用自定义注解例子

在这里插入图片描述
等等…

4.1.3 日志切面类

package com.powernode.aspect;

import com.alibaba.fastjson.JSON;
import com.powernode.anno.Log;
import com.powernode.domain.SysLog;
import com.powernode.domain.SysUser;
import com.powernode.service.SysLogService;
import com.powernode.service.SysUserService;
import com.powernode.util.ManagerThreadPool;
import com.powernode.utils.AuthUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

@Component
@Aspect
public class LogAspect {

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private SysLogService sysLogService;

    @Around(value = "@annotation(com.powernode.anno.Log)")
    public Object logAround(ProceedingJoinPoint joinPoint) {
        Object resultt = null;
        //获取目标方法的参数
        Object[] args = joinPoint.getArgs();
        //获取目标方法
        //获取目标方法的签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        //通过签名对象来获取方法对象
        Method method = signature.getMethod();
        //获取目标方法名称
        String methodName = method.toString();
        //获取目标方法上指定的注解
        Log logAnnotation = method.getAnnotation(Log.class);
        String operation = logAnnotation.operation();
        //获取ip地址
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        String ip = request.getRemoteAddr();
        //开始时间
        long start = System.currentTimeMillis();
        try {
            //执行方法
            resultt = joinPoint.proceed(args);
        } catch (Throwable e) {
            throw new RuntimeException(e);
        }
        //结束时间
        long end = System.currentTimeMillis();
        //方法执行时长
        long execTime = end - start;
        //获取当前用户id
        String userId = AuthUtil.getLoginUserId();
        SysUser sysUser = sysUserService.getById(userId);

        //开启多线程操作
        ManagerThreadPool.poolExecutor.execute(()->{
            //新增操作日志
            SysLog sysLog = SysLog.builder()
                    .ip(ip)
                    .createDate(new Date())
                    .operation(operation)
                    .method(methodName)
                    .params(args == null ? "": JSON.toJSONString(args))
                    .time(execTime)
                    .username(sysUser.getUsername())
                    .build();
            sysLogService.save(sysLog);
        });
        return resultt;
    }
}

数据库记录下来的操作:
在这里插入图片描述

4.2 使用线程池来记录操作为日志

先了解线程池参数的意义:Java线程池的七个参数

4.2.1 创建线程池工具类ManagerThreadPool

package com.powernode.util;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ManagerThreadPool {
    public static ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
            4,
            Runtime.getRuntime().availableProcessors(),
            30,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(30),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
    );
}

4.2.2 在切面类引入线程池:

在这里插入图片描述

4.3 多条件查询操作形为日志

package com.powernode.controller;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.powernode.domain.SysLog;
import com.powernode.service.SysLogService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Api(tags = "操作日志接口管理")
@RequestMapping("sys/log")
@RestController
public class SysLogController {

    @Autowired
    private SysLogService sysLogService;


    @ApiOperation("多条件查询操作形为日志")
    @GetMapping("page")
    @PreAuthorize("hasAuthority('sys:log:page')")
    public ResponseEntity<Page<SysLog>> loadSysLogPage(Page<SysLog> page,SysLog sysLog) {
        page = sysLogService.page(page,new LambdaQueryWrapper<SysLog>()
                .like(StringUtils.hasText(sysLog.getUsername()),SysLog::getUsername,sysLog.getUsername())
                .like(StringUtils.hasText(sysLog.getOperation()),SysLog::getOperation,sysLog.getOperation())
                .orderByDesc(SysLog::getCreateDate)
        );
        return ResponseEntity.ok(page);
    }
}

5、商品功能

5.1 查询商品类目集合

controller:
在这里插入图片描述

5.2 查询商品一级类目集合

controller:
在这里插入图片描述

5.3 阿里云对象存储服务—简单上传

官网api接口文档:简单上传

package com.powernode.controller;

import cn.hutool.core.date.DateUtil;
import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.model.PutObjectRequest;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.net.URL;
import java.util.Date;

@Api(tags = "文件上传接口管理")
@RequestMapping("admin/file")
@RestController
public class FileController {

    @Value("${oss.endpoint}")
    private String endpoint;
    @Value("${oss.accessKeyId}")
    private String accessKeyId;
    @Value("${oss.accessKeySecret}")
    private String accessKeySecret;
    @Value("${oss.bucketName}")
    private String bucketName;

    @ApiOperation("上传单个文件")
    @PostMapping("upload/element")
    public ResponseEntity<String> uploadFile(@RequestBody MultipartFile file) {
        //创建以天为单位的文件夹名称
        String folderName = DateUtil.format(new Date(),"yyyy-MM-dd");
        //定义新的文件名称
        String fileName = DateUtil.format(new Date(),"HHmmssSSS");
        //获取原文件名称
        String originalFilename = file.getOriginalFilename();
        //获取文件的后缀名称
        String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));

        // 填写Object完整路径,完整路径中不能包含Bucket名称,例如exampledir/exampleobject.txt。
        String objectName = folderName+"/"+fileName+suffix;

        // 创建OSSClient实例。
        OSS ossClient = new OSSClientBuilder().build(endpoint,accessKeyId,accessKeySecret);
        URL url = null;
        try {
            // 创建PutObjectRequest对象。
            PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, objectName, file.getInputStream());
            // 上传请求文件对象
            ossClient.putObject(putObjectRequest);
            //获取访问刚上传文件的路径
            url = ossClient.generatePresignedUrl(bucketName, objectName, DateUtil.offsetDay(new Date(), 365 * 10));
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } catch (IOException e) {
            throw new RuntimeException(e);
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
        return ResponseEntity.ok(url.toString());
    }
}

配置文件:
在这里插入图片描述

5.4 新增商品类目

controller:
在这里插入图片描述

业务层serviceimpl:
000000000

5.5 根据标识查询商品类目详情

controller:
在这里插入图片描述

5.6 修改商品类目信息

controller:
在这里插入图片描述

业务层serviceimpl:
在这里插入图片描述

5.7 删除商品类目

controller:
在这里插入图片描述

业务层serviceimpl:
在这里插入图片描述

5.8 多条件分页查询商品分组标签列表

controller:
在这里插入图片描述

5.9 新增商品分组标签

controller:
在这里插入图片描述

业务层serviceimpl:
在这里插入图片描述

5.10 根据标识查询商品分组标签详情

controller:
在这里插入图片描述

5.11 修改商品分组标签信息

controller:
在这里插入图片描述

业务层serviceimpl:
在这里插入图片描述

5.12 删除商品分组标签

controller:
在这里插入图片描述

业务层serviceimpl:
在这里插入图片描述

5.13 多条件分页查询商品规格列表

controller:
在这里插入图片描述

业务层serviceimpl:
(跨表查询,多表变单表方法)
在这里插入图片描述

5.14 新增商品规格

在这里插入图片描述
在这里插入图片描述

5.15 修改商品规格

在这里插入图片描述
在这里插入图片描述

5.16 删除商品规格

在这里插入图片描述
在这里插入图片描述

5.17 多条件分页查询商品评论列表

多表操作:
原表没有需要查询的字段,在domain实体类添加需要的属性:
在这里插入图片描述
在这里插入图片描述

controller:
在这里插入图片描述

业务层serviceimpl:
在这里插入图片描述

5.18 根据标识查询评论详情

在这里插入图片描述

5.19 审核并回复商品评论

在这里插入图片描述
在这里插入图片描述

5.20 多条件分页查询商品列表

在这里插入图片描述

5.21 查询商品分组标签集合

在这里插入图片描述
在这里插入图片描述

5.22 查询商品规格

在这里插入图片描述
在这里插入图片描述

5.23 新增商品

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

5.24 查询商品详情

在这里插入图片描述
在这里插入图片描述

5.25 修改商品信息

在这里插入图片描述
在这里插入图片描述

5.26 批量删除商品

在这里插入图片描述
在这里插入图片描述

6、门店服务

6.1 多条件分页查询公告列表

在这里插入图片描述

6.2 新增公告

在这里插入图片描述
在这里插入图片描述

6.3 查看公告详情

在这里插入图片描述

6.4 修改公告信息

在这里插入图片描述
在这里插入图片描述

6.5 删除公告

在这里插入图片描述
在这里插入图片描述

6.6 查询全国地址列表

在这里插入图片描述

6.7 多条件分页查询自提点列表

在这里插入图片描述

6.8 根据父节点查询地区子节点集合

在这里插入图片描述

6.9 新增自提点

在这里插入图片描述
在这里插入图片描述

6.10 查询自提点详情

在这里插入图片描述

6.11 修改自提点信息

在这里插入图片描述

6.12 批量删除自提点地址

在这里插入图片描述

6.13 多条件分页查询轮播图列表

在这里插入图片描述

6.14 新增轮播图

在这里插入图片描述
在这里插入图片描述

6.15 根据标识查询轮播图详情

在这里插入图片描述
在这里插入图片描述

6.16 修改轮播图信息

在这里插入图片描述
在这里插入图片描述

6.17 批量删除轮播图

在这里插入图片描述
在这里插入图片描述

7、微信小程序

7.1 微信小程序登录

package com.powernode.service.impl;

/**
 * 登录:2个前端,pc端和小程序端
 */

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.powernode.constant.AuthConstant;
import com.powernode.domain.LoginUser;
import com.powernode.domain.SysLoginUser;
import com.powernode.mapper.LoginUserMapper;
import com.powernode.mapper.SysLoginUserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;

@Service
public class UserDetailsServiceImpl implements UserDetailsService {

    @Autowired
    private SysLoginUserMapper sysLoginUserMapper;

    @Autowired
    private LoginUserMapper loginUserMapper;

    @Value("${wx.appid}")
    private String appid;
    @Value("${wx.secret}")
    private String secret;
    @Value("${wx.url}")
    private String url;

    /**
     * 根据用户名查询数据库的方法
     *  (因为我们有2个前端,所以要区分开来)
     *
     * @param username
     * @return
     * @throws UsernameNotFoundException
     */
    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        //获取request请求对象
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = requestAttributes.getRequest();
        //获取请求头中的用户类型(后台管理员or会员)
        String loginType = request.getHeader(AuthConstant.LOGIN_TYPE);
        //判断是否为空
        if (StrUtil.isEmpty(loginType)) {
            return null;
        }
        //判断用户类型
        switch (loginType) {
            case AuthConstant.SYS_USER:
                //后台管理员用户
                //根据用户名查询用户
                SysLoginUser sysLoginUser = sysLoginUserMapper.selectOne(new LambdaQueryWrapper<SysLoginUser>()
                        .eq(SysLoginUser::getUsername, username)
                );
                //判断用户是否存在
                if (ObjectUtil.isNotNull(sysLoginUser)) {
                    //后台管理员有权限,查询当前登录管理员的权限
                    List<String> auths = sysLoginUserMapper.selectAuthsByUserId(sysLoginUser.getUserId());
                    if (CollectionUtil.isNotEmpty(auths) && auths.size() != 0) {
                        //将查询出来的权限封装到用户对象中
                        sysLoginUser.setAuths(auths);
                    }
                }
                return sysLoginUser;
            case AuthConstant.MEMBER:
                //请求来自于微信小程序客户端
                //调用登录凭证校验接口,想获取微信客户端的openid
                //当我们拿到打开微信小程序的微信客户端openid然后来创建我们小程序内的用户体系
                String realUrl = String.format(url, appid, secret, username);
                String resultJsonStr = HttpUtil.get(realUrl);
                //使用fastjson来解析json格式的字符串
                JSONObject jsonObject = JSONObject.parseObject(resultJsonStr);
                //获取openid
                String openid = jsonObject.getString("openid");
                //我们需要通过openid来查询当前用户是否已经是我们用户体系内的用户
                LoginUser loginUser = loginUserMapper.selectOne(new LambdaQueryWrapper<LoginUser>()
                        .eq(LoginUser::getUserId, openid)
                );
                //判断是否有值
                if (ObjectUtil.isNull(loginUser)) {
                    //如果不是:我们需要完成用户注册
                    loginUser = createLoginUser(openid,request);
                }
                //如果是:即登录完成
                return loginUser;
        }
        return null;
    }

    private LoginUser createLoginUser(String openid, HttpServletRequest request) {
        //获取远程ipf址地
        String remoteHost = request.getRemoteHost();
        LoginUser loginUser = LoginUser.builder()
                .userId(openid)
                .userRegtime(new Date())
                .modifyTime(new Date())
                .userLasttime(new Date())
                .userLastip(remoteHost)
                .userRegip(remoteHost)
                .status(1).build();
        //插入数据库
        loginUserMapper.insert(loginUser);
        return loginUser;
    }
}

在这里插入图片描述

8、会员功能

8.1 更新用户的头像和昵称

在这里插入图片描述

8.2 查询用户是否绑定手机号码

在这里插入图片描述

8.3 查询当前用户收藏商品的数量

在这里插入图片描述

9、订单功能

9.1 查询用户订单状态数量

在这里插入图片描述
在这里插入图片描述

9.2 查询用户收货地址列表

在这里插入图片描述
在这里插入图片描述

9.3 新增用户收货地址

在这里插入图片描述
在这里插入图片描述

9.4 查询收货地址详情

在这里插入图片描述

9.5 修改收货地址信息

在这里插入图片描述
在这里插入图片描述

9.6 删除用户收货地址

在这里插入图片描述
在这里插入图片描述

9.7 设置默认收货地址

在这里插入图片描述

在这里插入图片描述

9.8 查询用户购物车中商品的数量

在这里插入图片描述
在这里插入图片描述

10、小程序补充

10.1 查询小程序轮播图列表

在这里插入图片描述
在这里插入图片描述

10.2 查询小程序置顶公告列表

在这里插入图片描述
在这里插入图片描述

10.3 查询小程序所有公告列表

在这里插入图片描述
在这里插入图片描述

10.4 查询公告详情

在这里插入图片描述

10.5 查询小程序商品分组标签集合

在这里插入图片描述
在这里插入图片描述

11、搜索功能

11.1 根据商品分组标签查询商品列表

在这里插入图片描述
在这里插入图片描述

11.2 根据类目父节点查询子节点集合

在这里插入图片描述

11.3 查询当前类目和子类目的所有商品集合

在这里插入图片描述
在这里插入图片描述

11.4 查询商品详情(包含商品sku集合)

在这里插入图片描述
在这里插入图片描述

11.6 根据产品标识查询商品评论总览信息

在这里插入图片描述
在这里插入图片描述

11.7 根据商品标识分页查询商品评论列表

在这里插入图片描述
在这里插入图片描述

11.8 查询商品是否被用户收藏

在这里插入图片描述

11.9 添加或取消用户收藏商品

在这里插入图片描述
在这里插入图片描述

11.10 查询用户收藏商品列表

在这里插入图片描述
在这里插入图片描述

12、购物车功能

12.1 查询用户购物车商品列表

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

12.2 计算购物车中选中商品的金额

在这里插入图片描述
在这里插入图片描述

12.3 添加商品到购物车或修改购物车中商品数量

在这里插入图片描述
在这里插入图片描述

12.4 删除购物车中选中的商品

在这里插入图片描述

13、短信功能

13.1 用户订单确认页面

在这里插入图片描述
在这里插入图片描述

13.2 用户提交订单

在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值