国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【瑞吉外賣】適合速成SpringBoot和MyBatis的作業(yè)項目

這篇具有很好參考價值的文章主要介紹了【瑞吉外賣】適合速成SpringBoot和MyBatis的作業(yè)項目。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。


目標(biāo)很明確,快速掌握最最基礎(chǔ)的SpringBoot + MyBatis-Plus怎么用,兩天趕著把項目做了一大半,但過程里缺乏一些思考和總結(jié),現(xiàn)在來復(fù)盤一下。僅列出覺得有價值的部分。

還是很適合作為上手項目,業(yè)務(wù)邏輯確實比較簡單,主要是要掌握一整套流程,以及涉及到多個表的連接查詢操作、一個表的分頁查詢應(yīng)該如何處理,以及文件的上傳下載、手機短信發(fā)送驗證碼知識。

但這樣的項目,如果不主動思考,能得到的東西就很少了,因為它開發(fā)的流程已經(jīng)給了一個答案,雖然未必是標(biāo)準(zhǔn)答案,但是直接照著抄、不考慮應(yīng)該怎么實現(xiàn),可能除了查表更熟練以外能收獲的技能不多。不過查表更熟練也算小提升吧。

以及覺得如果有個ER圖 / 接口說明的話,會清晰很多,不用這樣對著前端分析傳過來什么,應(yīng)該傳回去什么。

MyBatis Plus確實方便了很多,這個項目從頭到尾沒寫過<if> <foreach> <set> <where>,方便得讓人不安,牛的。

自己一個人git還是缺少鍛煉,體會不到那種pull下來發(fā)現(xiàn)有沖突,需要merge的絕望。

【瑞吉外賣】適合速成SpringBoot和MyBatis的作業(yè)項目

【瑞吉外賣】適合速成SpringBoot和MyBatis的作業(yè)項目

下一步速成redis和微服務(wù),主要還是學(xué)學(xué)各種中間件怎么使。然后找個能拿得出手的項目。

零、MyBatisPlus

極大簡化CRUD代碼。

  • 基本上是傻瓜式操作,因為幾乎不用記對應(yīng)的SQL查詢要怎么寫,戳一個.就能得到一波hint和提示補全。
  • 提供分頁插件。
  • 提供全局?jǐn)r截規(guī)則,設(shè)置@TableField及對應(yīng)的MetaObjectHandler就可以對字段進(jìn)行填充。

【瑞吉外賣】適合速成SpringBoot和MyBatis的作業(yè)項目

一、管理端登錄

1.0 統(tǒng)一的返回結(jié)果Result類

還是有必要的,之前寫前端的時候很需要這個code和msg讓我知道這個接口我是調(diào)成功了還是失敗了,調(diào)失敗了的話問題在哪。

@Data
public class Result<T> {
    /**
     *  code - 編碼:1成功,0和其它數(shù)字為失敗
     *  msg - 錯誤信息
     *  data - 數(shù)據(jù)
     *  map - 動態(tài)數(shù)據(jù)
     */
    private Integer code;
    private String msg;
    private T data;
    private Map map = new HashMap();

    public static <T> Result<T> success(T object) {
        Result<T> result = new Result<T>();
        result.data = object;
        result.code = 1;
        return result;
    }

    public static <T> Result<T> error(String msg) {
        Result result = new Result();
        result.msg = msg;
        result.code = 0;
        return result;
    }

    public Result<T> add(String key, Object value) {
        this.map.put(key, value);
        return this;
    }

}

1.1 admin/login

說明:這一部分是好久好久好久以前寫的,改了改前端和接口,但邏輯是一樣的。

客戶端請求(TODO: 前端裸傳密碼還是有一點怪怪……有時間了解一下現(xiàn)實世界的實現(xiàn)):

POST
/admin/login
參數(shù):
{
  "name": "扣扣",
  "password": "koukou123456"
}

管理員實體:

@Data
public class Admin {
    private Long adminId;
    private String password;
    private String phoneNumber;
    private String name;
}

邏輯:

  1. 將參數(shù)password進(jìn)行MD5加密
import org.springframework.util.DigestUtils;
password = DigestUtils.md5DigestAsHex(password.getBytes());
  1. 判斷數(shù)據(jù)庫中是否存在該對象,與數(shù)據(jù)庫中取到的密碼是否一致

  2. 登錄成功時,將管理員id存入當(dāng)前session,作為本次會話的一個屬性。

request.getSession().setAttribute("admin", adm.getAdminId());

AdminController代碼:

	/**
     * 密碼md5加密 + 根據(jù)name查詢數(shù)據(jù)庫 + 比對密碼
     * @param request 該參數(shù)為了將該admin對象的id存入當(dāng)前session中
     * @param admin 封裝好的Admin Bean參數(shù)
     * @return
     */
    @PostMapping("/login")
    public Result<Admin> login(HttpServletRequest request, @RequestBody Admin admin) {

        // 1 將頁面提交的密碼進(jìn)行md5加密處理
        String password = admin.getPassword();
        password = DigestUtils.md5DigestAsHex(password.getBytes());

        // 2 根據(jù)頁面提交的用戶名username查詢數(shù)據(jù)庫
        LambdaQueryWrapper<Admin> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Admin::getName, admin.getName());
        Admin adm = adminService.getOne(queryWrapper);

        // 3、無結(jié)果返回登陸失敗
        if (adm == null) {
            return Result.error("用戶名錯誤,登錄失敗");
        }

        // 4、比對密碼
        if (!adm.getPassword().equals(password)) {
            return Result.error("密碼錯誤,登錄失敗");
        }

        // 5、登錄成功,將管理員id存入Session并返回登錄成功結(jié)果
        request.getSession().setAttribute("admin", adm.getAdminId());
        return Result.success(adm);
    }

1.2 admin/logout

把當(dāng)前管理員的id移出session

	@PostMapping("/logout")
    public Result<String> login(HttpServletRequest request) {
        request.getSession().removeAttribute("admin");
        return Result.success("退出成功");
    }

1.3 Filter

Servelet中的Filter接口。需要加入@WebFilter注解聲明攔截路徑,并在啟動類加入@ServletComponentScan注解,使得這個Filter可以被Scan到。

一些頁面 / 接口需要在訪問前判斷當(dāng)前是否為登錄狀態(tài),所以設(shè)置這個Filter。

核心邏輯為判斷當(dāng)前訪問的Url以及從Session中取出id。

/**
 * 檢查是否登錄
 */
@WebFilter(urlPatterns = "/*")
@Slf4j
public class LoginCheckFilter implements Filter {
    /**
     * 路徑匹配器,用于檢查該路徑是否需要攔截
     */
    private static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
       
    }


    /**
    *   判斷requestUrl是否在urls中
    */
    public boolean canPass(String[] urls, String requestURI) {
        for (String url: urls) {
            if (PATH_MATCHER.match(url, requestURI)) {
                return true;
            }
        }
        return false;
    }
}

核心為doFilter方法,邏輯如下:

  • 定義可放行請求路徑集合,判斷request的Url是否在集合中,如果在集合中,可以直接放行;

  • 嘗試從session中得到login時存入的屬性(可能是管理員login,也可能是用戶login)

    req.getSession().getAttribute("admin");
    
  • 如果返回值不為空,說明已經(jīng)登錄,可以放行

  • 否則需要response拒絕請求:

    Result<String> error = Result.error("對不起,您尚未登錄,請先進(jìn)行登錄操作!");
    resp.getWriter().write(JSONObject.toJSONString(error));
    return;
    

完整代碼如下:

 	@Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
      
		HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;

        // 可放行集合
        String[] canPassUrls = {
                "/admin/login",
                "/admin/logout",
                // 靜態(tài)資源路徑就不處理了
                "/backend/**",
                "/front/**",
                // 一些其他請求,發(fā)送短信、移動端登錄
                "/common/**",
                "/user/sendMsg",
                "/user/login"
        };

        // 1、得到URI
        String requestURI = req.getRequestURI();
        log.info("攔截到請求: {}", requestURI);

        // 2、得到登錄狀態(tài)
        Object adminLoginId = req.getSession().getAttribute("admin");
        Object userLoginId = req.getSession().getAttribute("user");

        // 3、如果未登錄且是不可訪問頁面,拒絕請求
        if (!canPass(canPassUrls, requestURI) && adminLoginId == null && userLoginId == null) {
            Result<String> error = Result.error("對不起,您尚未登錄,請先進(jìn)行登錄操作!");
            resp.getWriter().write(JSONObject.toJSONString(error));
            return;
        }

        if (adminLoginId != null) {
            BaseContext.setCurrentId((Long)adminLoginId);
        }

        if (userLoginId != null) {
            BaseContext.setCurrentId((Long)userLoginId);
        }

        filterChain.doFilter(servletRequest, servletResponse);
   }

1.4 自定義消息轉(zhuǎn)換器

這部分只是意會了,讓我自己寫可能還是不會。

long轉(zhuǎn)為js會精度丟失,那么我們就對數(shù)據(jù)進(jìn)行轉(zhuǎn)型,響應(yīng)json時進(jìn)行處理,將long轉(zhuǎn)為字符串。

并且轉(zhuǎn)換時間格式。

還是有點AOP的。

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import java.math.BigInteger;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import static com.fasterxml.jackson.databind.DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES;

/**
 * 對象映射器:基于jackson將Java對象轉(zhuǎn)為json,或者將json轉(zhuǎn)為Java對象
 * 將JSON解析為Java對象的過程稱為 [從JSON反序列化Java對象]
 * 從Java對象生成JSON的過程稱為 [序列化Java對象到JSON]
 */
public class JacksonObjectMapper extends ObjectMapper {

    public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
    public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
    public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";

    public JacksonObjectMapper() {
        super();
        //收到未知屬性時不報異常
        this.configure(FAIL_ON_UNKNOWN_PROPERTIES, false);

        //反序列化時,屬性不存在的兼容處理
        this.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);


        SimpleModule simpleModule = new SimpleModule()
                .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)))

                .addSerializer(BigInteger.class, ToStringSerializer.instance)
                .addSerializer(Long.class, ToStringSerializer.instance)
                .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_TIME_FORMAT)))
                .addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern(DEFAULT_DATE_FORMAT)))
                .addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern(DEFAULT_TIME_FORMAT)));

        //注冊功能模塊 例如,可以添加自定義序列化器和反序列化器
        this.registerModule(simpleModule);
    }
}

WebMVCConfig中需要進(jìn)行相依ing的設(shè)置。

import com.beautysalon.common.JacksonObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

import java.util.List;

@Slf4j
@Configuration
public class WebMVCConfig extends WebMvcConfigurationSupport {
    /**
     * 設(shè)置靜態(tài)資源映射
     * @param registry
     */
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/backend/**").addResourceLocations("classpath:/backend/");
        registry.addResourceHandler("/front/**").addResourceLocations("classpath:/front/");
    }

    @Override
    protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
        log.info("擴展消息轉(zhuǎn)換器");
        MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
        messageConverter.setObjectMapper(new JacksonObjectMapper());
        converters.add(0, messageConverter);
    }
}

二、員工管理

2.1 新增員工-字段填充

可以統(tǒng)一處理的變量可以使用注解@TableField,然后再定義一個Handler實現(xiàn)填充方法。

@Slf4j
@Data
public class Employee {
    private Long id;
    private String name;
    private String username;
    private String password;
    private String phone;
    private String sex;
    private String idNumberReal;
    private Integer status;

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @TableField(fill = FieldFill.INSERT)
    private Long createByAdmin;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateByAdmin;
}

實現(xiàn)MetaObjectHandler接口和insertFill、upDateFill方法。

可以使用hasSetter判斷是否具有某個屬性。

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;

@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("公共字段自動填充[insert]");
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime", LocalDateTime.now());
        if (metaObject.hasSetter("createByAdmin")) {
            metaObject.setValue("createByAdmin", BaseContext.getCurrentId());
            metaObject.setValue("updateByAdmin", BaseContext.getCurrentId());
        }
        if (metaObject.hasSetter("createUser")) {
            metaObject.setValue("createUser", BaseContext.getCurrentId());
            metaObject.setValue("updateUser", BaseContext.getCurrentId());
        }

    }

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自動填充[update]");
        metaObject.setValue("updateTime", LocalDateTime.now());
        if (metaObject.hasSetter("updateByAdmin")) {
            metaObject.setValue("updateByAdmin", BaseContext.getCurrentId());
        }
        if (metaObject.hasSetter("updateUser")) {
            metaObject.setValue("updateUser", BaseContext.getCurrentId());
        }
    }
}

BaseContext如下,在login時設(shè)置了BaseContext相關(guān)屬性,需要填充時再get,因為是靜態(tài)方法,所以不需要注入:

/**
 * 基于ThreadLocal封裝工具類
 */
@Component
public class BaseContext {
    private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();

    public static void setCurrentId(Long id) {
        threadLocal.set(id);
    }

    public static Long getCurrentId() {
        return threadLocal.get();
    }
}

2.2 全局異常捕獲

使用@ControllerAdvice@ExceptionHandler注解,@ExceptionHandler指明了捕獲什么樣的異常。

/**
 * 全局異常處理
 */
@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 異常處理方法
     * @return
     */
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public Result<String> exceptionHandler(SQLIntegrityConstraintViolationException ex){
        log.error(ex.getMessage());

        if(ex.getMessage().contains("Duplicate entry")){
            String[] split = ex.getMessage().split(" ");
            String msg = split[2] + "已存在";
            return Result.error(msg);
        }

        return Result.error("未知錯誤");
    }

    /**
     * 異常處理方法
     * @return
     */
    @ExceptionHandler(CustomException.class)
    public Result<String> exceptionHandler(CustomException ex){
        log.error(ex.getMessage());
        return Result.error(ex.getMessage());
    }

}

2.3 員工信息分頁查詢

需要配置MyBatis提供的分頁插件攔截器:

import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class MyBatisPlusConfig {

    /**
     * 分頁插件
     * @return
     */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

使用MyBatis-PlusPage進(jìn)行分頁:

	@GetMapping("/page")
    public Result<Page<Employee>> page(@RequestParam Integer page,
                                       @RequestParam Integer pageSize,
                                       @RequestParam(required = false) String name) {
        log.info("員工分頁信息查詢:{}, {}", page, pageSize);

        // 配置分頁構(gòu)造器
        Page<Employee> pageInfo = new Page<>(page, pageSize);

        // 條件構(gòu)造器
        LambdaQueryWrapper<Employee> queryWrapper = new LambdaQueryWrapper<>();
        // 添加過濾條件,如果name不為空,加入name=#{name}條件
        queryWrapper.like(!StringUtils.isEmpty(name), Employee::getName, name);
        // 添加排序條件
        queryWrapper.orderByDesc(Employee::getUpdateTime);

        // 執(zhí)行查詢
        employeeService.page(pageInfo, queryWrapper);
        return Result.success(pageInfo);
    }

三、分類管理

3.1 分類的刪除

刪除前需要先去dish表和setmeal表查看有無菜品。操作涉及到3個表:

  • dish表是否有元素categoryId為當(dāng)前分類
  • setmeal表是否有元素categoryId為當(dāng)前分類
  • category表刪除該分類

【瑞吉外賣】適合速成SpringBoot和MyBatis的作業(yè)項目

四、菜品管理

4.1 文件的上傳與下載

上傳:保存到本地指定位置

下載:作為Response吐給瀏覽器顯示

1 上傳

在屬性的yml文件中定義相關(guān)路徑位置:

koukou:
  path: E:\\leetcode\\project_pre\\BeautySalon\\src\\main\\resources\\front\\upload\\

使用${}指定圖片保存路徑

	@Value("${koukou.path}")
    private String basePath;

    @PostMapping("/upload")
    public Result<String> upload(MultipartFile file) {
        
        // 提取文件相關(guān)信息
        String filename = file.getOriginalFilename();
        int index = filename.lastIndexOf('.');
        String ext = filename.substring(index);

        // UUID賦予新名稱
        String newName = UUID.randomUUID().toString();
        String path = basePath + newName + ext;
        log.info(path);

        // 保存文件
        try {
            file.transferTo(new File(path));
        }
        catch (IOException e) {
            e.printStackTrace();
        }

        return Result.success(newName + ext);
    }
2 下載
	/**
     * 讓本地的圖片在瀏覽器上顯示,寫入Response的輸出流
     * @param name
     * @param response
     */
    @GetMapping("/download")
    public void download(String name, HttpServletResponse response) {
        try {
            // 輸入流
            FileInputStream fileInputStream = new FileInputStream(new File(basePath + name));
            // 輸出流
            ServletOutputStream outputStream = response.getOutputStream();
            // 設(shè)置response的content類型
            response.setContentType("image/jpeg");

            int len = 0;
            byte[] bytes = new byte[1024];
            while ((len = fileInputStream.read(bytes)) != -1) {
                outputStream.write(bytes, 0, len);
                outputStream.flush();
            }
            outputStream.close();
            fileInputStream.close();
        }
        catch (Exception e) {
            e.printStackTrace();
        }
    }

4.2 新增菜品

@Transactional(rollbackFor = Exception.class)開啟事務(wù),并在啟動類上加上@EnableTransactionManagement.

設(shè)計到三個表:

  1. 菜品的分類:因為前端在新增菜品時,需要選擇菜品分類,因此需要返回菜品的所有可能分類取值。
  2. dish表,表示菜品
  3. dish_flavor表,表示菜品的口味,由于是一對多關(guān)系,該表存儲了dish的主碼id

【瑞吉外賣】適合速成SpringBoot和MyBatis的作業(yè)項目

1、查詢所有可能的菜品分類,使用一個category來接收參數(shù),解釋是這樣以后需求增加時(比如按其它屬性search)不必重構(gòu)這個方法

	@GetMapping("/list")
    public Result<List<Category>> list(Category category) {
        log.info("根據(jù)條件查詢分類數(shù)據(jù)");

        LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(category.getType() != null, Category::getType, category.getType());
        queryWrapper.orderByAsc(Category::getSort).orderByDesc(Category::getUpdateTime);

        List<Category> list = categoryService.list(queryWrapper);

        return Result.success(list);
    }

2、這個add請求由于攜帶了額外的信息,用一個DishDTO接?。?/p>

	@PostMapping
    public Result<String> save(@RequestBody DishDto dishDto) {
        dishService.saveWithFlavor(dishDto);
        return Result.success("成功保存菜品");
    }

DishDto繼承了Dish類,包含Dish的所有屬性,但增加了flavors的擴展。

categoryName我覺得是想說明怎么實現(xiàn)兩個表的連接,把categoryId轉(zhuǎn)為categoryName。

/**
 * DTO:Data Transfer Object,用于傳輸數(shù)據(jù), 對dish的擴展
 */
@Data
public class DishDto extends Dish {

    private List<DishFlavor> flavors = new ArrayList<>();
    private String categoryName;
    private Integer copies;
}

  • 先將dishDto存入Dish表
  • 然后設(shè)置每個Flavor的dishId,并存進(jìn)Flavor表。

【瑞吉外賣】適合速成SpringBoot和MyBatis的作業(yè)項目

4.3 修改菜品

修改菜品的邏輯比較類似,但首先需要先把這個菜品的信息查詢出來,放進(jìn)DishDto里傳給前端,前端顯示這個菜品。

使用到了BeanUtils.copyProperties進(jìn)行兩個對象間的復(fù)制。

【瑞吉外賣】適合速成SpringBoot和MyBatis的作業(yè)項目

然后前端進(jìn)行修改,然后再傳回后端,后端進(jìn)行修改。類似地,先update這個dish,然后再update這個菜品對應(yīng)的口味。

【瑞吉外賣】適合速成SpringBoot和MyBatis的作業(yè)項目

4.4 菜品信息分頁查詢

類似地,需要查找菜品及其對應(yīng)的口味,并將categoryId轉(zhuǎn)為name,同樣用到了BeanUtils進(jìn)行Page之間的復(fù)制。

  • 查找滿足條件的分頁數(shù)據(jù) Page<Dish>,賦值給 Page<DishDto>
  • 查找所有dish的口味和種類,賦值給DishDto,加入列表。
	@GetMapping("/page")
    public Result<Page> page(int page, int pageSize, String name) {

        // 先把分頁數(shù)據(jù)查出來
        Page<Dish> pageInfo = new Page<>(page, pageSize);

        LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(name != null, Dish::getName, name);
        queryWrapper.orderByDesc(Dish::getUpdateTime);

        dishService.page(pageInfo, queryWrapper);

        // 和另一個flavor表綜合
        Page<DishDto> dishDtoPage = new Page<>();
        // 把查詢出來的數(shù)據(jù)拷貝到新對象
        BeanUtils.copyProperties(pageInfo, dishDtoPage, "records");
        
        // 處理records
        List<Dish> dishes = pageInfo.getRecords();
        List<DishDto> dishDtos = new ArrayList<>();
        
        for (Dish dish: dishes) {
            DishDto dishDto = new DishDto();
            // 把dish拷貝到新對象
            BeanUtils.copyProperties(dish, dishDto);
            Long categoryId = dish.getCategoryId();
            String categoryName = categoryService.getById(categoryId).getName();
            dishDto.setCategoryName(categoryName);
            dishDtos.add(dishDto);
        }

        // 賦值
        dishDtoPage.setRecords(dishDtos);
        return Result.success(dishDtoPage);
    }

五、套餐管理

5.1 添加套餐

和新增菜品的邏輯很類似,涉及到setmeal和setmealdish兩張表,setmeal保存套餐信息,setmealdish記錄菜品與套餐間的關(guān)系。

  • 先保存套餐信息
  • 然后設(shè)置 套餐菜品關(guān)系 的套餐id,存入表

【瑞吉外賣】適合速成SpringBoot和MyBatis的作業(yè)項目

5.2 批量刪除套餐

需要先批量刪除setmeal套餐表,然后用.in判斷菜品套餐關(guān)系表,刪除SetmealDish表中含該套餐id的項。

【瑞吉外賣】適合速成SpringBoot和MyBatis的作業(yè)項目

5.3 套餐信息分頁查詢

與菜品信息分頁查詢類似:

  • Setmeal和SetmealDto之間的BeanUtils.copyProperties
  • 以及兩個Page之間的BeanUtils.copyProperties
    @GetMapping("/page")
    public Result<Page> page(int page, int pageSize, String name) {

        Page<Setmeal> pageInfo = new Page<>(page, pageSize);
        // 需要返回的數(shù)據(jù)類型
        Page<SetmealDto> dtoPage = new Page<>();

        // 先把這一頁的信息查出來
        LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.like(name != null, Setmeal::getName, name);
        setmealService.page(pageInfo, queryWrapper);

        List<Setmeal> setmeals = pageInfo.getRecords();
        List<SetmealDto> setmealDtos = new ArrayList<>();

        BeanUtils.copyProperties(pageInfo, dtoPage, "records");
        // 將id轉(zhuǎn)換為name
        for (Setmeal setmeal: setmeals) {
            String categoryName = categoryService.getById(setmeal.getCategoryId()).getName();
            SetmealDto setmealDto = new SetmealDto();
            BeanUtils.copyProperties(setmeal, setmealDto);
            setmealDto.setCategoryName(categoryName);
            setmealDtos.add(setmealDto);
        }

        dtoPage.setRecords(setmealDtos);
        return Result.success(dtoPage);
    }

六、用戶相關(guān)

6.1 發(fā)送驗證碼

生成4位驗證碼:

public class ValidateCodeUtils {

    public static String generateValidateCode4String(int length){
        Random rdm = new Random();
        String hash1 = Integer.toHexString(rdm.nextInt());
        String capstr = hash1.substring(0, length);
        return capstr;
    }
}

發(fā)送短信,即調(diào)用API發(fā)請求的過程:

import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;

/**
 * 短信發(fā)送工具類
 */
public class SMSUtils {
	private static final String SIGN_NAME = "小扣外賣";
	private static final String TEMPLATE_CODE = "SM1";

	/**
	 * 發(fā)送短信
	 * @param phoneNumbers 手機號
	 * @param param 參數(shù)
	 */
	public static void sendMessage(String phoneNumbers, String param){
		DefaultProfile profile = DefaultProfile.getProfile(
				"cn-hangzhou",
				"key",
				"private key"
		);
		IAcsClient client = new DefaultAcsClient(profile);

		SendSmsRequest request = new SendSmsRequest();
		request.setSysRegionId("cn-hangzhou");
		request.setPhoneNumbers(phoneNumbers);
		request.setSignName(SIGN_NAME);
		request.setTemplateCode(TEMPLATE_CODE);
		request.setTemplateParam("{\"code\":\"" + param + "\"}");
		try {
			SendSmsResponse response = client.getAcsResponse(request);
			System.out.println("短信發(fā)送成功");
		}catch (ClientException e) {
			e.printStackTrace();
		}
	}

}

controller需要調(diào)用工具類發(fā)送短信,并將驗證碼存入Session:

	@PostMapping("/sendMsg")
    public Result<String> sendMsg(@RequestBody User user, HttpSession session) {
        String code = ValidateCodeUtils.generateValidateCode4String(4);
        SMSUtils.sendMessage(user.getPhone(), code);
        log.info("發(fā)送驗證碼:{}", code);
        
        // 將驗證碼保存到Session
        session.setAttribute(user.getPhone(), code);
        return Result.success("短信發(fā)送成功,驗證碼為" + code);
    }

6.2 登錄

  • 將用戶發(fā)來的驗證碼,與session中存起來的驗證碼進(jìn)行比較
    • 不同,登錄失敗
    • 相同,用戶表中是否有該user,如果是新用戶,加入user表里
      • 將id存入session,以便CheckLoginFilter能夠取到
      • 如果仔細(xì)觀察你會發(fā)現(xiàn)userService.save(user)以后用戶自動擁有了一個id。
@PostMapping("/login")
    public Result<User> login(@RequestBody Map<String, String> map, HttpSession session) {
        // 獲取手機號、驗證碼進(jìn)行比對
        String phone = map.get("phone");
        String code = map.get("code");
        String sessionCode = (String) session.getAttribute(phone);

        // 比對成功,登錄成功
        if (code != null && code.equals(sessionCode)) {
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
            queryWrapper.eq(User::getPhone, phone);
            User user = userService.getOne(queryWrapper);
            // 如果當(dāng)前用戶是新用戶,加入user表中
            if (user == null) {
                user = new User();
                user.setPhone(phone);
                user.setStatus(1);
                userService.save(user);
            }
            session.setAttribute("user", user.getId());
            log.info("用戶登錄成功,{}", user.getId());
            return Result.success(user);
        }

        // 比對失敗
        return Result.error("登錄失敗");
    }

七、購物車

7.1 添加菜品和套餐

購物車表:

【瑞吉外賣】適合速成SpringBoot和MyBatis的作業(yè)項目

  • 判斷是菜品還是套餐
  • 每個用戶對應(yīng)一個購物車id,查看該用戶的購物車中是否存在該item
  • 存在,count + 1,更新;不存在,count=1,寫入。
@PostMapping("/add")
public Result<ShoppingCart> add(@RequestBody ShoppingCart shoppingCart){
    log.info("購物車數(shù)據(jù):{}",shoppingCart);

    // 先設(shè)置相應(yīng)屬性,然后看看這道菜購物車?yán)镉袥]有,如果沒有,加入表;如果有,number+1
    shoppingCart.setUserId(BaseContext.getCurrentId());

    // 查看當(dāng)前菜品 或 套餐是否在購物車中
    LambdaQueryWrapper<ShoppingCart> queryWrapper = new LambdaQueryWrapper();
    queryWrapper.eq(ShoppingCart::getUserId, shoppingCart.getUserId());
    
    if (shoppingCart.getDishId() != null) {
        queryWrapper.eq(ShoppingCart::getDishId, shoppingCart.getDishId());
    }
    if (shoppingCart.getSetmealId() != null) {
        queryWrapper.eq(ShoppingCart::getSetmealId, shoppingCart.getSetmealId());
    }
    ShoppingCart target = shoppingCartService.getOne(queryWrapper);

    if (target != null) {
        // 在購物車?yán)?,?shù)量加一
        target.setNumber(target.getNumber() + 1);
        shoppingCartService.updateById(target);
    }
    else {
        shoppingCart.setNumber(1);
        shoppingCartService.save(shoppingCart);
        target = shoppingCart;
    }

    return Result.success(target);
}

文件配置

通過配置這里設(shè)置了端口,發(fā)送response的編碼,mybatis plus的名字映射方式,全局id的生成方式,文件上傳路徑。

application.yml

server:
  port: 629
  servlet:
    encoding:
      force: true
      charset: UTF-8

spring:
  application:
    #應(yīng)用的名稱,
    name: 
  datasource:
    druid:
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://localhost:3306/beautysalon?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull&useSSL=false&allowPublicKeyRetrieval=true
      username: root
      password: 

mybatis-plus:
  configuration:
    #在映射實體或者屬性時,將數(shù)據(jù)庫中表名和字段名中的下劃線去掉,按照駝峰命名法映射
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  global-config:
    db-config:
    # 全局id的生成方式
      id-type: ASSIGN_ID

koukou:
  path: E:\\leetcode\\project_pre\\BeautySalon\\src\\main\\resources\\front\\upload\\

pom.xml文章來源地址http://www.zghlxwxcb.cn/news/detail-501057.html

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.2</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>

        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.76</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.1.23</version>
        </dependency>

        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-core</artifactId>
            <version>4.5.16</version>
        </dependency>

        <dependency>
            <groupId>com.aliyun</groupId>
            <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
            <version>2.1.0</version>
        </dependency>

    </dependencies>

到了這里,關(guān)于【瑞吉外賣】適合速成SpringBoot和MyBatis的作業(yè)項目的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進(jìn)行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 【SpringBoot項目實戰(zhàn)+思維導(dǎo)圖】瑞吉外賣①(項目介紹、開發(fā)環(huán)境搭建、后臺登陸/退出功能開發(fā))

    【SpringBoot項目實戰(zhàn)+思維導(dǎo)圖】瑞吉外賣①(項目介紹、開發(fā)環(huán)境搭建、后臺登陸/退出功能開發(fā))

    全文主體框架來源于黑馬瑞吉外賣的項目資料,我在文中會嵌入如下五個方面的個人內(nèi)容: 項目中易發(fā)生錯誤的地方 項目中涉及的一些難理解知識點 一些遺忘知識點的回顧 業(yè)務(wù)的多種實現(xiàn)方法 我在做項目時的思考和一些踩坑 作為一名軟件開發(fā)工程師,我們需要了解在軟件開

    2024年02月05日
    瀏覽(36)
  • 瑞吉外賣項目——瑞吉外賣

    瑞吉外賣項目——瑞吉外賣

    需求分析:產(chǎn)品原型、需求規(guī)格說明書 設(shè)計:產(chǎn)品文檔、UI界面設(shè)計、概要設(shè)計、詳細(xì)設(shè)計、數(shù)據(jù)庫設(shè)計 編碼:項目代碼、單元測試 測試:測試用例、測試報告 上線運維:軟件環(huán)境安裝、配置 項目經(jīng)理:對整個項目負(fù)責(zé),任務(wù)分配、把控進(jìn)度 產(chǎn)品經(jīng)理:進(jìn)行需求調(diào)研,輸

    2023年04月26日
    瀏覽(22)
  • 最適合新手的SpringBoot+SSM項目《蒼穹外賣》實戰(zhàn)—(二)項目概述

    最適合新手的SpringBoot+SSM項目《蒼穹外賣》實戰(zhàn)—(二)項目概述

    黑馬程序員最新Java項目實戰(zhàn)《蒼穹外賣》,最適合新手的SpringBoot+SSM的企業(yè)級Java項目實戰(zhàn)。 《蒼穹外賣》項目的定位是一款為餐飲企業(yè)(餐廳、飯店)定制的軟件產(chǎn)品。該項目是一個在線外賣訂購系統(tǒng),顧客可以通過網(wǎng)站或者手機 App 訂購餐點。該項目可以提供以下的功能:

    2024年02月12日
    瀏覽(19)
  • 最適合新手的SpringBoot+SSM項目《蒼穹外賣》實戰(zhàn)—(一)項目概述

    最適合新手的SpringBoot+SSM項目《蒼穹外賣》實戰(zhàn)—(一)項目概述

    黑馬程序員最新Java項目實戰(zhàn)《蒼穹外賣》,最適合新手的SpringBoot+SSM的企業(yè)級Java項目實戰(zhàn)。 《蒼穹外賣》項目的定位是一款為餐飲企業(yè)(餐廳、飯店)定制的軟件產(chǎn)品。該項目是一個在線外賣訂購系統(tǒng),顧客可以通過網(wǎng)站或者手機 App 訂購餐點。該項目可以提供以下的功能:

    2024年02月15日
    瀏覽(23)
  • 最適合新手的SpringBoot+SSM項目《蒼穹外賣》實戰(zhàn)—(五)員工管理

    最適合新手的SpringBoot+SSM項目《蒼穹外賣》實戰(zhàn)—(五)員工管理

    黑馬程序員最新Java項目實戰(zhàn)《蒼穹外賣》,最適合新手的SpringBoot+SSM的企業(yè)級Java項目實戰(zhàn)。 設(shè)計 DTO 類 我們需要根據(jù)新增員工接口設(shè)計對應(yīng)的 DTO 類去接收前端傳遞的參數(shù),前端傳遞參數(shù)列表如下: 注意: 當(dāng)前端提交的數(shù)據(jù)和實體類中對應(yīng)的屬性差別比較大時,建議使用

    2024年02月15日
    瀏覽(48)
  • 最適合新手的SpringBoot+SSM項目《蒼穹外賣》實戰(zhàn)—(二)開發(fā)環(huán)境搭建

    最適合新手的SpringBoot+SSM項目《蒼穹外賣》實戰(zhàn)—(二)開發(fā)環(huán)境搭建

    黑馬程序員最新Java項目實戰(zhàn)《蒼穹外賣》,最適合新手的SpringBoot+SSM的企業(yè)級Java項目實戰(zhàn)。 前端工程基于 nginx 運行,因為《蒼穹外賣》項目側(cè)重于后端開發(fā),所以黑馬程序員給我們直接提供了前端的代碼部分,我們只需要在本地搭建好前端環(huán)境,并運行起來,專注于后端開

    2024年02月10日
    瀏覽(19)
  • 項目筆記-瑞吉外賣(全)

    項目筆記-瑞吉外賣(全)

    1.對后端返回請求值的分析 2.對不同種請求參數(shù)的分析 3.事務(wù)管理 1.軟件開發(fā)整體介紹 2.項目整體介紹?? 后端:管理菜品和員工信息 前臺:通過手機端,可以瀏覽菜品和添加客戶端 開發(fā)項目流程: 實現(xiàn)基本需求,用戶能在手機瀏覽器訪問 對移動端應(yīng)用改進(jìn),使用微信小程

    2024年02月07日
    瀏覽(24)
  • 瑞吉外賣項目記錄

    瑞吉外賣項目記錄

    本文為個人學(xué)習(xí)黑馬《瑞吉外賣》項目后進(jìn)行的項目總結(jié),更偏向于對自己編寫文本能力的鍛煉以及對項目知識點的簡短記錄。因為個人能力問題,其中可行性分析和測試部分只進(jìn)行了小標(biāo)題的陳列,并沒有進(jìn)行編輯。對《瑞吉外賣》項目感興趣的朋友也可以瀏覽本文后再去

    2024年02月05日
    瀏覽(19)
  • 瑞吉外賣項目----(2)緩存優(yōu)化

    瑞吉外賣項目----(2)緩存優(yōu)化

    將項目推送到遠(yuǎn)程倉庫里,教程在git 提交遠(yuǎn)程倉庫前建議取消代碼檢查 創(chuàng)建新的分支v1.0(用于實現(xiàn)緩存優(yōu)化)并推送到遠(yuǎn)程倉庫 1.1.1 maven坐標(biāo) 導(dǎo)入spring-data-redis的maven坐標(biāo): 1.1.2 配置文件 在application.yml中加入redis相關(guān)配置: 1.1.3 配置類 在項目中加入RedisConfig 1.2.1 實現(xiàn)思路

    2024年02月14日
    瀏覽(16)
  • 瑞吉外賣項目——前后端分離

    瑞吉外賣項目——前后端分離

    前后端分離開發(fā),就是在項目開發(fā)過程中,對于前端代碼的開發(fā)由專門的 前端開發(fā)人員 負(fù)責(zé),后端代碼則由 后端開發(fā)人員 負(fù)責(zé),這樣可以做到分工明確、各司其職,提高開發(fā)效率,前后端代碼并行開發(fā),可以加快項目開發(fā)進(jìn)度。 目前,前后端分離開發(fā)方式已經(jīng)被越來越多

    2023年04月20日
    瀏覽(17)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包