醫院管理API & 服務註冊與發現
尚硅谷Spring筆記-08

醫院管理系統

  • hospital-manage醫院管理系統,是一個已經完成的模組,功能是提供給醫院方操作,讓醫院管理人員上傳醫院的科室、排班資訊等等的一個小系統
  • 我們的項目是預約掛號平台,要實現求診者與醫院的配對
  • 現在就要模擬實際業務,參考提供的API文檔完成對接

連接MongoDB

練習點,這邊把醫院方上傳的資料存到mongoDB

  • 回到service-hosp模組,先去application.properties設定資料庫連線地址
  • 建立接口繼承MongoRepository
@Repository
public interface HospitalRepository extends MongoRepository<Hospital, String> {}
  • 建立HospitalServiceImpl接口與實現類、ApiController開始對接API

上傳醫院

  • 控制層知識點:
    • request.getParameterMap()拿到的是String[],最好轉成Obj方便後續操作
    • 這邊用了base64編碼傳了一張圖片,"+“變成了” “空白要還原
    • 之後用<img src="data:image/png;base64,編碼串"/>可以把圖片還原顯示出來
@ApiOperation("上傳醫院")
@PostMapping("saveHospital")
public Result saveHospital(HttpServletRequest request) {
    Map<String, String[]> parameterMap = request.getParameterMap();
    // 調用工具類把map中的string[]轉成obj
    Map<String, Object> stringObjectMap = HttpRequestHelper.switchMap(parameterMap);
    // base64編碼傳送過程中把"+"變成了" "空白,要轉換回來
    String logoData = (String) stringObjectMap.get("logoData");
    logoData = logoData.replaceAll(" ", "+");
    stringObjectMap.put("logoData", logoData);
    return hospitalService.save(stringObjectMap) ? Result.ok() : Result.build(230, "醫院編號已存在,修改請聯絡工程師");
}
  • 服務層
    • 他教學這邊用alibaba.fastjson包把stringObjectMap轉成JSON,再從JSON轉回Bean,把我看暈了
    • 幹嘛不乾脆用apache.commons.beanutilspopulate
    • 後來發現mybatis+自帶BeanUtils.mapToBean也可以,還少導依賴
    • MD5加密跟修改上傳也把我看暈了,他教學這裡有點瑕疵,反正原理能懂,暫時跳過吧
@Override
public boolean save(Map<String, Object> stringObjectMap) {
    // 將map轉成Hospital實體類物件
    // String string = JSONObject.toJSONString(stringObjectMap);
    // Hospital hospital = JSONObject.parseObject(string, Hospital.class);
    Hospital hospital = BeanUtils.mapToBean(stringObjectMap, Hospital.class);
    // 判斷是否已存在
    String hoscode = hospital.getHoscode();
    Hospital hospitalExist = hospitalRepository.getHospitalByHoscode(hoscode);
    /*  TODO 這裡有bug,他設定hoscode是唯一,但是又不標記成mongo的Id,物件沒有mongo的Id作為屬性,
    就沒辦法靠同Id視為更新的save去更新,永遠只會說重複不給存報錯,如果拿掉不重複標記,
    就變成一直存成新物件,修這個要從model層去改
    而MD5加密驗證的部分,覺得這邊他教的太混亂,應該寫在service層
    原理懂就好,就醫院方API把key用MD5加密傳過來,平台方去醫院設定mysql也抓那個key
    然後執行一樣的MD5加密,驗證是否相同,利用MD5不可逆推(暴力哈希碰撞除外)特性
    實現安全驗證
    */
    if (hospitalExist == null) {
        // 新物件
        hospital.setStatus(0);
        hospital.setCreateTime(new Date());
        hospital.setUpdateTime(new Date());
        hospital.setIsDeleted(0);
        hospitalRepository.save(hospital);
        return true;
    } else {
        // 已存在,要修改
        return false;
    }
}

查詢醫院

  • 毫無難度,利用Repository自動生成的方法,用Hoscode往mongoDB查找出對應的Hospital物件丟回去就完了
@ApiOperation("查詢醫院")
@PostMapping("hospital/show")
public Result showHospital(HttpServletRequest request) {
    Map<String, String[]> parameterMap_0 = request.getParameterMap();
    // 調用工具類把map中的string[]轉成obj
    Map<String, Object> parameterMap = HttpRequestHelper.switchMap(parameterMap_0);
    String hoscode = (String) parameterMap.get("hoscode");
    return Result.ok(hospitalService.showHospital(hoscode));
}

科室

  • 控制層,注意Page是spring框架的
    @ApiOperation("上傳科室")
    @PostMapping("saveDepartment")
    public Result saveDepartment(HttpServletRequest request) {
        Map<String, String[]> parameterMap_0 = request.getParameterMap();
        Map<String, Object> parameterMap = HttpRequestHelper.switchMap(parameterMap_0);
        return departmentService.save(parameterMap) ? Result.ok() : Result.build(230, "醫院科室編號已存在,修改請聯絡工程師");
    }

    @ApiOperation("查詢科室")
    @PostMapping("department/list")
    public Result findDepartment(HttpServletRequest request) {
        Map<String, String[]> parameterMap_0 = request.getParameterMap();
        Map<String, Object> parameterMap = HttpRequestHelper.switchMap(parameterMap_0);
        // 獲取醫院編號
        String hoscode = (String) parameterMap.get("hoscode");
        // 獲取當前頁 和 每頁記錄數,沒指定(為空)就是1跟10
        int page = StringUtils.isEmpty(parameterMap.get("page")) ? 1 : Integer.parseInt((String) parameterMap.get(
                "page"));
        int limit = StringUtils.isEmpty(parameterMap.get("limit")) ? 10 : Integer.parseInt((String) parameterMap.get(
                "limit"));

        DepartmentQueryVo departmentQueryVo = new DepartmentQueryVo();
        departmentQueryVo.setHoscode(hoscode);
        // 調用service方法
        Page<Department> pageModel = departmentService.findPageDepartment(page, limit, departmentQueryVo);
        return Result.ok(pageModel);
    }

    @ApiOperation("刪除科室")
    @PostMapping("department/remove")
    public Result removeDepartment(HttpServletRequest request) {
        Map<String, String[]> parameterMap_0 = request.getParameterMap();
        Map<String, Object> parameterMap = HttpRequestHelper.switchMap(parameterMap_0);
        // 醫院編號 和 科室編號
        String hoscode = (String) parameterMap.get("hoscode");
        String depcode = (String) parameterMap.get("depcode");
        if (depcode == null || hoscode == null) {
            return Result.build(230, "醫院或科室編號不存在");
        }
        departmentService.remove(hoscode, depcode);
        return Result.ok();
    }
  • 服務層,知識重點在條件查詢與頁數
@Service
public class DepartmentServiceImpl implements DepartmentService {
    @Autowired
    private DepartmentRepository departmentRepository;

    @Override
    public boolean save(Map<String, Object> parameterMap) {
        Department department = BeanUtils.mapToBean(parameterMap, Department.class);
        // 根據醫院編號 和 科室編號查詢
        Department departmentExist = departmentRepository.
                getDepartmentByHoscodeAndDepcode(department.getHoscode(), department.getDepcode());
        if (departmentExist == null) {
            department.setCreateTime(new Date());
            department.setUpdateTime(new Date());
            departmentRepository.save(department);
            return true;
        }
        return false;
    }

    @Override
    public Page<Department> findPageDepartment(int page, int limit, DepartmentQueryVo departmentQueryVo) {
        // 創建Pageable,設置當前頁和每頁記錄數,0是第一頁
        Pageable pageable = PageRequest.of(page - 1, limit);
        // 查詢用VO,所以要先造一個department去承接屬性
        Department department = new Department();
        org.springframework.beans.BeanUtils.copyProperties(departmentQueryVo, department);
        // 創建Example設定查詢條件
        ExampleMatcher matcher = ExampleMatcher.matching()
                .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING)
                .withIgnoreCase(true);
        Example<Department> example = Example.of(department, matcher);

        Page<Department> all = departmentRepository.findAll(example, pageable);
        return all;
    }

    @Override
    public void remove(String hoscode, String depcode) {
        // 根據醫院編號 和 科室編號查詢
        Department department = departmentRepository.getDepartmentByHoscodeAndDepcode(hoscode, depcode);
        if (department != null) {
            // 調用方法刪除
            departmentRepository.deleteById(department.getId());
        }
    }
}

班表

  • 類似的就不重複,小結操作MongoDB的流程:
    • 建立對應的Bean,有要條件查詢的可以建立VO
      • 後來查了一下這算是開發規範,前端用VO後端用POJO
    • 造xxxRepository介面繼承MongoRepository<bean, String>,記得註解@Repository
    • 造service層介面與實現類,實現類記得註解@Service
    • 造controller對接API,oooMapping(“URL”)

服務註冊中心

  • 接著要繼續完成預約平台的後台系統,這邊需要橫跨多個模組查詢資料,例如醫院設定 + 字典檔模組
  • 微服務架構之下,當啟動多個Spring服務要讓他們彼此能溝通,就需要依靠服務註冊與發現
  • 常見的組合有:
    • Spring Cloud Eureka + Spring Cloud Config
    • Zookeeper + dubbo
    • Consul
    • 這邊使用Nacos,優勢在於註冊與配置二合一,簡單

Nacos前置

用docker啟動

# 拉映像
docker pull nacos/nacos-server

# 啟動容器
docker run --env MODE=standalone --name nacos -d -p 8848:8848 nacos/nacos-server
  • --env 配置環境
  • MODE=standalong 單節點
  • --name nacos 映象名稱為nacos
  • -d 背景啟動
  • 預設網址為localhost:8848/nacos,帳號密碼都是nacos
  • 通常為了持久化會搭配資料庫啟動,把服務資訊寫到資料庫例如mysql中,這邊先用內建的

Java部分

  • pom.xml
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
  • application.properties
# nacos服務地址
spring.cloud.nacos.discovery.server-addr=127.0.0.1:8848
  • 啟動類上加註解
    • 小竅門: 在IDEA下面Service那邊點兩下就能快速找到
@EnableDiscoveryClient
  • 設定完重啟服務就完成註冊了,可以在列表中看到

後台模組

  • 接著回頭到平台的後台管理,在後台要能看到醫院方上傳來的科室、班表資料那些,先開始搭建後端
  • 他教學的模組分類與命名真的忍不住要吐槽,甚麼hos、hosp、hospital、hospitalSet全都寫在一起辨識度有夠差,等整個搭完我一定要全部重新理一遍
  • 不行了,這個項目差點把我做吐了,先停在P84,決定先去練谷粒商城,有機會再回來填坑吧

上次修改於 2022-01-18