武漢 網(wǎng)站建設(shè)公司登封網(wǎng)站關(guān)鍵詞優(yōu)化軟件
蒼穹外賣學(xué)習(xí)
文章目錄
- 蒼穹外賣學(xué)習(xí)
- 知識前提:
- **<font color="red">Nginx**
- **<font color="red">Swagger**
- 1.管理員登錄
- 思路:
- 詳細(xì)步驟:
- 1.1新增員工
- 問題1:在新增員工時,需要將當(dāng)前登錄人的id存入表中對應(yīng)的創(chuàng)建人id和修改人id。
- 問題2:錄入的用戶名已存在時,拋出異常后并未處理,直接停止服務(wù)了。
- 1.2員工信息分頁查詢
- 問題1:分頁查詢時,Records中的時間與前端需要的不符
- 知識點:序列化和反序列化
- 1.3修改員工時信息回顯
- 1.4 修改員工信息
- 2.刪除分類注意事項
- 2.1公共字段自動填充
- 實現(xiàn)思路
- 2.2文件上傳功能(阿里云OSS存儲)
- 2.3新增菜品功能注意
- DTO/VO/DO/PO理解:
- 2.4分頁查詢sql
- 2.5刪除菜品
- 4.1.3 表設(shè)計
- 2.5新增菜品接口
- 3.新增套餐
- 4.SpringCache緩存
知識前提:
Nginx
前端請求通過Nginx轉(zhuǎn)發(fā)到后端服務(wù)器,從而實現(xiàn)反向代理。
Swagger
-
在配置類中加入 knife4j 相關(guān)配置
WebMvcConfiguration.java
/*** 通過knife4j生成接口文檔* @return */@Beanpublic Docket docket() {ApiInfo apiInfo = new ApiInfoBuilder().title("蒼穹外賣項目接口文檔").version("2.0").description("蒼穹外賣項目接口文檔").build();Docket docket = new Docket(DocumentationType.SWAGGER_2).apiInfo(apiInfo).select().apis(RequestHandlerSelectors.basePackage("com.sky.controller")).paths(PathSelectors.any()).build();return docket;}
-
設(shè)置靜態(tài)資源映射,否則接口文檔頁面無法訪問
WebMvcConfiguration.java
/*** 設(shè)置靜態(tài)資源映射* @param registry */ protected void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/doc.html").addResourceLocations("classpath:/META-INF/resources/");registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/"); }
1.管理員登錄
思路:
- 前端發(fā)起登錄請求:前端通過 HTTP 請求向后端發(fā)送登錄信息,包括用戶名和密碼。
- 后端接收并處理請求:后端接收請求,將前端傳來的數(shù)據(jù)封裝成
EmployeeLoginDTO
對象,并進(jìn)行登錄校驗。 - 業(yè)務(wù)邏輯處理:在業(yè)務(wù)層進(jìn)行具體的登錄邏輯處理,包括數(shù)據(jù)庫查詢和密碼校驗。
- 生成 JWT 令牌:登錄成功后,生成 JWT 令牌,并將其與管理員信息一起封裝成
EmployeeLoginVO
對象。 - 響應(yīng)結(jié)果:將封裝好的
EmployeeLoginVO
對象作為響應(yīng)返回給前端。
詳細(xì)步驟:
- 前端發(fā)起登錄請求
- 前端通過 POST 請求向后端發(fā)送管理員的登錄信息(用戶名和密碼)。
- 后端接收并處理請求
- 控制層接收前端傳來的登錄信息,并將其封裝成
EmployeeLoginDTO
對象。 - 調(diào)用業(yè)務(wù)層的登錄方法進(jìn)行校驗。
- 控制層接收前端傳來的登錄信息,并將其封裝成
MD5加密:password = DigestUtils.md5DigestAsHex(password.getBytes()); //MD5加密
業(yè)務(wù)邏輯處理
- 在業(yè)務(wù)層通過用戶名查詢數(shù)據(jù)庫
- 判斷用戶名是否存在或鎖定
- 接著進(jìn)行密碼校驗,校驗完畢后,成功返回管理員對象,否則報異常(用戶名不存在、密碼不對、賬號被鎖定)
生成 JWT 令牌
-
此時驗證成功后,生成包含管理員信息的 JWT 令牌,格式是Map鍵值對
-
通過
yaml
文件的配置生成JWT令牌。通常 JWT 令牌會包含管理員的 ID 和一些其他必要的信息。
封裝響應(yīng)對象
- 將管理員信息和 JWT 令牌封裝進(jìn)
EmployeeLoginVO
對象中。 EmployeeLoginVO
類實現(xiàn)了Serializable
接口,確保對象可以被序列化。- 使用 Lombok 的
@Data
、@Builder
等注解簡化對象的創(chuàng)建和使用。
這里VO
對象封裝是使用了構(gòu)造器模式和序列化接口等技術(shù),使對象的創(chuàng)建和使用更方便高效。
1.1新增員工
問題1:在新增員工時,需要將當(dāng)前登錄人的id存入表中對應(yīng)的創(chuàng)建人id和修改人id。
可以通過ThreadLocal進(jìn)行單一的線程存儲,存儲的id實在JWT令牌解析后,直接存進(jìn)Threadlocal,因為線程都是一致的,所以可以直接使用。
問題2:錄入的用戶名已存在時,拋出異常后并未處理,直接停止服務(wù)了。
這里可以看出,報錯的異常名為SQLIntegrityConstraintViolationException
Duplicate entry ‘zhangsan’ for key ‘employee.idx_username’ 意思就是重復(fù)的用戶名,所以就需要設(shè)置自定義異常
1.2員工信息分頁查詢
注意事項:
- 請求參數(shù)類型為Query,不是json格式提交,在路徑后直接拼接。/admin/employee/page?name=zhangsan
- 返回數(shù)據(jù)中records數(shù)組中使用Employee實體類對屬性進(jìn)行封裝。
思考:
- 通過PageHelper分頁插件進(jìn)行分頁查詢,在業(yè)務(wù)層方面使用前端傳入的DTO對象,將當(dāng)前頁碼和每頁的大小傳入分頁插件,通過Mapper進(jìn)行分頁查詢后,需要將指定數(shù)據(jù)傳回前端。
- 例如Total(總記錄數(shù)/總頁數(shù))和Records(當(dāng)前頁數(shù)據(jù)集合),此時page中已經(jīng)完成分頁查詢并動態(tài)計算出當(dāng)前頁面數(shù)據(jù)和總頁數(shù)記錄,只需get、set即可傳入PageResult
- PageResult是一個封裝分頁查詢結(jié)果的類
問題1:分頁查詢時,Records中的時間與前端需要的不符
- 方法1:通過@JsonFormat進(jìn)行時間格式規(guī)范化
@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”)
- 方法2:通過在WebMvcConfiguration中擴(kuò)展SpringMVC的消息轉(zhuǎn)換器,統(tǒng)一對日期類型進(jìn)行格式處理
/*** 擴(kuò)展Spring MVC框架的消息轉(zhuǎn)化器* @param converters*/protected void extendMessageConverters(List<HttpMessageConverter<?>> converters) {log.info("擴(kuò)展消息轉(zhuǎn)換器...");//創(chuàng)建一個消息轉(zhuǎn)換器對象MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();//需要為消息轉(zhuǎn)換器設(shè)置一個對象轉(zhuǎn)換器,對象轉(zhuǎn)換器可以將Java對象序列化為json數(shù)據(jù)converter.setObjectMapper(new JacksonObjectMapper());//將自己的消息轉(zhuǎn)化器加入容器中converters.add(0,converter);}
這里的JacksonObjectMapper是一個對象映射器,通過規(guī)范化定義時間格式確保日期和時間以特定的格式進(jìn)行序列化和反序列化
知識點:序列化和反序列化
在這個場景中,序列化和反序列化指的是將 Java 對象轉(zhuǎn)換為 JSON 格式的字符串(序列化),以及將 JSON 格式的字符串轉(zhuǎn)換回 Java 對象(反序列化)的過程。
序列化 (Serialization)
- 序列化是將 Java 對象的狀態(tài)信息轉(zhuǎn)換為可以存儲或傳輸?shù)男问降倪^程。在這個例子中,序列化是將 Java 對象轉(zhuǎn)換為 JSON 格式的字符串。
- 例如,如果你有一個 Employee 類,序列化會將 Employee 對象的信息轉(zhuǎn)換為一個 JSON 字符串,如下所示:
Employee employee = new Employee("John Doe", 30, LocalDate.now());// 序列化為 JSON
String json = objectMapper.writeValueAsString(employee);
System.out.println(json);
輸出的 JSON 字符串可能會類似于:
{"name": "John Doe","age": 30,"birthdate": "2024-08-14"
}
反序列化 (Deserialization)
反序列化是從序列化的表示形式中提取數(shù)據(jù),并重新創(chuàng)建 Java 對象的過程。在這個例子中,反序列化是從 JSON 字符串中恢復(fù)出原始的 Java 對象。例如:
String json = “{“name”:“John Doe”,“age”:30,“birthdate”:“2024-08-14”}”;
// 反序列化為 Java 對象
Employee employee = objectMapper.readValue(json, Employee.class);
System.out.println(employee.getName());
這將輸出:
John Doe
總結(jié)
序列化:將 Java 對象轉(zhuǎn)換為 JSON 字符串。反序列化:將 JSON 字符串轉(zhuǎn)換回 Java 對象。
自定義序列化器和反序列化器:通過 SimpleModule 添加自定義邏輯來處理特定類型的序列化和反序列化,例如日期和時間類型。
1.3修改員工時信息回顯
通過點擊當(dāng)前員工修改按鈕,獲取該員工id,通過id查詢數(shù)據(jù)庫
1.4 修改員工信息
這里可以使用先前啟用禁用員工賬號的更新方法,萬金油,通過參數(shù)值是null來排除修改的值,有值才會更修改,null直接跳過
省略類似的CRUD功能
2.刪除分類注意事項
- 在刪除分類之前判斷是否有關(guān)聯(lián)的菜品或套餐是為了保證數(shù)據(jù)的一致性和完整性。
- 如果直接刪除一個被其他數(shù)據(jù)引用的分類,可能會導(dǎo)致數(shù)據(jù)庫中的數(shù)據(jù)丟失或者出現(xiàn)“懸掛”引用的情況,即某些記錄失去了與之相關(guān)的分類信息。
在實際應(yīng)用中,這樣的邏輯是非常重要的,因為它可以幫助開發(fā)者確保應(yīng)用程序的數(shù)據(jù)一致性。例如,在餐飲管理系統(tǒng)中,如果一個分類被菜品或套餐引用,那么刪除該分類可能會導(dǎo)致用戶無法查看到這些菜品或套餐所屬的分類信息,從而影響用戶體驗和系統(tǒng)的可靠性。因此,在刪除分類之前進(jìn)行這樣的檢查是非常必要的。
2.1公共字段自動填充
使用AOP切面編程,實現(xiàn)功能增強(qiáng),來完成公共字段自動填充功能。
實現(xiàn)思路
在實現(xiàn)公共字段自動填充,也就是在插入或者更新的時候為指定字段賦予指定的值,使用它的好處就是可以統(tǒng)一對這些字段進(jìn)行處理,避免了重復(fù)代碼。在上述的問題分析中,我們提到有四個公共字段,需要在新增/更新中進(jìn)行賦值操作, 具體情況如下:
序號 | 字段名 | 含義 | 數(shù)據(jù)類型 | 操作類型 |
---|---|---|---|---|
1 | create_time | 創(chuàng)建時間 | datetime | insert |
2 | create_user | 創(chuàng)建人id | bigint | insert |
3 | update_time | 修改時間 | datetime | insert、update |
4 | update_user | 修改人id | bigint | insert、update |
實現(xiàn)步驟:
1). 自定義注解 AutoFill,用于標(biāo)識需要進(jìn)行公共字段自動填充的方法
2). 自定義切面類 AutoFillAspect,統(tǒng)一攔截加入了 AutoFill 注解的方法,通過反射為公共字段賦值
@Slf4j
@Aspect //定義一個切面
@Component
public class AutoFillAspect {/*** 切入點*/@Pointcut("execution(* com.sky.mapper.*.*(..)) && @annotation(com.sky.annotation.AutoFill)")public void autoFillPointCut(){}/*** 前置通知,在通知中進(jìn)行公共字段賦值*/@Before("autoFillPointCut()")public void autoFill(JoinPoint joinPoint){log.info("開始進(jìn)行公共字段的自動填充...");//獲取到當(dāng)前被攔截的方法上和數(shù)據(jù)庫操作類型MethodSignature signature = (MethodSignature)joinPoint.getSignature(); //方法簽名對象AutoFill autoFill = signature.getMethod().getAnnotation(AutoFill.class); //獲得方法上的注解對象OperationType operationType = autoFill.value(); //獲取數(shù)據(jù)庫操作類型//獲取到當(dāng)前被攔截的方法的參數(shù)--實體對象Object[] args = joinPoint.getArgs();if(args==null|| args.length==0){return; //判斷如果你更新修改的時候里面沒有值,我就不要你了}Object entity = args[0];//準(zhǔn)備賦值的數(shù)據(jù)LocalDateTime now = LocalDateTime.now();Long currentId = BaseContext.getCurrentId();//根據(jù)當(dāng)前不同的操作類型,為對應(yīng)的屬性通過反射來賦值if(operationType == OperationType.INSERT){try {//為4個公共字段賦值Method setCreateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);Method setCreateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);//通過反射為對象屬性賦值setCreateTime.invoke(entity,now);setCreateUser.invoke(entity,currentId);setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,currentId);}catch (Exception e){e.printStackTrace();;}}else if(operationType == OperationType.UPDATE){try {//為2個公共字段賦值Method setUpdateTime = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);Method setUpdateUser = entity.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);//通過反射為對象屬性賦值setUpdateTime.invoke(entity,now);setUpdateUser.invoke(entity,currentId);}catch (Exception e){e.printStackTrace();;}}}
}
理解:
- 通過給
AutoFillAspect
添加注解 @Aspect 說明這是一個切面類,這個 @Pointcut 是一個切入點,也就是作為攔截的,而execution
(定義切入點表達(dá)式)是設(shè)置攔截的范圍是 com.sky.mapper 包下的所有類和所有方法, @annotation(com.sky.annotation.AutoFill) 表示它只攔截帶有AutoFill
注解類型的方法。 - 此時,因為公共字段填充字段是需要在修改字段或者新增字段之前完成填充,所以是前置通知 @before , @Before(“autoFillPointCut()”) 這個參數(shù)就是告訴你,這個前置通知的方法是需要找范圍和要求的,而這個范圍就是這個切入點的范圍和要求。
- 通過執(zhí)行 autoFill 方法,這里參數(shù)是
joinPoint
,我認(rèn)為這是獲取的一個切入點,通過getSignature
獲取當(dāng)前切入點的方法簽名,例如 update 方法,這只是方法簽名,想要真正獲取到方法上的注解,就需要進(jìn)一步獲取到該方法上的注解,所以使用getMethod
以及getAnnotation
去指定獲取 AutoFill 自定義注解。 - 接下來獲取到這個方法上的 AutoFill 注解了,但是我只需要這個數(shù)據(jù)庫操作參數(shù),所以我們可以發(fā)現(xiàn)它的
value
值正好就是數(shù)據(jù)庫操作類型, autoFill 是以鍵值對存儲這個操作類型的。 - 通過 getArgs 獲取
joinPoint
的實體對象,此時這個實體對象其實就是前端獲取到你輸入的字段,比如新增的字段或者修改的字段,把它存進(jìn) args 里面,判斷它非空且長度非零,然后將第一個元素賦給一個 Object 類的實體(其實里頭最多就一個元素因為不考慮并發(fā)),然后進(jìn)行準(zhǔn)備賦值數(shù)據(jù),使用 LocalDateTime.now() 和 BaseContext.getCurrentId() 來獲取當(dāng)前時間和當(dāng)前操作人的ID。 - 判斷操作類型,通過先前獲取的數(shù)據(jù)庫操作類型與 insert 和 update 進(jìn)行比對。例子中是 update ,所以直接進(jìn)入 else if 。這里通過反射機(jī)制獲取實體類中的方法:自己定義一個
Method
對象setUpdateTime
,這個對象代表 entity 對象內(nèi)的一個方法。 - 這個方法里面有兩個參數(shù): AutoFillConstant.SET_UPDATE_TIME 表示方法的名字,即 setUpdateTime 。 LocalDateTime.class 表示這個方法接受一個 LocalDateTime 類型的參數(shù)。通過
entity.getClass()
獲取當(dāng)前運行的類。再通過getDeclaredMethod
方法,我們獲取到了當(dāng)前實體類中聲明的方法 setUpdateTime 。 - setUpdateTime.invoke(entity, now) 反射機(jī)制就反著看,
invoke
方法允許我們在運行時動態(tài)地調(diào)用對象的方法。我們實際上是調(diào)用了 entity 對象的 setUpdateTime 方法,并將當(dāng)前時間 now 作為參數(shù)傳遞給這個方法。
3). 在 Mapper 的方法上加入 AutoFill 注解
2.2文件上傳功能(阿里云OSS存儲)
配置yaml文件,配置阿里云OSS的相關(guān)屬性,包括endpoint、accessKeyId、accessKeySecret和bucketName,注意區(qū)分開發(fā)環(huán)境和生產(chǎn)環(huán)境的配置差異。創(chuàng)建AliOssProperties類,用@ConfigurationProperties
注解來自動讀取這些配置以便后期用
其次,創(chuàng)建一個oss對象配置類,用來返回一個文件上傳對象,這個對象里面存儲了阿里云對應(yīng)配置的屬性。
接著創(chuàng)建一個AliOssUtil
工具類,里面有一個upload方法里面?zhèn)z參數(shù):字節(jié)數(shù)組和目標(biāo)文件名,只需要搞懂,這個方法可以創(chuàng)建一個ossclient
實例,將oss屬性參數(shù)存進(jìn)去,因為存儲路徑由https://BucketName.Endpoint/ObjectName組成的,所以通過bucketName和endpoint以及最后的objectName獲取到文件訪問路徑。
最后,在控制層進(jìn)行上傳業(yè)務(wù),通過對應(yīng)接口mapping進(jìn)行上傳操作,需要注意,在上傳文件之前,需要注意文件名需要保證唯一性,通常的做法是使用UUID生成新的文件名,截取文件的后綴名,并保留原有的文件擴(kuò)展名最后進(jìn)行upload操作。
業(yè)務(wù)流程:
- 從
MultipartFile
對象中讀取文件數(shù)據(jù),使用getBytes()
方法獲取文件的字節(jié)流。
生成唯一文件名:截取文件原始名稱的擴(kuò)展名,通常使用UUID來保證唯一性,生成一個新的文件名。 - 調(diào)用
aliOssUtil.upload(file.getBytes(), name)
方法上傳文件。這里file.getBytes()
獲取文件字節(jié)流,name
是新生成的唯一文件名。 - 上傳成功,返回文件的訪問路徑,路徑由
https://BucketName.Endpoint/ObjectName
組成。
2.3新增菜品功能注意
思考:初步分析新增菜品分為三部分:文件上傳、新增菜品和新增菜品口味,所以新增的是兩個表的數(shù)據(jù)
業(yè)務(wù)層邏輯是向菜品表和口味表插入數(shù)據(jù),其中需要注意:
- DTO的類型轉(zhuǎn)換和數(shù)據(jù)拷貝,插入菜品表是一行數(shù)據(jù)一行數(shù)據(jù)插入。
- 口味是有多種口味在一個屬性內(nèi),就需要插入n條數(shù)據(jù)并判斷非空。
- 口味表的dish_id就是菜品表的id,這里通過數(shù)據(jù)庫的自動生成策略獲取新插入菜品的ID,再將此ID設(shè)置到每個口味項的dishId上,并批量插入口味數(shù)據(jù)到dish_flavor表。
- 使用了@Transactional注解保證事務(wù)一致性,即菜品和口味的插入操作要么全部成功,要么全部失敗。
DishMapper
接口定義了插入菜品數(shù)據(jù)的方法,并使用@AutoFill注解自動填充創(chuàng)建時間和更新時間等字段。對應(yīng)XML文件提供了SQL語句實現(xiàn)插入操作,并啟用useGeneratedKeys
返回自動生成的主鍵。
- useGeneratedKeys=“true”: 意思是Mybatis在執(zhí)行完插入操作后,使用數(shù)據(jù)庫自動生成的主鍵值填充返回的對象。
- keyProperty=“id”: 指定將自動生成的主鍵值填充到對象的哪個屬性中。在這個例子中,id 是對象的主鍵屬性名稱。
DishFlavorMapper
接口用于操作口味數(shù)據(jù),定義了批量插入口味數(shù)據(jù)的方法,其XML文件中通過foreach實現(xiàn)了一次性插入多條口味記錄的功能。這里的separator表示分隔符為逗號,collection 屬性用于指定集合名稱,item表示集合中的每一個元素的別名。
DTO/VO/DO/PO理解:
通俗的解釋:領(lǐng)域模型中的實體類分為四種模型:VO、DTO、DO和PO
VO:(View Object):視圖對象,用于展示層。
DTO(Data Transfer Object):數(shù)據(jù)傳輸對象在后端,它的存在形式是java對象,也就是在controller里面定義的請求參數(shù)
DO(Domain Object):領(lǐng)域?qū)ο?/strong>,它用來接收數(shù)據(jù)庫對應(yīng)的實體,是一種抽象化的數(shù)據(jù)狀態(tài),介于數(shù)據(jù)庫與業(yè)務(wù)邏輯之間
PO(Persistent Object):持久化對象,它跟持久層(通常是關(guān)系型數(shù)據(jù)庫)的數(shù)據(jù)結(jié)構(gòu)形成一一對應(yīng)的映射關(guān)系。
BO (Business Object) :業(yè)務(wù)對象 BO通常位于中間層或業(yè)務(wù)邏輯層。BO支持序列化和反序列化,用來是把業(yè)務(wù)邏輯封裝為一個對象
方向:后端–>前端
VO:前端頁面顯示使用的數(shù)據(jù),是后端傳遞給前端的。
方向:前端–>后端
DTO:前端調(diào)用后端接口的時候傳遞給后端
DO:controller中接收到DTO之后,新建一個DO傳遞給service,
PO:service接收到傳遞的DO之后,轉(zhuǎn)換成一個PO,傳給mapper的方法,進(jìn)行持久化處理。
BO:用于微服務(wù)之間傳輸數(shù)據(jù)
2.4分頁查詢sql
左外連接:返回的是VO對象,用到了左外連接,連接條件是將category表中id字段等同為dish表中的category_id字段,并根據(jù)VO對象屬性參數(shù)查詢dish表和category的name字段。
動態(tài) SQL:使用 標(biāo)簽動態(tài)構(gòu)建 WHERE 子句,根據(jù)傳入的參數(shù)決定是否添加過濾條件。
2.5刪除菜品
業(yè)務(wù)規(guī)則:
- 可以一次刪除一個菜品,也可以批量刪除菜品
- 起售中的菜品不能刪除
- 被套餐關(guān)聯(lián)的菜品不能刪除
- 刪除菜品后,關(guān)聯(lián)的口味數(shù)據(jù)也需要刪除掉
4.1.3 表設(shè)計
在進(jìn)行刪除菜品操作時,會涉及到以下三張表。
注意事項:
- 在dish表中刪除菜品基本數(shù)據(jù)時,同時,也要把關(guān)聯(lián)在dish_flavor表中的數(shù)據(jù)一塊刪除。
- setmeal_dish表為菜品和套餐關(guān)聯(lián)的中間表。
- 若刪除的菜品數(shù)據(jù)關(guān)聯(lián)著某個套餐,此時,刪除失敗。
- 若要刪除套餐關(guān)聯(lián)的菜品數(shù)據(jù),先解除兩者關(guān)聯(lián),再對菜品進(jìn)行刪除。
刪除代碼優(yōu)化:
2.5新增菜品接口
1.根據(jù)id查詢菜品:
2.修改菜品
3.新增套餐
接口設(shè)計(共涉及到4個接口):
- 根據(jù)類型查詢分類(已完成)
- 根據(jù)分類id查詢菜品
- 圖片上傳(已完成)
- 新增套餐
業(yè)務(wù)規(guī)則:
- 套餐名稱唯一
- 套餐必須屬于某個分類
- 套餐必須包含菜品
- 名稱、分類、價格、圖片為必填項
- 添加菜品窗口需要根據(jù)分類類型來展示菜品
- 新增的套餐默認(rèn)為停售狀態(tài)
4.SpringCache緩存
其中store是cacheManager緩存,默認(rèn)存儲的一個HashMap類型數(shù)據(jù)