【Java】專案採用的設計模式案例
先說一下業務需要:
做電競酒店後臺系統,第一期功能有一個服務申請的訊息通知功能
就是酒店使用者在小程式點選服務功能,可以在後臺這邊查到使用者的服務需要
原本設計是隻需要一張表儲存這些訊息,但是考慮設計是SAAS結構(所有酒店資料歸於一張表,根據商家ID區分各自家的資料)
資料量可能過大,組長決定把表拆分,一個服務型別一張表
這裡有7個型別,就有七張訊息通知表
-- aisw_e_service_renewal_msg CREATE TABLE `aisw_e_service_renewal_msg` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `ROOM_NO`varchar(10) DEFAULT NULL COMMENT '房間號', `MERCHANT_ID` int(11) DEFAULT NULL COMMENT '商家ID', `SERVICE_TYPE` varchar(50) DEFAULT NULL COMMENT '服務型別(與字典表AISW_MERCHANT_DICT表關聯)', `USER_ID` int(11) DEFAULT NULL COMMENT '使用者ID(與AISW_USER表關聯)', `RENEWAL_TYPE` tinyint(1) DEFAULT NULL COMMENT '續房型別(1-續住此房間 2-換房續住)', `RENEWAL_DAY` int(11) DEFAULT NULL COMMENT '續住天數', `ACCEPT_STATUS` tinyint(1) DEFAULT '0' COMMENT '受理狀態(0-未受理 1-已受理)', `STATUS` tinyint(1) DEFAULT '1' COMMENT '狀態( 0-無效 1-有效)', `CREATE_DATE` datetime DEFAULT NULL COMMENT '建立時間', `UPDATE_DATE` datetime DEFAULT NULL COMMENT '更新時間', `CREATE_BY`varchar(50) DEFAULT NULL COMMENT '建立人', `UPDATE_BY` varchar(50) DEFAULT NULL COMMENT '更新人', `REMARKS` text COMMENT '備註', PRIMARY KEY (`ID`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=13 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='電競酒店續住服務通知表'; -- aisw_e_service_clean_msg CREATE TABLE `aisw_e_service_clean_msg` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `ROOM_NO` varchar(10) DEFAULT NULL COMMENT '房間號', `MERCHANT_ID` int(11) DEFAULT NULL COMMENT '商家ID', `SERVICE_TYPE` varchar(50) DEFAULT NULL COMMENT '服務型別(與字典表AISW_MERCHANT_DICT表關聯)', `USER_ID` int(11) DEFAULT NULL COMMENT '使用者ID(與AISW_USER表關聯)', `CLEAN_DATE` varchar(50) DEFAULT NULL COMMENT '清掃時間', `ACCEPT_STATUS` tinyint(1) DEFAULT '0' COMMENT '受理狀態(0-未受理 1-已受理)', `STATUS` tinyint(1) DEFAULT '1' COMMENT '狀態(0-無效 1-有效)', `CREATE_DATE` datetime DEFAULT NULL COMMENT '建立時間', `UPDATE_DATE` datetime DEFAULT NULL COMMENT '更新時間', `CREATE_BY` varchar(50) DEFAULT NULL COMMENT '建立人', `UPDATE_BY` varchar(50) DEFAULT NULL COMMENT '更新人', `REMARKS` text COMMENT '備註', PRIMARY KEY (`ID`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='電競酒店清掃服務通知表'; -- aisw_e_service_delivery_msg CREATE TABLE `aisw_e_service_delivery_msg` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `ROOM_NO` varchar(10) DEFAULT NULL COMMENT '房間號', `MERCHANT_ID` int(11) DEFAULT NULL COMMENT '商家ID', `SERVICE_TYPE` varchar(50) DEFAULT NULL COMMENT '服務型別(與字典表AISW_MERCHANT_DICT表關聯)', `USER_ID` int(11) DEFAULT NULL COMMENT '使用者ID(與AISW_USER表關聯)', `DELIVERY_TYPE` text COMMENT '配送物品型別(與AISW_MERCHANT_DICT表關聯),多個以逗號隔開', `DELIVERY_NUMS` text COMMENT '配送物品數量(以逗號分隔)', `ACCEPT_STATUS` tinyint(1) DEFAULT '0' COMMENT '受理狀態(0-未受理 1-已受理)', `STATUS` tinyint(1) DEFAULT '1' COMMENT '狀態(0-無效 1-有效)', `CREATE_DATE` datetime DEFAULT NULL COMMENT '建立時間', `UPDATE_DATE` datetime DEFAULT NULL COMMENT '更新時間', `CREATE_BY` varchar(50) DEFAULT NULL COMMENT '建立人', `UPDATE_BY` varchar(50) DEFAULT NULL COMMENT '更新人', `REMARKS` text COMMENT '備註', PRIMARY KEY (`ID`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='電競酒店物品配送服務通知表'; -- aisw_e_service_fault_msg CREATE TABLE `aisw_e_service_fault_msg` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `ROOM_NO` varchar(10) DEFAULT NULL COMMENT '房間號', `MERCHANT_ID` int(11) DEFAULT NULL COMMENT '商家ID', `SERVICE_TYPE` varchar(50) DEFAULT NULL COMMENT '服務型別(與字典表AISW_MERCHANT_DICT表關聯)', `USER_ID` int(11) DEFAULT NULL COMMENT '使用者ID(與AISW_USER表關聯)', `FAULT_TYPE` text COMMENT '保修物品(與AISW_MERCHANT_DICT表關聯),多個以逗號隔開', `ACCEPT_STATUS` tinyint(1) DEFAULT '0' COMMENT '受理狀態(0-未受理 1-已受理)', `STATUS` tinyint(1) DEFAULT '1' COMMENT '狀態(0-無效 1-有效)', `CREATE_DATE` datetime DEFAULT NULL COMMENT '建立時間', `UPDATE_DATE` datetime DEFAULT NULL COMMENT '更新時間', `CREATE_BY` varchar(50) DEFAULT NULL COMMENT '建立人', `UPDATE_BY` varchar(50) DEFAULT NULL COMMENT '更新人', `REMARKS` text COMMENT '備註', PRIMARY KEY (`ID`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='電競酒店故障保修服務通知表'; -- aisw_e_service_invoice_msg CREATE TABLE `aisw_e_service_invoice_msg` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `ROOM_NO` varchar(10) DEFAULT NULL COMMENT '房間號', `MERCHANT_ID` int(11) DEFAULT NULL COMMENT '商家ID', `SERVICE_TYPE` varchar(50) DEFAULT NULL COMMENT '服務型別(與字典表AISW_MERCHANT_DICT表關聯)', `USER_ID` int(11) DEFAULT NULL COMMENT '使用者ID(與AISW_USER表關聯)', `INVOICE_TYPE` tinyint(1) DEFAULT NULL COMMENT '發票型別(1-單位 2-個人)', `INVOICE_TITLE` varchar(100) DEFAULT NULL COMMENT '發票抬頭', `INVOICE_TAX_NUMBER` varchar(50) DEFAULT NULL COMMENT '發票稅號', `INVOICE_COMPANY_ADDRESS` varchar(100) DEFAULT NULL COMMENT '單位地址', `INVOICE_TELEPHONE` varchar(11) DEFAULT NULL COMMENT '電話號碼', `INVOICE_BANK_ACCOUNT` varchar(50) DEFAULT NULL COMMENT '開戶銀行賬戶', `INVOICE_BANK_NAME` varchar(50) DEFAULT NULL COMMENT '開戶銀行名稱', `ACCEPT_STATUS` tinyint(1) DEFAULT '0' COMMENT '受理狀態(0-未受理 1-已受理)', `STATUS` tinyint(1) DEFAULT '1' COMMENT '狀態( 0-無效 1-有效)', `CREATE_DATE` datetime DEFAULT NULL COMMENT '建立時間', `UPDATE_DATE` datetime DEFAULT NULL COMMENT '更新時間', `CREATE_BY` varchar(50) DEFAULT NULL COMMENT '建立人', `UPDATE_BY` varchar(50) DEFAULT NULL COMMENT '更新人', `REMARKS` text COMMENT '備註', PRIMARY KEY (`ID`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='電競酒店發票服務通知表'; -- aisw_e_service_one_checkout_msg CREATE TABLE `aisw_e_service_one_checkout_msg` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `ROOM_NO` varchar(10) DEFAULT NULL COMMENT '房間號', `MERCHANT_ID` int(11) DEFAULT NULL COMMENT '商家ID', `SERVICE_TYPE` varchar(50) DEFAULT NULL COMMENT '服務型別(與字典表AISW_MERCHANT_DICT表關聯)', `USER_ID` int(11) DEFAULT NULL COMMENT '使用者ID(與AISW_USER表關聯)', `ACCEPT_STATUS` tinyint(1) DEFAULT '0' COMMENT '受理狀態(0-未受理 1-已受理)', `STATUS` tinyint(1) DEFAULT '1' COMMENT '狀態(0-無效 1-有效)', `CREATE_DATE` datetime DEFAULT NULL COMMENT '建立時間', `UPDATE_DATE` datetime DEFAULT NULL COMMENT '更新時間', `CREATE_BY` varchar(50) DEFAULT NULL COMMENT '建立人', `UPDATE_BY` varchar(50) DEFAULT NULL COMMENT '更新人', `REMARKS` text COMMENT '備註', PRIMARY KEY (`ID`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='電競酒店一鍵退房服務通知表'; -- aisw_e_service_wake_msg CREATE TABLE `aisw_e_service_wake_msg` ( `ID` int(11) NOT NULL AUTO_INCREMENT, `MERCHANT_ID` int(11) DEFAULT NULL COMMENT '商家ID', `ROOM_NO` varchar(10) DEFAULT NULL COMMENT '房間號', `SERVICE_TYPE` varchar(50) DEFAULT NULL COMMENT '服務型別(與字典表AISW_MERCHANT_DICT表關聯)', `USER_ID` int(11) DEFAULT NULL COMMENT '使用者ID(與AISW_USER表關聯)', `WAKE_DATE` varchar(50) DEFAULT NULL COMMENT '叫醒時間', `WAKE_PHONE` int(11) DEFAULT NULL COMMENT '叫醒手機號', `IS_KNOCK` tinyint(1) DEFAULT NULL COMMENT '是否敲門叫醒(0-否 1-是)', `ACCEPT_STATUS` tinyint(1) DEFAULT '0' COMMENT '受理狀態(0-未受理 1-已受理)', `STATUS` tinyint(1) DEFAULT '1' COMMENT '狀態(0-無效 1-有效)', `CREATE_DATE` datetime DEFAULT NULL COMMENT '建立時間', `UPDATE_DATE` datetime DEFAULT NULL COMMENT '更新時間', `CREATE_BY` varchar(50) DEFAULT NULL COMMENT '建立人', `UPDATE_BY` varchar(50) DEFAULT NULL COMMENT '更新人', `REMARKS` text COMMENT '備註', PRIMARY KEY (`ID`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 ROW_FORMAT=DYNAMIC COMMENT='電競酒店叫醒服務通知表';
那我寫後臺就需要對應的寫7個介面 和下面這些檔案
Controller -> ServiceInterface -> ServiceImpl -> DaoMapper -> MapperXML
組長想了一個策略模式的辦法:
只需要一個Controller 和 一個策略介面,但是後面的服務還是需要各自實現
首先看組長寫的策略介面:
該介面只定義規範,7個資源共同遵守這個規範來獲取資源
package cn.ymcd.aisw.common.strategy; import cn.ymcd.aisw.common.strategy.dto.RoomDTO; import com.baomidou.mybatisplus.core.metadata.IPage; import java.util.List; /** * 策略介面 * * @author wangkun * @version 1.0 * @projectName aisw-api * @date 2022年3月16日 11:27 */ public interface Strategy { /** * 新增服務 * * @param jsonParam 服務訊息新增傳入引數 * @return * @author wangkun * @createTime 2020/9/18 11:28 */ boolean doServiceDataAdd(String jsonParam); /** * 查詢服務翻頁 * @param jsonParam * @return com.baomidou.mybatisplus.core.metadata.IPage<java.lang.Object> * @author cloud9 * @createTime 2022/3/17 14:29 * */ IPage<? extends Object> doServiceDataPage(String jsonParam); /** * 訊息受理更新 * @param jsonParam * @return boolean * @author cloud9 * @createTime 2022/3/23 19:18 * */ boolean doServiceDataUpdate(String jsonParam); }
然後組長對7個服務進行了列舉,繫結好實現類的名稱
只有Key 和 Value
package cn.ymcd.aisw.common.strategy; /** * 策略列舉 * * @author wangkun * @version 1.0 * @projectName aisw-api * @date 2022年3月16日 11:27 */ public enum StrategyEnum { OPERATE_MERCHANT_SERVICE_CONNECT_IMPL("eServiceConnectService", "連線wifi服務實現類"), OPERATE_MERCHANT_SERVICE_CLEAN_IMPL("eServiceCleanMsgService", "清潔服務實現類"), OPERATE_MERCHANT_SERVICE_DELIVERY_IMPL("eServiceDeliveryMsgService", "商品配送服務實現類"), OPERATE_MERCHANT_SERVICE_FAULT_IMPL("eServiceFaultService", "故障保修服務實現類"), OPERATE_MERCHANT_SERVICE_INVOICE_IMPL("eServiceInvoiceService", "發票服務實現類"), OPERATE_MERCHANT_SERVICE_RENEWAL_IMPL("eServiceRenewalService", "續住服務實現類"), OPERATE_MERCHANT_SERVICE_WAKE_IMPL("eServiceWakeService", "喚醒服務實現類"), OPERATE_MERCHANT_SERVICE_ONE_CHECKOUT_IMPL("eServiceOneCheckoutService", "一鍵退房服務實現類"), TEST_IMPL("testServiceImpl", "測試實現類別名"), ; private String value; private String msg; StrategyEnum(String value, String msg) { this.value = value; this.msg = msg; } public String getValue() { return value; } public String getMsg() { return msg; } }
然後是一個策略裝飾器?這個類叫StrategyFacade
最核心的呼叫在這裡實現:
1、內部成員宣告一個策略介面的Map容器
2、只存在一個這樣的帶參構造器,且該引數註解了自動裝配?
所有業務實現類註解@Service會存在Bean例項,這個註解的意思像是會把業務Bean都裝進來
Key 就是 業務Bean的名字 Value就是Bean的例項
3、獲取策略型別,根據提供的channelCode,返回對應的Bean名字
再經過Map容器獲取就能得到對應的Bean例項,再呼叫對應的業務實現類的方法
4、考慮翻頁返回的是各個自己的業務PO,所以泛型規約成通用的Object,更新一類的操作則完全可以使用Boolean統一
package cn.ymcd.aisw.common.strategy; import com.baomidou.mybatisplus.core.metadata.IPage; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; /** * create by wangkun * Date 2018/06/25 */ @Service public class StrategyFacade { private final Map<String, Strategy> strategyMap = new ConcurrentHashMap<>(); /** * 注入所有實現了Strategy介面的Bean * * @param strategyMap */ @Autowired public StrategyFacade(Map<String, Strategy> strategyMap) { this.strategyMap.clear(); strategyMap.forEach((k, v) -> this.strategyMap.put(k, v)); } /** * 新增服務資訊策略 * * @param channelCode 渠道code clean-清潔服務 delivery-商品配送服務 fault-故障保修服務 invoice-發票服務 renewal-續住服務 wake-喚醒服務 onecheckout-一鍵退房 * @param jsonParam 服務訊息傳入引數 * @return * @author wangkun * @createTime 2020/9/18 13:58 */ public boolean doServiceDataAdd(String channelCode, String jsonParam) { return strategyMap.get(getStrategyType(channelCode)).doServiceDataAdd(jsonParam); } /** * 查詢服務資訊策略 * * @param channelCode 渠道code clean-清潔服務 delivery-商品配送服務 fault-故障保修服務 invoice-發票服務 renewal-續住服務 wake-喚醒服務 onecheckout-一鍵退房 * @param jsonParam 服務訊息傳入引數 * @return * @author wangkun * @createTime 2020/9/18 13:58 */ public IPage<? extends Object> doServiceDataPage(String channelCode, String jsonParam) { return strategyMap.get(getStrategyType(channelCode)).doServiceDataPage(jsonParam); } /*** * 訊息受理更新 * @param channelCode * @param jsonParam * @return boolean * @author 戴知舟 * @createTime 2022/3/23 19:17 * */ public boolean doServiceDataUpdate(String channelCode, String jsonParam) { return strategyMap.get(getStrategyType(channelCode)).doServiceDataUpdate(jsonParam); } /** * 根據不同條件生成不同的策略(使用策略場景變化是需要修改) * * @param channelCode 渠道code * @return * @author wangkun * @createTime 2020/9/18 14:00 */ private static String getStrategyType(String channelCode) { switch (channelCode) { case "connect": //連線WIFI服務資訊處理 return StrategyEnum.OPERATE_MERCHANT_SERVICE_CONNECT_IMPL.getValue(); case "clean": //清潔服務資訊處理 return StrategyEnum.OPERATE_MERCHANT_SERVICE_CLEAN_IMPL.getValue(); case "delivery": //商品配送服務資訊處理 return StrategyEnum.OPERATE_MERCHANT_SERVICE_DELIVERY_IMPL.getValue(); case "fault": //故障保修服務資訊處理 return StrategyEnum.OPERATE_MERCHANT_SERVICE_FAULT_IMPL.getValue(); case "invoice": //發票服務資訊處理 return StrategyEnum.OPERATE_MERCHANT_SERVICE_INVOICE_IMPL.getValue(); case "renewal": //續住服務資訊處理 return StrategyEnum.OPERATE_MERCHANT_SERVICE_RENEWAL_IMPL.getValue(); case "wake": //喚醒服務資訊處理 return StrategyEnum.OPERATE_MERCHANT_SERVICE_WAKE_IMPL.getValue(); case "oneCheckout": //一鍵退房服務資訊處理 return StrategyEnum.OPERATE_MERCHANT_SERVICE_ONE_CHECKOUT_IMPL.getValue(); default: //測試類 return StrategyEnum.TEST_IMPL.getValue(); } } }
在Controller中的呼叫是這樣的:
根據約定好的channelCode來完成不同業務的呼叫
package cn.ymcd.aisw.room.controller; import cn.ymcd.aisw.common.Constant; import cn.ymcd.aisw.common.strategy.StrategyFacade; import cn.ymcd.aisw.common.strategy.dao.EServiceBasicDAO; import cn.ymcd.aisw.room.dto.EServiceMsgDTO; import cn.ymcd.comm.base.BaseController; import cn.ymcd.wss.util.json.JacksonUtil; import com.baomidou.mybatisplus.core.metadata.IPage; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.apache.http.util.Asserts; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; import java.util.Map; /** * aisw_e_service_msg 電競酒店服務通知表 前端控制器 * * @projectName: * @author:daizhizhou * @date:2022-03-14 * @version 1.0 */ @Api(tags = "客房服務 - 服務申請") @RestController @RequestMapping("${sys.path}/room/message") public class EServiceMsgController extends BaseController { @Autowired private StrategyFacade strategyFacade; @Autowired private EServiceBasicDAO eServiceBasicDAO; /** * /sys/room/message/page * @param json * @return com.baomidou.mybatisplus.core.metadata.IPage<cn.ymcd.aisw.room.dto.EServiceMsgDTO> * @author cloud9 * @createTime 2022/3/17 15:09 * { * "channelCode":"wake", * "jsonParam":{ * "merchantId":"1001", * "serviceType":"360000002", * "unionId":"o9K950jpDrqIRiR_4pNqT89RgEhs", * "cleanDate":"12:00", * "page": { * "size": 10, * "current: 1 * } * } */ @ApiOperation(value = "訊息通知查詢", notes = "訊息通知查詢") @PostMapping("/page") public IPage<Object> doServiceDataPage(@RequestBody String json) { Asserts.notEmpty(json, "傳入引數為空"); Map<String, Object> map = JacksonUtil.jsonToMap(json); String channelCode = map.get("channelCode").toString(); String jsonParam = map.get("jsonParam").toString(); return (IPage<Object>)strategyFacade.doServiceDataPage(channelCode, jsonParam); } }
其中的一個策略實現類,就是業務實現類:
注意@Service註解的名稱,通過這個Bean名稱決定Spring是否裝配進Map容器
package cn.ymcd.aisw.common.strategy.impl; import cn.ymcd.aisw.common.CommonExtendUtils; import cn.ymcd.aisw.common.strategy.Strategy; import cn.ymcd.aisw.common.strategy.dao.EServiceBasicDAO; import cn.ymcd.aisw.common.strategy.dao.EServiceCleanMsgDAO; import cn.ymcd.aisw.common.strategy.dao.UserDAO; import cn.ymcd.aisw.common.strategy.dto.EServiceCleanMsgDTO; import cn.ymcd.aisw.common.strategy.dto.UserDTO; import cn.ymcd.wss.util.json.JacksonUtil; import com.baomidou.mybatisplus.core.metadata.IPage; import org.springframework.stereotype.Service; import javax.annotation.Resource; /** * 清潔服務策略 * * @author wangkun * @version 1.0 * @projectName aisw-api * @date 2022年03月16日 10:47 */ @Service("eServiceCleanMsgService") public class EServiceCleanMsgServiceImpl implements Strategy { @Resource private EServiceCleanMsgDAO eServiceCleanMsgDAO; @Resource private UserDAO userDAO; @Resource private EServiceBasicDAO eServiceBasicDAO; /** * * @param jsonParam * @return com.baomidou.mybatisplus.core.metadata.IPage<cn.ymcd.aisw.common.strategy.dto.EServiceCleanMsgDTO> * @author cloud9 * @createTime 2022/3/17 15:01 * * { * "channelCode":"clean", * "jsonParam":{ * "merchantId":"1001", * "serviceType":"360000002", * "unionId":"o9K950jpDrqIRiR_4pNqT89RgEhs", * "cleanDate":"12:00", * "page": { * "size": 10, * "current: 1 * } * } * } * */ @Override public IPage<EServiceCleanMsgDTO> doServiceDataPage(String jsonParam) { EServiceCleanMsgDTO cleanMsgDTO = JacksonUtil.jsonToObject(jsonParam, EServiceCleanMsgDTO.class); // cleanMsgDTO.setMerchantId(merchantBasicDAO.getMerchantIdBySysUserId(LoginUserContext.getUser().getId())); cleanMsgDTO.setMerchantId(eServiceBasicDAO.getMerchantIdBySysUserId("user1001")); return eServiceCleanMsgDAO.cleanServiceMsgPage(cleanMsgDTO.getPage(), cleanMsgDTO); } }
最後是介面測試
裡面存在的一些問題:
有些業務操作是在小程式端的
小程式不需要翻頁和訊息查詢,只是提供給酒店使用者這些服務功能
但是這個業務所在的實現類必須也要按照策略介面實現方法,實現,但是不需要這個資源
有那麼一點點資源浪費