1.業(yè)務開發(fā)
1.對后端返回請求值的分析
2.對不同種請求參數(shù)的分析
3.事務管理
day01
1.軟件開發(fā)整體介紹
2.項目整體介紹??
- 后端:管理菜品和員工信息
- 前臺:通過手機端,可以瀏覽菜品和添加客戶端
開發(fā)項目流程:
- 實現(xiàn)基本需求,用戶能在手機瀏覽器訪問
- 對移動端應用改進,使用微信小程序實現(xiàn)
- 對系統(tǒng)進行優(yōu)化升級,提高系統(tǒng)的使用性能
技術選型:
?
功能架構:
角色:
3.開發(fā)環(huán)境搭建
- 涉及數(shù)據(jù)庫 + maven
-
數(shù)據(jù)庫表介紹:
-
Maven項目搭建
- 第一步,先創(chuàng)建一個maven空項目,然后設置好pom.xml文件和application.yml文件
- 第二步,配置springboot環(huán)境,啟動測試
- 第三部,導入前端靜態(tài)資源,加入配置類來將瀏覽器路徑和本地項目文件路徑做匹配
@Slf4j @Configuration public class WebMvcConfig extends WebMvcConfigurationSupport { /** * 設置靜態(tài)資源映射 * @param registry */ @Override protected void addResourceHandlers(ResourceHandlerRegistry registry) { log.info("開始進行靜態(tài)資源映射..."); registry.addResourceHandler("/backend/**").//瀏覽器地址欄 //映射到真實的路徑(映射的真實路徑末尾必須添加斜杠`/`) addResourceLocations("classPath:/backend/");//這里不要加空格符,貼著放 registry.addResourceHandler("/front/**") .addResourceLocations("classpath:/front/"); } }
4.登錄功能??
查看項目代碼的一般邏輯:
? 前端html界面,找到響應的元素,找到對應的js動態(tài)方法,分析發(fā)送(Ajax)請求到后端的過程,處理好后端代碼,返回處理的R對象給前端來判斷使用(判斷運用是否正確),最后前端再決定跳轉到哪一個界面
-
需求分析
前端代碼
-
功能結構
4.1代碼實現(xiàn)
-
導入通用返回結果類R類
? 前端代碼與R類關系
R類
-
梳理登錄方法邏輯
-
代碼實現(xiàn)
5.退出功能
-
功能邏輯
6.頁面效果出現(xiàn)
-
index.html
menuList屬性值封裝了不同頁面的信息
day02
完成功能:
1.完善登錄功能
-
問題分析
使用過濾器或者攔截器實現(xiàn)
-
代碼實現(xiàn)步驟
-
具體實現(xiàn)
-
1.攔截器用原生的Servlet攔截,因此主加載類要加上@ServletComponentScan注解攔截
2.加上日志注解,能夠使用日志輸出
- 2.具體邏輯
前端處理部分
前端處理響應攔截器:如果是這個狀態(tài)那么自動跳轉回登錄頁面
后端部分:
package com.itiheima.reggie.filter; import com.alibaba.fastjson.JSON; import com.itiheima.reggie.entity.R; import lombok.extern.slf4j.Slf4j; import org.springframework.util.AntPathMatcher; import javax.servlet.*; import javax.servlet.annotation.WebFilter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * @author qin start * @create 2023-04-24-11:28 */ @WebFilter(filterName = "LoginCheckFilter",urlPatterns = "/*")//攔截所有路徑 @Slf4j public class LoginCheckFilter implements Filter { //spring路徑匹配器 public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher(); @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //轉成http格式的Servlet HttpServletRequest request = (HttpServletRequest) servletRequest; HttpServletResponse response = (HttpServletResponse) servletResponse; // 1、獲取本次請求的URI //定義不需要處理的請求路徑 String url = request.getRequestURI(); String[] urls = new String[]{ "/employee/login", "/employee/logout", "/backend/**",//靜態(tài)資源放行 "/front/**" }; // 2、判斷本次請求是否需要處理 boolean check = check(url, urls); // 3、如果不需要處理,則直接放行 if(check){ log.info("攔截到的請求:{}",url); filterChain.doFilter(request,response); return; } // 4、判斷登錄狀態(tài),如果已登錄,則直接放行 //通過判斷session存儲的數(shù)據(jù) if(request.getSession().getAttribute("id") != null){ log.info("登陸成功!用戶id為:{}",request.getSession().getAttribute("id")); filterChain.doFilter(request,response); return; } // 5、如果未登錄則返回未登錄結果 // //這里要用輸出流,因為不是控制器自動返回json格式對象 log.info("登陸失?。√D回登錄界面"); response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));//不放行 return; } /** * 判斷請求路徑是否在不需要處理的路徑里 * @param url * @param urls * @return */ public boolean check(String url,String[] urls){ for (String pattern : urls) { //這里順序不能搞反,第一個參數(shù)為匹配模式 if(PATH_MATCHER.match(pattern,url)){ return true; } } return false; } }
-
2.新增員工功能
- 功能
- 數(shù)據(jù)模型中,employee字段要唯一
-
執(zhí)行流程
-
代碼實現(xiàn)
@PostMapping public R<String> addEmployee(HttpServletRequest request,@RequestBody Employee employee){ // log.info(employee.toString()); //設置初始密碼 employee.setPassword(DigestUtils.md5DigestAsHex("123456".getBytes(StandardCharsets.UTF_8))); employee.setCreateTime(LocalDateTime.now()); employee.setUpdateTime(LocalDateTime.now()); employee.setCreateUser((long)request.getSession().getAttribute("id")); employee.setUpdateUser((long)request.getSession().getAttribute("id")); employeeService.save(employee); return R.success("新增員工成功"); }
-
處理數(shù)據(jù)庫插入重復名字異常
全局異常處理器來處理異常
關鍵點在@ControllerAdvice和@ExceptionHandler,一個用來攔截方法,一個用來處理異常
@ControllerAdvice捕獲方法后,有異常就處理
@ControllerAdvice(annotations = {RestController.class, Controller.class}) @ResponseBody//java對象轉為json格式的數(shù)據(jù) @Slf4j public class GlobalExceptionHandler { //用來捕獲插入重復數(shù)據(jù)異常 @ExceptionHandler(SQLIntegrityConstraintViolationException.class) public R<String> exceptionHandler (SQLIntegrityConstraintViolationException exception){ log.error(exception.getMessage()); return R.error("failed"); } }
//用來捕獲插入重復數(shù)據(jù)異常 @ExceptionHandler(SQLIntegrityConstraintViolationException.class) public R<String> exceptionHandler (SQLIntegrityConstraintViolationException exception){ if (exception.getMessage().contains("Duplicate entry")){ String[] split = exception.getMessage().split(" ");//根據(jù)空格符分割數(shù)組 String msg = split[2] + "已存在"; return R.error(msg); } return R.error("unknown error"); }
-
小結:
-
3.啟用禁用員工信息??(自定義消息轉換器使用)
- 需求分析
-
啟用、禁用員工賬號,本質(zhì)上就是一個更新操作,也就是對status狀態(tài)字段進行操作在Controller中創(chuàng)建update方法,此方法是一個通用的修改員工信息的方法
@PutMapping public R<String> update(HttpServletRequest request, @RequestBody Employee employee){ log.info(employee.toString()); Long empID = (Long)request.getSession().getAttribute("employee"); employee.setUpdateTime(LocalDateTime.now()); employee.setUpdateUser(empID); employeeService.updateById(employee); return R.success("員工修改信息成功"); }
-
出現(xiàn)問題:
原因:js對后端傳過來的數(shù)據(jù)long類型精度丟失,因為Java對象默認通過SpringMVC消息轉換器傳遞過來的數(shù)據(jù)默認是一般的json格式,"id"字段會被當做整型數(shù)據(jù)處理,而js中Long型精度和后端不匹配。
**解決:**對SpringMVC配置自定義的消息處理器,將"id"對應的json格式數(shù)據(jù)轉為字符串值
-
具體解決步驟:
①提供對象轉換器Jackson0bjectMapper,基于Jackson進行Java對象到json數(shù)據(jù)的轉換
②在WebMcConfig配置類中擴展Spring mvc的消息轉換器,在此消息轉換器中使用提供的對象轉換器進行Java對象到json數(shù)據(jù)的轉換//JacksonObjectMapper package com.itzq.reggie.common; 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對象轉為json,或者將json轉為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); } }
/** * 擴展mvc框架的消息轉換器 * @param converters */ @Override protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) { //創(chuàng)建消息轉換器對象 MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter(); //設置對象轉化器,底層使用jackson將java對象轉為json messageConverter.setObjectMapper(new JacksonObjectMapper()); //將上面的消息轉換器對象追加到mvc框架的轉換器集合當中(index設置為0,表示設置在第一個位置,避免被其它轉換器接收,從而達不到想要的功能) converters.add(0,messageConverter); }
4.編輯員工信息
-
功能分析
-
后端功能代碼實現(xiàn)
回顯數(shù)據(jù)
@GetMapping("/{id}") public R<Employee> getById(@PathVariable Long id){ log.info("根據(jù)id查詢員工信息。。。"); Employee employee = employeeService.getById(id); if (employee != null){ return R.success(employee); } return R.error("沒有查詢到該員工信息"); }
修改功能
前面在禁用員工的時候使用到了修改數(shù)據(jù),所以這里不用配置
day03
1.公共字段自動填充
-
問題分析:
設置修改時間和修改人等字段在每張表中基本上都有,而且屬于多條記錄共有的具有相似功能的字段,因此可以每次修改或者插入的時候自動處理
-
用到技術:①Mybatis Plus公共字段自動填充②ThreadLocal線程內(nèi)部屬性
-
技術詳解:
ThreadLocal線程內(nèi)部屬性:客戶端發(fā)送的每次http請求,對應的在服務端都會分配一個新的線程來處理,在處理過程中涉及到下面類中的方法都屬于相同的一個線程,因此一次請求中ThreadLocal對象也應該相同
因此,可以用ThreadLocal用于保存登錄用戶id,解決實現(xiàn)公共字段填充類無法在方法上自動裝配HttpServletRequest的困境,做到會話間數(shù)據(jù)共享
-
實現(xiàn)步驟
-
具體代碼實現(xiàn)
第一版
package com.itzq.reggie.common; import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.reflection.MetaObject; import org.springframework.stereotype.Component; import java.time.LocalDateTime; @Component @Slf4j public class MyMetaObjectHandler implements MetaObjectHandler { @Override public void insertFill(MetaObject metaObject) { log.info("公共字段自動填充【insert】"); log.info(metaObject.toString()); metaObject.setValue("createTime", LocalDateTime.now()); metaObject.setValue("updateTime", LocalDateTime.now()); metaObject.setValue("createUser", new Long(1)); metaObject.setValue("updateUser", new Long(1)); } @Override public void updateFill(MetaObject metaObject) { log.info("公共字段自動填充【update】"); log.info(metaObject.toString()); } }
第二版
@Component @Slf4j public class MyMetaObjectHandler implements MetaObjectHandler { /** * 插入時對公共字段賦值 * @param metaObject */ @Override public void insertFill(MetaObject metaObject) { log.info("公共字段自動填充【insert】"); //1.直接給公共字段設置值 metaObject.setValue("createTime", LocalDateTime.now()); metaObject.setValue("updateTime", LocalDateTime.now()); metaObject.setValue("createUser", BaseContext.get()); metaObject.setValue("updateUser", BaseContext.get()); } /** * 更新時對公共字段賦值 * @param metaObject */ @Override public void updateFill(MetaObject metaObject) { log.info("公共字段自動填充【update】"); metaObject.setValue("updateTime", LocalDateTime.now()); metaObject.setValue("updateUser", new Long(1)); } } /** * 工具類,用來獲取當前登錄用戶的id * 這里設置為靜態(tài)的可以,因為每個線程的ThreadLocal值不同,這樣聲明成靜態(tài)的時候不同線程會賦予不同的ThreadLocal值 * @author qin start * @create 2023-04-26-17:34 */ public class BaseContext { private static ThreadLocal<Long> threadLocal = new ThreadLocal<>(); public static void set(Long id){ threadLocal.set(id); } public static Long get(){ return threadLocal.get(); } }
2.新增分類
-
功能分析
-
代碼實現(xiàn)
@RestController @RequestMapping("/category") @Slf4j public class CategoryController { @Autowired private CategoryService categoryService; @PostMapping public R<String> save(@RequestBody Category category){ log.info("新增菜品:{}",category); categoryService.save(category); return R.success("1"); } }
3.分類的分頁查詢
-
-
代碼實現(xiàn)
@GetMapping("/page") public R<Page> page(int page,int pageSize){ //1.定義分頁構造器 Page<Category> pageInfo = new Page<>(page,pageSize); //2.定義條件構造器 LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.orderByDesc(Category::getSort); //3.進行查詢 categoryService.page(pageInfo,queryWrapper); return R.success(pageInfo); }
4.分類刪除??
-
-
簡單代碼開發(fā)(第一版)
@DeleteMapping public R<String> delete(long ids){//這里一定要為long類型 log.info("刪除菜品id:{}",ids); categoryService.removeById(ids); return R.success("1"); }
-
第二版代碼開發(fā)思路+實現(xiàn)??
因為
菜品``和套餐
都有關聯(lián)到分類的可能性,因此如果刪除分類時,分類里有相應的菜品和套餐,那么要判斷不能刪除①頁面發(fā)送Ajax請求,傳過來要刪除菜品分類的id
②根據(jù)id去
菜品表
和套餐表
去查詢有幾條數(shù)據(jù),如果有數(shù)據(jù)的話拋出一個自定義業(yè)務異常,提示不能刪除③沒有業(yè)務異常的話,進行正常的刪除操作
1.增加一個業(yè)務方法
@Service public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService { @Autowired private DishService dishService; @Autowired private SetMealService setMealService; @Override public void remove(long id) { //1.根據(jù)id去查詢菜品表 LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(Dish::getCategoryId,id); int count = dishService.count(queryWrapper); //拋出自定義業(yè)務處理異常 if(count > 0){ throw new CustomException("無法刪除分類,該分類下存在菜品信息"); } //2.根據(jù)id去查詢套餐表 LambdaQueryWrapper<Setmeal> queryWrapper1 = new LambdaQueryWrapper<>(); queryWrapper1.eq(Setmeal::getCategoryId,id); int count1 = setMealService.count(queryWrapper1); //拋出自定義業(yè)務處理異常 if(count1 > 0){ throw new CustomException("無法刪除分類,該分類下存在套餐信息"); } //3.如果沒有關聯(lián),那么調(diào)用父類ServiceImpl的方法刪除 super.removeById(id); } }
2.自定義異常類和全局異常類方法
public class CustomException extends RuntimeException{ public CustomException(String message){ super(message); } }
//用來處理刪除菜品分類信息 @ExceptionHandler(CustomException.class) public R<String> exceptionHandler (CustomException ex){ return R.error(ex.getMessage()); }
3.修改分類
-
需求分析
-
代碼實現(xiàn)
/** * 修改分類信息 * @param category * @return */ @PutMapping public R<String> update(@RequestBody Category category){ log.info("修改分類信息為:{}",category); categoryService.updateById(category); return R.success("修改分類信息成功"); }
day04
菜品管理相關內(nèi)容
1.文件上傳??
如果部署上去之后,無法打開頁面,先clean一下,再打開項目
-
需求分析
- 文件過濾器會先攔截下來請求,直接返回“未登錄”的信息
-
具體實現(xiàn)
-
MutipartFile文件名必須為file,與前端標簽名保持一致
-
文件上傳后轉存問題:如果文件上傳后不轉存到指定位置,那么默認存儲在一個本地的臨時文件中,程序運行后就會刪除,因此要文件轉存
-
代碼:
注意點:①讀取路徑用@Value注解從配置文件中讀取
? ②文件名的拼接
? ③文件路徑的判別合法問題
? ④返回文件名稱
@RestController @RequestMapping("/common") @Slf4j public class CommonController { //@Value注解用來取值 @Value("${reggie.path}") private String basePath; /** * 上傳文件 * @param file 文件參數(shù)名必須為file * @return */ @RequestMapping("/upload") public R<String> upload(@RequestBody MultipartFile file){ log.info("上傳圖片:{}",file.toString()); //1.生成文件名字,實現(xiàn)文件的保存功能,目前先保存到本地上 //獲取文件類型 String originalFilename = file.getOriginalFilename(); String suffix = originalFilename.substring(originalFilename.lastIndexOf(".")); //使用UUID生成自定義文件名 String fileName = UUID.randomUUID().toString() + suffix; //創(chuàng)建存放的目錄對象 File dir = new File(basePath); if(!dir.exists()){ dir.mkdirs(); } try { file.transferTo(new File(basePath + fileName)); } catch (IOException e) { e.printStackTrace(); } //返回文件名稱,因為頁面之后要使用 return R.success(fileName); } }
-
2.文件下載
-
前端分析
-
代碼實現(xiàn)
- 注意name字段由前端傳過來的參數(shù)
/common/download?name=${response.data}
自動賦值 - 注意這里讀取是從服務端讀,發(fā)送是HttpServletResponse獲取的輸出去,傳送回去需要先設置響應頭數(shù)據(jù)格式
@GetMapping("/download") //這的name由前端自動賦值 public void downLoad(String name, HttpServletResponse response){ //獲取文件名稱,從本地數(shù)據(jù)來創(chuàng)建一個input流進行讀取,output流進行輸出 File file = new File(basePath + name); try { FileInputStream is = new FileInputStream(file); OutputStream outputStream = response.getOutputStream(); response.setContentType("image/jpeg"); //文件讀寫操作 int len = 0; byte[] bytes = new byte[1024]; while((len = is.read(bytes)) != -1){ outputStream.write(bytes); //所儲存的數(shù)據(jù)全部清空 outputStream.flush(); } is.close(); outputStream.close(); } catch (Exception e) { e.printStackTrace(); } }
- 注意name字段由前端傳過來的參數(shù)
3.新增菜品??
-
需求分析
-
功能實現(xiàn)
-
回顯菜品分類信息
因為一進去發(fā)送地址在category下,因此要在category下編寫相應的請求
/** * 根據(jù)條件查詢分類信息,并返回json數(shù)據(jù) * @param category * @return */ @GetMapping("/list") public R<List<Category>> list(Category category){ LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(category.getType() != null,Category::getType,category.getType()); queryWrapper.orderByDesc(Category::getSort).orderByDesc(Category::getUpdateTime); List<Category> list = categoryService.list(queryWrapper); return R.success(list); }
-
處理前端發(fā)過來的請求
DTO繼承實體類,擴展實體類
-
將DTO中的數(shù)據(jù)保存到兩張表中
@Service @Transactional public class DishServiceImpl extends ServiceImpl<DishMapper, Dish> implements DishService { @Autowired DishFlavorService dishFlavorService; /** * 根據(jù)菜品信息,保存數(shù)據(jù)到菜品表和菜品口味表 * @param dishDto */ @Override public void saveWithFlavor(DishDto dishDto) { //保存菜品的基本信息到菜品的基本表中,如果有的菜品的屬性對不上,那么不保存 super.save(dishDto); //這里保存之后,會將表中的數(shù)據(jù)重新填回到dishDto,因此也就獲取了此時的菜品id //獲取新增的菜品id,為口味表中的每一個口味增加相應的菜品id Long id = dishDto.getId(); //獲取菜品口味 List<DishFlavor> flavors = dishDto.getFlavors(); for (DishFlavor flavor : flavors) { flavor.setDishId(id); } //批量保存菜品口味數(shù)據(jù)到菜品口味表 dishFlavorService.saveBatch(flavors); } }
-
-
總結分析
- 處理前端請求
- 后端返回值識前端需求的數(shù)據(jù)為準,后端定義了數(shù)據(jù)模型之后只要根據(jù)前端需求,將相應的需求放在R.data屬性中即可
4.菜品信息分頁查詢
-
需求分析
- 處理分頁查詢中的難點在于:如何將dish表中的categoryId字段切換為categoryName,因為前端頁面需要菜品分類而不是菜品ID。所以我們就要用到dishDto這個類擴展dish類,① 先從dish表中查數(shù)據(jù)然后封裝到page類中 ② 將page類中的分頁信息數(shù)據(jù)數(shù)據(jù)拷貝到Page這個分頁信息類 ③ 將page中的records信息映射到page的分頁信息類
封裝Dish數(shù)據(jù)
-
代碼實現(xiàn)
- BeanUtils工具類屬于
org.springframework.beans.BeanUtils
,是spring框架提供的工具類,簡化數(shù)據(jù)封裝,用于封裝JavaBean,避免了大量的get,set方法進行拷貝賦值
@GetMapping("/page") public R<Page> page(int page,int pageSize,String name){ //1.根據(jù)名字查詢分頁信息 //首先保證名字不為空 Page<Dish> dishPageInfo = new Page<>(page,pageSize); Page<DishDto> dtoPageInfo = new Page<>(); LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.like(name != null,Dish::getName,name); dishService.page(dishPageInfo,queryWrapper); //2.對dish分頁信息進行拷貝 BeanUtils.copyProperties(dishPageInfo,dtoPageInfo,"records"); List<Dish> records = dishPageInfo.getRecords(); //3.返回頁面的新records數(shù)據(jù),就是data數(shù)據(jù) List<DishDto> list = records.stream().map((item) -> { //用來將每一個item變?yōu)閐ishDto,然后返回給Page<dishDto> DishDto dishDto = new DishDto(); BeanUtils.copyProperties(item,dishDto); Long categoryId = item.getCategoryId(); Category category = categoryService.getById(categoryId); if(category != null){ String categoryName = category.getName(); dishDto.setCategoryName(categoryName); } return dishDto; }).collect(Collectors.toList()); dtoPageInfo.setRecords(list); return R.success(dtoPageInfo); }
- BeanUtils工具類屬于
5.菜品修改信息??
- 凡是涉及對數(shù)據(jù)庫數(shù)據(jù)的
多次增刪改(>=2次)
,都需要事務控制,來防止一次修改出錯而接著照常執(zhí)行的錯誤
-
需求分析
- 保存修改數(shù)據(jù)的時候發(fā)送的請求
-
代碼實現(xiàn)
-
DishServiceImpl —> 回顯功能
進行簡單的查詢兩個表dish和Flavor,中間橋梁是dishID;查詢數(shù)據(jù)封裝到DishDto,返回給頁面
dish中有dishID,而Flavor表中有dishID,可根據(jù)dishId查詢口味表
/** * 根據(jù)id查詢查詢dishDto來賦值 * 回顯信息 * @param id * @return */ @Override public DishDto getByIdWithFlavors(Long id) { //查詢dish表信息,獲取對應菜品 Dish dish = this.getById(id); DishDto dishDto = new DishDto(); //拷貝到拓展對象中 BeanUtils.copyProperties(dish,dishDto); Long dishId = dish.getId(); LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(dishId != null,DishFlavor::getDishId,dishId); List<DishFlavor> list = dishFlavorService.list(queryWrapper); dishDto.setFlavors(list); //返回主角數(shù)據(jù) return dishDto; }
-
保存修改數(shù)據(jù):
操作:清理當前菜品口味信息,然后批量插入口味信息 —> 避免了還需要判斷是清楚還是增加的麻煩
①更新dish表②更新口味表(先清除之間口味信息,再批量插入當前口味信息)
DishServiceImpl
/** * 修改菜品信息 * @param dishDto */ @Override @Transactional//保證事務一致性 public void updateWithFlavor(DishDto dishDto) { //1.更新dish表中的信息 this.updateById(dishDto); //清理當前菜品,然后批量插入口味信息 //2.根據(jù)id清除相關口味信息 Long id = dishDto.getId(); LambdaQueryWrapper<DishFlavor> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(DishFlavor::getDishId,id); dishFlavorService.remove(queryWrapper); //3.批量插入口味信息,重新設置flavor信息,因為傳遞過來的flavor的dishId屬性沒賦上值 List<DishFlavor> flavors = dishDto.getFlavors(); flavors = flavors.stream().map((item) -> { item.setDishId(dishDto.getId()); return item; }).collect(Collectors.toList()); dishFlavorService.saveBatch(flavors); }
-
day05
1.概述
數(shù)據(jù)模型
注意套餐關系表中存儲的數(shù)據(jù),
一個套餐對應多個菜品
,分開存儲
2.新增套餐
-
分析??
和上一個開發(fā)類似
-
前端提交過來的信息包含
套餐基本信息
和套餐與菜品關聯(lián)信息
因此需要設置一個setmealDto,Dto中包含套餐基本信息和套餐與菜品關聯(lián)信息
-
后端在setmealController中接收這個Dto,然后新增業(yè)務方法去處理Dto
-
業(yè)務方法:
①將dto基本信息傳入到
套餐基本信息表
中②將
套餐id
和這個對象中的list集合
中的數(shù)據(jù)添加到套餐菜品表
中③涉及操作兩張表,需要加入@transactional注解,要么同時成功,要么同時失敗
-
-
功能實現(xiàn)一:回顯添加菜品
根據(jù)分類categoryId,來去相應dish表中查詢信息,進而回顯信息
/** * 根據(jù)categoryId,回顯對應分類下的菜品信息 * @param dish * @return */ @GetMapping("/list") public R<List<Dish>> list(Dish dish){ LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>(); //兩個eq信息 queryWrapper.eq(dish != null,Dish::getCategoryId,dish.getCategoryId()); queryWrapper.eq(Dish::getStatus,1); //添加排序條件 queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime); List<Dish> list = dishService.list(queryWrapper); return R.success(list); }
功能實現(xiàn)二:
實現(xiàn)
添加菜品
功能- 這里要注意
setMealId
在前端傳過來的數(shù)據(jù)沒有,需要將前端基本信息添加到SetMeal表中,才能得到相應的Id,然后為套餐菜品對象
賦值上值
SetMealServiceImpl
@Service public class SetMealServiceImpl extends ServiceImpl<SetMealMapper, Setmeal> implements SetMealService { @Autowired private SetMealDishService setMealDishService; /** * 新增菜品套餐 * @param setmealDto */ @Override public void saveWithDish(SetmealDto setmealDto) { //1.調(diào)用setMeal本有的功能,順便得到套餐id this.save(setmealDto); //2.將從數(shù)據(jù)庫得到的套餐id封裝回setmealDishes對象中 List<SetmealDish> setmealDishes = setmealDto.getSetmealDishes(); setmealDishes.stream().map((item) -> { item.setSetmealId(setmealDto.getId()); return item; }).collect(Collectors.toList()); //3. setMealDishService.saveBatch(setmealDishes); } }
- 這里要注意
3.套餐分頁查詢
- 功能與day04的菜品信息分類查詢相似
- 在套餐管理界面,套餐分類字段顯示的是
categoryId對應的中文
,但在數(shù)據(jù)庫里查詢到的是categoryId,因此需要利用categoryId查詢到categoryName,并賦值給數(shù)據(jù)傳輸對象SetmealDto
/**
* 套餐分頁查詢
* @param page
* @param pageSize
* @param name
* @return
*/
@GetMapping("/page")
public R<Page> list(int page, int pageSize, String name){
//分頁構造器對象
Page<Setmeal> pageInfo = new Page<>(page, pageSize);
Page<SetmealDto> dtoPage = new Page<>();
//構造查詢條件對象
LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(name != null, Setmeal::getName, name);
//操作數(shù)據(jù)庫
setMealService.page(pageInfo,queryWrapper);
//對象拷貝
BeanUtils.copyProperties(pageInfo,dtoPage,"records");
List<Setmeal> records = pageInfo.getRecords();
List<SetmealDto> list = records.stream().map((item) -> {
SetmealDto setmealDto = new SetmealDto();
BeanUtils.copyProperties(item, setmealDto);
//獲取categoryId
Long categoryId = item.getCategoryId();
Category category = categoryService.getById(categoryId);
if (category != null) {
String categoryName = category.getName();
setmealDto.setCategoryName(categoryName);
}
return setmealDto;
}).collect(Collectors.toList());
dtoPage.setRecords(list);
return R.success(dtoPage);
}
4.刪除套餐信息
-
需求分析
提供一個方法處理刪除一個和刪除多個請求
-
代碼開發(fā)
注意點:
①接受前端ids數(shù)據(jù),傳過來的數(shù)據(jù)本身是數(shù)組形式,所以加不加注解無所謂,但是
List是列表,所以要加注解@RequestParam
②根據(jù)id刪除套餐,不僅
刪除套餐
,也刪除關聯(lián)套餐表
中的信息業(yè)務邏輯:(SetMealServiceImpl)
? 1.查詢套餐狀態(tài),確定是否可用刪除
? 2.如果不能刪除,拋出一個業(yè)務異常,提示在售賣中
? 3.如果可以刪除,先刪除套餐表中的數(shù)據(jù)
? 4.刪除關系表中的數(shù)據(jù)
/**
* 根據(jù)ids刪除套餐信息
* @param ids
*/
@Override
@Transactional
public void removeWithDish(List<Long> ids) {
// 1.查詢套餐狀態(tài),確定是否可用刪除
//SQL語句:select count(*) from setMeal where id in ids and status = 1;
LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(Setmeal::getId,ids);
queryWrapper.eq(Setmeal::getStatus,1);
int count = this.count(queryWrapper);
// 2.如果不能刪除,拋出一個業(yè)務異常,提示**在售賣中**
if(count > 0){
throw new CustomException("商品還在銷售,不能刪除");
}
// 3.如果可以刪除,先刪除套餐表中的數(shù)據(jù)
this.removeByIds(ids);
// 4.刪除關系表中的數(shù)據(jù)
//根據(jù)套餐id去關系表中去查數(shù)據(jù),然后匹配刪除
//delete from setMealDish where setmealId in ids
LambdaQueryWrapper<SetmealDish> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.in(SetmealDish::getSetmealId,ids);
setMealDishService.remove(lambdaQueryWrapper);
}
-
起售,停售操作(SetMealServiceImpl)
- 這里采用遍歷操作實現(xiàn)批量停售,起售不太好,應該用mp的具體更新方法操作,等學了mp之后再來補吧,希望還記得
/**
* 更改售賣狀態(tài)
* @param ids
* @param status 1表示啟售,0表示停售
*/
@Override
public void changeStatus(List<Long> ids, int status) {
//改變售賣狀態(tài)
for (int i = 0; i < ids.size(); i++) {
Long id = ids.get(i);
//根據(jù)id得到每個dish菜品。
Setmeal setmeal = this.getById(id);
setmeal.setStatus(status);
this.updateById(setmeal);
}
}
5.短信發(fā)送
-
概述
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-zEC2oEPD-1685267442440)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305082027150.png)]
-
代碼開發(fā)
6.手機驗證碼登錄
-
需求分析
-
代碼開發(fā)(前期準備)
①對
短信服務
進行放行,否則會自動跳回到登錄頁面②改寫全局過濾器,對移動端用戶進行驗證碼訪問,進行放行
-
代碼開發(fā)(發(fā)送驗證碼)
避坑:
這個測試的時候,前端頁面有問題,login.html不發(fā)送ajax請求,解決辦法:
把day05代碼中的所有前端代碼
替換到自己的項目中就行了
/**
* 發(fā)送手機驗證碼
* @param user
*/
@PostMapping("/sendMsg")
public R<String> sendMsg(@RequestBody User user, HttpSession session){
//1.獲取手機號,并進行檢驗
String phone = user.getPhone();
//2.生成驗證碼,采用短信發(fā)送服務進行發(fā)送短信
if(phone != null){
String code = String.valueOf(ValidateCodeUtils.generateValidateCode(4));
log.info("驗證碼為:{}",code);
// SMSUtils.sendMessage("瑞吉外賣","SMS_460725810",phone,code);
//3.將發(fā)送的驗證碼保存在session中,便于之后查驗
session.setAttribute(phone,code);
return R.success("發(fā)送驗證碼成功,等待查收");
}
//4.短信發(fā)送錯誤,返回錯誤信息;成功,返回成功信息
return R.error("發(fā)送驗證碼失敗");
}
-
代碼開發(fā)(驗證碼登錄)
-
因前端傳過來的對象,后端沒有相應的實體類與其對應
這時可以采取
拓展實體類dto
或者是map集合的方式
接收 -
登錄方法返回值為User對象,這樣讓
瀏覽器也保存一份用戶信息
-
/**
* 發(fā)送手機驗證碼
* @param map
*/
@PostMapping("/login")
public R<User> login(@RequestBody Map map, HttpSession session){
log.info("登錄操作:{}",map);
//1.獲取手機號和驗證碼
String phone = map.get("phone").toString();
String code = map.get("code").toString();
//2.進行手機號和驗證碼比對,如果成功進行登錄的邏輯
Object codeInSession = session.getAttribute("phone");
if(codeInSession != null && codeInSession.equals(code)){
//3.匹配結果對上之后,如果手機號在表中不存在自動完成祖冊
LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(User::getPhone,phone);
User user = userService.getOne(queryWrapper);
if(user == null){
user = new User();
user.setPhone(phone);
user.setStatus(1);
userService.save(user);
}
session.setAttribute("id",user.getId());
return R.success(user);
}
//4.短信發(fā)送錯誤,返回錯誤信息;成功,返回成功信息
return R.error("登陸失敗");
}
day06
這一天都是移動端開發(fā)
1.地址簿相關功能
- 需求分析
/**
* 地址簿管理
*/
@Slf4j
@RestController
@RequestMapping("/addressBook")
public class AddressBookController {
@Autowired
private AddressBookService addressBookService;
/**
* 新增
*/
@PostMapping
public R<AddressBook> save(@RequestBody AddressBook addressBook) {
addressBook.setUserId(BaseContext.get());
log.info("addressBook:{}", addressBook);
addressBookService.save(addressBook);
//把返回的信息,交給前端存起來
//每次前端已查詢
return R.success(addressBook);
}
/**
* 設置默認地址
*/
@PutMapping("default")
public R<AddressBook> setDefault(@RequestBody AddressBook addressBook) {
log.info("addressBook:{}", addressBook);
LambdaUpdateWrapper<AddressBook> wrapper = new LambdaUpdateWrapper<>();
wrapper.eq(AddressBook::getUserId, BaseContext.get());
wrapper.set(AddressBook::getIsDefault, 0);
//SQL:update address_book set is_default = 0 where user_id = ?
addressBookService.update(wrapper);
addressBook.setIsDefault(1);
//SQL:update address_book set is_default = 1 where id = ?
addressBookService.updateById(addressBook);
return R.success(addressBook);
}
/**
* 根據(jù)id查詢地址
*/
@GetMapping("/{id}")
public R get(@PathVariable Long id) {
AddressBook addressBook = addressBookService.getById(id);
if (addressBook != null) {
return R.success(addressBook);
} else {
return R.error("沒有找到該對象");
}
}
/**
* 查詢默認地址
*/
@GetMapping("default")
public R<AddressBook> getDefault() {
LambdaQueryWrapper<AddressBook> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AddressBook::getUserId, BaseContext.get());
queryWrapper.eq(AddressBook::getIsDefault, 1);
//SQL:select * from address_book where user_id = ? and is_default = 1
AddressBook addressBook = addressBookService.getOne(queryWrapper);
if (null == addressBook) {
return R.error("沒有找到該對象");
} else {
return R.success(addressBook);
}
}
/**
* 查詢指定用戶的全部地址
*/
@GetMapping("/list")
public R<List<AddressBook>> list(AddressBook addressBook) {
addressBook.setUserId(BaseContext.get());
log.info("addressBook:{}", addressBook);
//條件構造器
LambdaQueryWrapper<AddressBook> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(null != addressBook.getUserId(), AddressBook::getUserId, addressBook.getUserId());
queryWrapper.orderByDesc(AddressBook::getUpdateTime);
//SQL:select * from address_book where user_id = ? order by update_time desc
return R.success(addressBookService.list(queryWrapper));
}
}
2.菜品展示
-
需求分析
①因為發(fā)送兩次請求,第二次失敗,所以展示信息有誤,這里將第二次的改用假數(shù)據(jù)
②存在問題:頁面發(fā)送的請求為http://localhost:8080/dish/list?categoryId=1397844263642378242&status=1,這個list請求在Controller中只設置了返回List類型,而Dish實體類中沒有相應的口味信息,因此在前端頁面上不會顯示
口味信息
,所以要拓展dish實體類,返回dishDto- 修改list方法,返回dishDto的代碼
/** * 根據(jù)categoryId,回顯對應分類以及菜品口味信息 * @param dish * @return */ @GetMapping("/list") public R<List<DishDto>> list(Dish dish){ LambdaQueryWrapper<Dish> queryWrapper = new LambdaQueryWrapper<>(); //兩個eq信息 queryWrapper.eq(dish != null,Dish::getCategoryId,dish.getCategoryId()); queryWrapper.eq(Dish::getStatus,1); //添加排序條件 queryWrapper.orderByAsc(Dish::getSort).orderByDesc(Dish::getUpdateTime); List<Dish> list = dishService.list(queryWrapper);//查出來所有菜品信息 //將每一條信息都 List<DishDto> dishDtoList = list.stream().map((item) ->{ DishDto dishDto = new DishDto(); //1.對象拷貝(每一個list數(shù)據(jù)) BeanUtils.copyProperties(item,dishDto); Long categoryId = item.getCategoryId(); //分類id //通過categoryId查詢到category內(nèi)容 Category category = categoryService.getById(categoryId); //判空 if(category != null){ String categoryName = category.getName(); dishDto.setCategoryName(categoryName); } //2.將菜品口味信息賦值給dto Long id = item.getId(); LambdaQueryWrapper<DishFlavor> dishFlavorLambdaQueryWrapper = new LambdaQueryWrapper<>(); dishFlavorLambdaQueryWrapper.eq(DishFlavor::getDishId,id); List<DishFlavor> dishFlavors = dishFlavorService.list(dishFlavorLambdaQueryWrapper); dishDto.setFlavors(dishFlavors); return dishDto; }).collect(Collectors.toList()); return R.success(dishDtoList); }
- 設置setMeal方法,用于展示套餐
/** * 用于移動端展示數(shù)據(jù) * @param setmeal * @return */ @GetMapping("/list") public R<List<Setmeal>> list(Setmeal setmeal){ //創(chuàng)建條件構造器 LambdaQueryWrapper<Setmeal> queryWrapper = new LambdaQueryWrapper<>(); //添加條件 queryWrapper.eq(setmeal.getCategoryId() != null,Setmeal::getCategoryId,setmeal.getCategoryId()); queryWrapper.eq(setmeal.getStatus() != null,Setmeal::getStatus,setmeal.getStatus()); //排序 queryWrapper.orderByDesc(Setmeal::getUpdateTime); List<Setmeal> list = setMealService.list(queryWrapper); return R.success(list); }
3.加入購物車
-
數(shù)據(jù)分析
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-btcgqkdD-1685267442442)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305141908129.png)]
-
代碼開發(fā)
設置userId,確定哪個用戶點的,然后判斷number數(shù)據(jù)類型
@PostMapping("/add") public R<ShoppingCart> add(@RequestBody ShoppingCart shoppingCart){ log.info("購物車數(shù)據(jù):{}",shoppingCart); //1.將當前用戶的id設置進數(shù)據(jù)庫中 Long currentId = BaseContext.get(); shoppingCart.setUserId(currentId); LambdaQueryWrapper<ShoppingCart> queryWrapper = new LambdaQueryWrapper<>(); queryWrapper.eq(ShoppingCart::getUserId,currentId); //2.判斷當前傳過來的是菜品信息還是套餐信息。用于后續(xù)判斷當前菜品或者套餐是否在購物車中 if(shoppingCart.getDishId() != null){ queryWrapper.eq(ShoppingCart::getDishId,shoppingCart.getDishId()); }else{ queryWrapper.eq(ShoppingCart::getSetmealId,shoppingCart.getSetmealId()); } //select * from shoppingCart where userId = ? and ... ShoppingCart cartServiceOne = shoppingCartService.getOne(queryWrapper); //3.進行查詢記錄,如果能查到記錄,那么說明數(shù)據(jù)庫中有,需要在原有基礎上+1 if(cartServiceOne != null){ Integer number = cartServiceOne.getNumber(); number += 1; cartServiceOne.setNumber(number); shoppingCartService.updateById(cartServiceOne); }else{ shoppingCart.setNumber(1); shoppingCartService.save(shoppingCart); cartServiceOne.setNumber(1); } return R.success(cartServiceOne); }
-
展示購物車信息 /list
根據(jù)userId,去查購物車返回list集合就可
按照登錄id去數(shù)據(jù)庫中查詢信息
不同用戶的userid不同,所以購物車信息不同
-
4.用戶下單??
用到了很多張表,具有可學習性
- 訂單表插入數(shù)據(jù) — 從購物表中算出總金額 查詢用戶信息
- 訂單明細表中插入多條數(shù)據(jù) — 從購物表得出
-
需求分析
訂單明細表
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-OMTzZb4b-1685267442445)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305142010371.png)]
-
代碼開發(fā)
用戶下單分析一:(下單之后,存入
訂單表
和訂單明細表
中)? 傳遞參數(shù):
? 不需要傳遞過來
購物車信息
和用戶id
,因為在登陸過程中已經(jīng)知道用戶id且購物車信息可根據(jù)用戶id查詢出來用戶下單分析二:
? 獲得當前用戶id
? 查詢當前用戶的
購物車數(shù)據(jù)
? 向訂單表插入數(shù)據(jù),一條數(shù)據(jù)
? 向訂單明細表插入數(shù)據(jù),多條數(shù)據(jù)
? 清空購物車數(shù)據(jù)
2.項目優(yōu)化
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-j3E0jDZj-1685267442447)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305262033884.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-wz3p48bs-1685267442447)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305262034094.png)]
day01
1.引入版本控制
- 先創(chuàng)建一個遠程的空倉庫,復制鏈接
- 本地先創(chuàng)建倉庫,然后連接遠程倉庫推送
- 創(chuàng)建兩個分支,
master
和v1.0
,v1.0
用于開發(fā)緩存內(nèi)容,開發(fā)完成后,合并到master
分支
2.環(huán)境搭建
springboot-redis-starter
redis
文件配置設置
redis配置類
,便于觀察
3.短信驗證碼(Redis)
-
實現(xiàn)思路
4.緩存菜品數(shù)據(jù)
-
實現(xiàn)思路
主從一致
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-TAWU5g3p-1685267442449)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305271513707.png)]
-
實現(xiàn)代碼
- 查詢菜品
- 先根據(jù)前端傳遞過來的categoryId和status構造一個key,從緩存數(shù)據(jù)庫中獲取,如果能獲取到,那么直接返回
- 獲取不到dishDto,進行1去數(shù)據(jù)庫查詢,然后放入緩存數(shù)據(jù)庫,然后返回
//查詢方法 @GetMapping("/list") public R<List<DishDto>> list(Dish dish){ List<DishDto> dishDtoList = null; //1.第一次訪問構造key,存入緩存數(shù)據(jù)庫中 String key = "dish_" + dish.getCategoryId() + "_" + dish.getStatus(); //2.從緩存數(shù)據(jù)庫中拿數(shù)據(jù) dishDtoList = (List<DishDto>)redisTemplate.opsForValue().get(key); //3.判斷緩存數(shù)據(jù)庫中有沒有,有的話直接去拿數(shù)據(jù) if(dishDtoList != null){ return R.success(dishDtoList); } //4.如果沒有的話,去數(shù)據(jù)庫查詢 //..... }
//修改方法 @PostMapping public R<String> add(@RequestBody DishDto dishDto){ log.info(dishDto.toString()); dishService.saveWithFlavor(dishDto); //添加后后刪除數(shù)據(jù)庫中對應的key 刪除dish開頭+dishid String key = "dish_" + dishDto.getCategoryId() + "_1"; redisTemplate.delete(key); return R.success("新增菜品成功"); }
5.SpringCache
-
概述
常用注解
-
使用
spring cache
使用哪種緩存技術,就導入對應的包,然后
開啟注解
即可 -
使用jar包
使用基礎功能的緩存,導入
web
包即可使用基于redis等緩存技術,那么要導入
spring-boot-starter-cache
-
-
普通cache使用
-
普通的緩存在加注解之后放在一個
線程安全的map
中,基于內(nèi)存 -
初始化緩存方式
①注入
cachemanager
,加上注解緩存操作
(獲取方法內(nèi)參數(shù),通過SPEL
),緩存的對象的類要實現(xiàn)序列化接口
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-FrJVSACv-1685267442450)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305271636494.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-icyXONac-1685267442450)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305271638756.png)]
刪除緩存
查詢數(shù)據(jù)
-
-
redis使用
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gUFBCIaN-1685267442452)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305271707236.png)]
6.使用springCache緩存套餐數(shù)據(jù)
緩存的數(shù)據(jù):
分類
+不同種類
day02
1.MySQL主從復制
-
介紹
實現(xiàn)讀寫分離,減輕單臺數(shù)據(jù)庫壓力和防止主數(shù)據(jù)庫數(shù)據(jù)損毀
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-lZYOYNoS-1685267442454)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305271739718.png)]
-
原理—從庫和主庫通過
日志
做一樣的操作[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-kgG6J8Kg-1685267442454)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305271745822.png)]
-
操作步驟
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-jLGSubZH-1685267442454)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305271859784.png)]
第二步:重啟MySQL
#mysql8第三步執(zhí)行 create user xiaoming identified by 'Root@123456'; grant replication slave on *.* to xiaoming;
MySQL主從復制略過了。。。之后有時間再來補吧
2.Nginx
1.概述
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-gebXbH18-1685267442455)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305271928408.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-0j5WVZVf-1685267442456)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305271939160.png)]
2.基本命令
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-s0W8kKqq-1685267442457)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305271942072.png)]
./nginx -v
查看版本
./nginx -t
檢查配置文件是否正確
3.配置文件
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-KB1hPHBa-1685267442458)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305281346785.png)]
4.niginx具體應用??
1.部署靜態(tài)資源
- 配置信息在config文件里
- 服務可
開多個
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-mk1fM9XZ-1685267442458)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305281352592.png)]
2.反向代理
正向代理是在
客戶端設置
,反向代理是在服務端設置
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-lGfUbUtV-1685267442459)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305281401642.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-KvR5Nqlq-1685267442459)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305281404563.png)]
實現(xiàn)反向代理 ----
轉發(fā)
3.負載均衡
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-1ZuNYVVu-1685267442460)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305281413916.png)]
實現(xiàn)
負載均衡算法
默認
輪詢算法
day03
1.前后端分離
1.概述
2.前后端分離開發(fā)
變化:前后端代碼不再混合在一個工程中
-
開發(fā)流程
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-MgWOZ1cB-1685267442461)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305281432025.png)]
3.YApi
提供API接口數(shù)據(jù)
4.Swagger(生成接口文檔)
-
介紹
-
具體實現(xiàn)
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ehhnnKDk-1685267442463)(https://weifengqin-image.oss-cn-nanjing.aliyuncs.com/img/202305281443013.png)]
-
功能
因為包掃描,所以能真正地controller
-
常用注解
目的是為了生成接口的時候,個性化定制
2.項目部署
部署架構
- 前端項目
-
部署靜態(tài)資源nginx上
將前端靜態(tài)資源文件放在nginx的html文件夾下,然后修改nginx配置信息,映射靜態(tài)文件
-
配置反向代理
-
后端項目
-
-
打包項目 部署工程
(如果訪問后端項目,訪問超時,可能是因為數(shù)據(jù)庫的問題,可能
遠端
沒有配置數(shù)據(jù)庫)
-
-
總結文章來源:http://www.zghlxwxcb.cn/news/detail-466971.html
之后再啟動項目,先啟動前端項目,再啟動后端項目
.aliyuncs.com/img/202305281417603.png" alt=“image-20230528141758552” style=“zoom:33%;” />
day03
1.前后端分離
1.概述
[外鏈圖片轉存中…(img-RglKcxwc-1685267442460)]
[外鏈圖片轉存中…(img-gqfy4Vyd-1685267442461)]
2.前后端分離開發(fā)
變化:前后端代碼不再混合在一個工程中
-
開發(fā)流程
[外鏈圖片轉存中…(img-MgWOZ1cB-1685267442461)]
3.YApi
提供API接口數(shù)據(jù)
4.Swagger(生成接口文檔)
-
介紹
[外鏈圖片轉存中…(img-OcSGU8NN-1685267442462)]
-
具體實現(xiàn)
[外鏈圖片轉存中…(img-ehhnnKDk-1685267442463)]
[外鏈圖片轉存中…(img-qddnjUoD-1685267442464)]
-
功能
因為包掃描,所以能真正地controller
-
常用注解
目的是為了生成接口的時候,個性化定制
[外鏈圖片轉存中…(img-OkPdI6g0-1685267442464)]
2.項目部署
部署架構
- 前端項目
-
部署靜態(tài)資源nginx上
將前端靜態(tài)資源文件放在nginx的html文件夾下,然后修改nginx配置信息,映射靜態(tài)文件
-
配置反向代理
[外鏈圖片轉存中…(img-mFWI408T-1685267442465)]
-
后端項目
-
[外鏈圖片轉存中…(img-sitC0vrN-1685267442465)]
-
打包項目 部署工程
(如果訪問后端項目,訪問超時,可能是因為數(shù)據(jù)庫的問題,可能
遠端
沒有配置數(shù)據(jù)庫)
-
-
總結
之后再啟動項目,先啟動前端項目,再啟動后端項目文章來源地址http://www.zghlxwxcb.cn/news/detail-466971.html
到了這里,關于項目筆記-瑞吉外賣(全)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!