微服务项目-尚上优选

项目介绍:类似于美团优选,多多买菜的功能软件,依托于社区和团长社交关系实现生鲜商品流通的互联网线上线下购物消费行为的新零售模式。

项目介绍:

项目技术架构

核心技术

SpringBoot:简化新Spring应用的初始搭建以及开发过程

SpringCloud:基于Spring Boot实现的云原生应用开发工具,SpringCloud使用的技术:(Spring Cloud Gateway、Spring Cloud OpenFeign、Spring Cloud Alibaba Nacos等)

MyBatis-Plus:持久层框架

Redis:缓存数据库

Redisson:基于redis的Java驻内存数据网格,实现分布式锁

RabbitMQ:消息中间件

ElasticSearch +Kibana: 全文检索服务器 +可视化数据监控

ThreadPoolExecutor:线程池来实现异步操作,提高效率

OSS:文件存储服务

Knife4j(Swagger):Api接口文档工具

Nginx:负载均衡

MySQL:关系型数据库

微信支付

微信小程序

Docker:容器技术

DockerFile:管理Docker镜像命令文本

接口相关工具类

1.MybatisPlus配置类

package com.atguigu.ssyx.common.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@MapperScan("com.atguigu.ssyx.*.mapper")
public class MybatisPlusConfig {

    /**
     * 添加分页插件
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return configuration -> configuration.setUseDeprecatedExecutor(false);
    }
}

2.统一返回结果类

2.1统一返回结果类

package com.atguigu.ssyx.common.result;

import lombok.Data;
//编写统一返回结果类
@Data
public class Result<T> {
    //状态码
    private Integer code;
    //信息
    private  String message;
    //数据
    private T data;
    //构造私有化
    private Result() {
    }
    //设置数据的方法
    public  static <T> Result<T> build(T data,ResultCodeEnum resultCodeEnum) {
       //创建Result对象,设置值,返回对象
        Result<T> result = new Result<>();
        //判断返回结果中是否需要数据
        if (data != null) {
            //设置数据到result对象中
            result.setData(data);
        }
        //设置其他值
        result.setCode(resultCodeEnum.getCode());
        result.setMessage(resultCodeEnum.getMessage());
        //返回值之后的对象
        return result;
    }
    //成功的方法
    public static <T> Result<T> ok(T data) {
        Result<T> result = build(data,ResultCodeEnum.SUCCESS);
        return result;
    }
    //失败的方法
    public static <T> Result<T> fail(T data) {
        return build(data,ResultCodeEnum.FAIL);
   }
}

2.2统一返回结果状态信息类

package com.atguigu.ssyx.common.result;

import lombok.Getter;
/**
 * 统一返回结果状态信息类
 *
 */
@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;
    }
}

3.统一异常处理类

3.1全局异常处理类

package com.atguigu.ssyx.common.exception;

import com.atguigu.ssyx.common.result.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

//AOP面向切面
@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(Exception.class)//异常处理器
    @ResponseBody//把对象转成json数据进行返回
    public Result error(Exception e){
        e.printStackTrace();
        return Result.fail(null);
    }
    //自定义异常处理
    @ExceptionHandler(SsyxException.class)
    @ResponseBody
    public Result error(SsyxException exception){
        return Result.fail(null);
    }
}

3.2自定义异常处理类


package com.atguigu.ssyx.common.exception;

import com.atguigu.ssyx.common.result.ResultCodeEnum;
import lombok.Data;

@Data//创建异常类
public class SsyxException extends RuntimeException{
    //异常状态码
    private Integer code;
    //通过状态码和错误消息创建异常对象@param message和@param code
    public SsyxException(String message, Integer code) {
        super(message);//调用父类异常信息
        this.code=code;
    }
    //接收枚举类型对象@param resultCodeEnum
    public SsyxException(ResultCodeEnum resultCodeEnum) {
        super(resultCodeEnum.getMessage());
        this.code=resultCodeEnum.getCode();
    }
}

4.编写Swagger配置类


package com.atguigu.ssyx.common.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.ParameterBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.schema.ModelRef;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.service.Parameter;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc;
import java.util.ArrayList;
import java.util.List;

/**
 * Swagger2配置信息
 */
@Configuration//创建Swagger2配置信息
@EnableSwagger2WebMvc//开启Swagger2
public class Swagger2Config {

    @Bean
    public Docket webApiConfig(){
        List<Parameter> 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")//参数放在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.atguigu.ssyx"))
                .paths(PathSelectors.regex("/api/.*"))
                .build()
                .globalOperationParameters(pars);
        return webApi;
    }

    @Bean
    public Docket adminApiConfig(){
        List<Parameter> 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.atguigu.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("atguigu", "http://atguigu.com", "atguigu"))
                .build();
    }

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

5.实体类

6.登录接口



package com.atguigu.ssyx.acl.controller;

import com.atguigu.ssyx.common.result.Result;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;
@Api(tags = "登录接口")
@RestController//创建控制器
@RequestMapping("/admin/acl/index")//路径,要和浏览器输入的路径一致
@CrossOrigin//允许跨域
public class IndexController {
// /admin/acl/index/login
//1 login登录
@ApiOperation("登录")
@PostMapping("login")
public Result login(){//模拟登录,使用Result返回统一格式数据
//返回token
Map<String,String> map = new HashMap<>();
map.put("token","admin-token");
return Result.ok(map);
}
//2 getInfo 获取信息
@ApiOperation("获取信息")
@GetMapping("info")
public Result info(){
Map<String,String> map = new HashMap<>();
map.put("name","admin");
map.put("avatar","https://bcn.135editor.com/files/images/editor_styles/d024d9edbbfe7dc1e90dd6084b30c9e2.gif");//头像
return Result.ok(map);
}
//3 logout 登出
//url: ‘/admin/acl/index/logout’,method: ‘post’
@ApiOperation("退出")
@PostMapping("logout")
public Result logout(){
return Result.ok(null);
}
}

7.权限管理模块

7.1角色管理

package com.atguigu.ssyx.acl.service.impl;
import com.atguigu.ssyx.acl.mapper.RoleMapper;
import com.atguigu.ssyx.acl.service.RoleService;
import com.atguigu.ssyx.model.acl.Role;
import com.atguigu.ssyx.vo.acl.RoleQueryVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;


@Service
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {
    @Autowired
    private RoleMapper roleMapper;
    //1.角色列表(条件查询分页)
    @Override
    public IPage<Role> selectRolePage(Page<Role> pageParam, RoleQueryVo roleQueryVo) {
        //获取条件
        String roleName = roleQueryVo.getRoleName();
        //创建mp条件对象
        LambdaQueryWrapper<Role> wrapper = new LambdaQueryWrapper<>();
        //判断条件值是否为空,不为封装查询条件
        //roleName like?
        if (!StringUtils.isEmpty(roleName)){
            wrapper.like(Role::getRoleName,roleName);
        }
        //调用方法实现条件分页查询
        IPage<Role> rolePage = baseMapper.selectPage(pageParam, wrapper);
        //返回分页对象
        return rolePage;
    }
}

调试

package com.atguigu.ssyx.acl.controller;

import com.atguigu.ssyx.acl.service.RoleService;
import com.atguigu.ssyx.common.result.Result;
import com.atguigu.ssyx.model.acl.Role;
import com.atguigu.ssyx.vo.acl.RoleQueryVo;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Api(tags = "角色接口")
@RestController
@RequestMapping("/admin/acl/role")
@CrossOrigin //跨域
public class RoleController {
    //注入service
    @Autowired
    private RoleService roleService;
    //1角色列表(条件分页查询)
    @ApiOperation("角色条件分页查询")
    @GetMapping("{current}/{limit}")
    public Result pageList(@PathVariable Long current, @PathVariable Long limit
    , RoleQueryVo roleQueryVo){
        //1创建Page对象,传递当前页(current),每页记录数(limit)
        Page<Role> pageParam = new Page<>(current,limit);
        ///2调用service方法实现分页查询,返回分页对象
        IPage<Role> pageModel = roleService.selectRolePage(pageParam,roleQueryVo);
        return Result.ok(pageModel);
    }
    //2.根据id查询角色
    @ApiOperation("根据id查询角色")
    @GetMapping("get/{id}")
    public Result get(@PathVariable Long id){
        Role role = roleService.getById(id);
        return Result.ok(role);
    }
    //3.添加角色
    @PostMapping("save")
    public Result save(@RequestBody Role role){//接收参数
        boolean is_success = roleService.save(role);
        if(is_success){
            return Result.ok(null);
        }else{
            return Result.fail(null);
        }
    }
    //4.修改角色
    @ApiOperation("修改角色")
    @PutMapping("update")
    public Result update(@RequestBody Role role){
        roleService.updateById(role);
        return Result.ok(null);
    }
    //5.根据id删除角色
    @ApiOperation("根据id删除角色")
    @DeleteMapping("remove/{id}")
    public Result remove(@PathVariable Long id){
        roleService.removeById(id);
        return Result.ok(null);
    }
    //6.批量删除角色
    //json数组[1,2,3]----对应java的List集合
    @ApiOperation("批量删除角色")
    @DeleteMapping("batchRemove")
    public Result batchRemove(@RequestBody List<Long> idList){
        roleService.removeByIds(idList);
        return Result.ok(null);
    }
}

用户接口

package com.atguigu.ssyx.acl.controller;

import com.atguigu.ssyx.acl.service.AdminService;
import com.atguigu.ssyx.common.result.Result;
import com.atguigu.ssyx.model.acl.Admin;
import com.atguigu.ssyx.vo.acl.AdminQueryVo;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@Api(tags = "用户接口")
@RestController
@RequestMapping("/admin/acl/user")
@CrossOrigin
public class AdminController {
    @Autowired
    private AdminService adminService;
    //1.用户列表
    @ApiOperation("用户列表")
    @GetMapping("{current}/{limit}")
    public Result list(@PathVariable Long current,
                       @PathVariable Long limit,
                       AdminQueryVo adminQueryVo) {
        Page<Admin> pageParam=new Page<Admin>(current,limit);//创建page对象,传递当前页,每页记录数
        IPage<Admin> pageModel = adminService.selectPageUser(pageParam,adminQueryVo);//调用service方法实现分页查询,返回分页对象
        return Result.ok(pageModel);
    }
    //2.id查询用户
    @ApiOperation("id查询用户")
    @GetMapping("get/{id}")
    public Result get(@PathVariable Long id) {
        Admin admin = adminService.getById(id);
        return Result.ok(admin);
    }
    //3.添加用户
    @ApiOperation("添加用户")
    @PostMapping("save")
    public Result save(@RequestBody Admin admin) {
        boolean save=adminService.save(admin);
        return Result.ok(null);
    }
    //4.修改用户
    @ApiOperation("修改用户")
    @PutMapping("update")
    public Result update(@RequestBody Admin admin) {
        adminService.updateById(admin);
        return Result.ok(null);
    }
    //5.id删除用户
    @ApiOperation("id删除用户")
    @DeleteMapping("remove/{id}")
    public Result remove(@PathVariable Long id) {
        adminService.removeById(id);
        return Result.ok(null);
    }
    //6.批量删除用户
    @ApiOperation("batchRemove")
    public Result batchRemove(@RequestBody List<Long> idList) {
        adminService.removeByIds(idList);
        return Result.ok(null);
    }
}