Java vue 养老系统源码详解:楼宇床位 care_space 与房态图 Spring Boot 实践

Java养老系统源码详解:楼宇床位 care_space 与房态图 Spring Boot 实践

在线演示 :管理端后台

标签:#Java #Spring Boot #MyBatis #养老系统 #建筑管理 #房态图 #care_bed


1. 前言:床号在 care_bed(床位表),不在 care_older(老人档案)

做机构版时,前台同事总问:档案里为啥不直接显示床号?我当时的做法是------老人主档 (care_older)只放人;住没住、住哪 交给入住单;这张床现在什么颜色 看 care_bed.status。楼宇、楼层、房间在 care_space 里级联,选完房型自动生成几张床。

这篇文章把 hub(中台模块) 里和楼宇、床位、房态图相关的代码串一遍:CareSpaceServiceImpl 快速盖楼、CareBedServiceImpl 判断能不能删床、房态 JSON 由 institution(机构模块) 的 CareInstitutionBedRoomStateBridge 来拼。你排查「快速添加失败、房态不刷新、在住床删不掉」时,按这个顺序看就行。

对照 演示站:建筑管理 · 楼宇设置 (四列、快速添加)→ 房型管理 (bedCount 决定每间几张床)→ 接待 · 房态图 (按楼层请求 GET /care/bed/room-state)。本地接口前缀 /admin-api/care/space/*、/admin-api/care/bed/*,请求头带上 tenant-id。

图1 楼宇设置 · 四列级联对应 care_space.type 0/1/2

图2 档案列表 · 床号不在 care_older,避免换床时改老人主档

2. 表结构:care_space(楼宇/楼层/房间)与 care_bed.status(床位状态 0~5)

care_space.type:0 楼宇、1 楼层、2 房间;房间可挂 layoutId(房型)。care_bed 冗余 buildingId/floorId/roomId,便于列表与报表,不必每次 join 树。

bash 复制代码
@TableName("care_bed")
public class CareBedDO extends CareOrgBaseDO {

    @TableId
    private Long id;
    private String name;
    private Long roomId;
    private Long floorId;
    private Long buildingId;
    private Long layoutId;
    private BigDecimal bedPrice;
    /** 1养老 2护理 */
    private Integer bedAttr;
    /** 0空闲 1预订 2在住 3请假占用 4维修 5试住 */
    private Integer status;
    private Long olderId;
    private Integer sort;
}
status 含义 房态图典型色 institution 谁写入
0 空闲 灰/空床 退订、腾床、释床
1 预订 绿订 CareReserveServiceImpl
2 在住 蓝在床 CareCheckinServiceImpl.assign
3 请假占用 橙 CareLeaveServiceImpl
5 试住 专用 CareTryliveServiceImpl
bash 复制代码
@TableName("care_space")
public class CareSpaceDO extends CareOrgBaseDO {

    @TableId
    private Long id;
    private String name;
    private Long parentId;
    private String ancestors;
    /** 0楼宇 1楼层 2房间 */
    private Integer type;
    private Long layoutId;
    private Long areaId;
}
  • ancestors :0,buildingId,floorId 链,建床时解析出 buildingId/floorId 写入 bed 冗余列。
  • layoutId :仅房间有效;非房间类型在 Service 里 clearLayoutIfNotRoom 置空。

房态按楼层拉床时走 Mapper 默认方法,避免 XML 里再写一遍排序:

bash 复制代码
@Mapper
public interface CareBedMapper extends BaseMapperX<CareBedDO> {

    default List<CareBedDO> selectListByFloorId(Long floorId) {
        return selectList(new LambdaQueryWrapperX<CareBedDO>()
                .eq(CareBedDO::getFloorId, floorId)
                .orderByAsc(CareBedDO::getRoomId)
                .orderByAsc(CareBedDO::getSort)
                .orderByAsc(CareBedDO::getId));
    }
}

3. 快速添加整栋楼:CareSpaceServiceImpl.builder(盖楼)与 insertBeds(建床)

「快速添加」一次插入整栋楼:N 层 × M 间 × 房型床位数,事务内完成。

bash 复制代码
@Override
@Transactional(rollbackFor = Exception.class)
public Long builder(CareSpaceBuilderReqVO reqVO) {
    CareRoomLayoutDO layout = careRoomLayoutMapper.selectById(reqVO.getLayoutId());
    int bedCount = layout.getBedCount() == null || layout.getBedCount() < 1 ? 1 : layout.getBedCount();

    CareSpaceDO building = new CareSpaceDO();
    building.setName(reqVO.getBuildingName());
    building.setType(TYPE_BUILDING);
    building.setParentId(0L);
    building.setAncestors("0");
    careSpaceMapper.insert(building);

    for (int i = 1; i <= reqVO.getStoreyNumber(); i++) {
        CareSpaceDO floor = new CareSpaceDO();
        floor.setName(i + "层");
        floor.setType(TYPE_FLOOR);
        floor.setParentId(building.getId());
        floor.setAncestors("0," + building.getId());
        careSpaceMapper.insert(floor);

        for (int r = 1; r <= reqVO.getRoomNumber(); r++) {
            CareSpaceDO room = new CareSpaceDO();
            room.setName(pad2(r));
            room.setType(TYPE_ROOM);
            room.setParentId(floor.getId());
            room.setAncestors(floor.getAncestors() + "," + floor.getId());
            room.setLayoutId(layout.getId());
            careSpaceMapper.insert(room);
            insertBeds(room, building.getId(), floor.getId(), layout, bedCount);
        }
    }
    return building.getId();
}
步骤 说明
读 layout bedCount、bedPrice 决定每间生成几张床
insert building ancestors=0 根节点
双层循环 楼层 sort、房间 sort 与 UI 列顺序一致
insertBeds 默认 status=0 空闲、bedAttr=1 养老

真正「一间房几张床」在 insertBeds 里循环插入,名称 01床、02床,价格和房型表一致:

bash 复制代码
private void insertBeds(CareSpaceDO room, Long buildingId, Long floorId,
                        CareRoomLayoutDO layout, int bedCount) {
    for (int i = 1; i <= bedCount; i++) {
        CareBedDO bed = new CareBedDO();
        bed.setName(pad2(i) + "床");
        bed.setRoomId(room.getId());
        bed.setFloorId(floorId);
        bed.setBuildingId(buildingId);
        bed.setLayoutId(layout.getId());
        bed.setBedPrice(layout.getBedPrice());
        bed.setBedAttr(1);
        bed.setStatus(BED_STATUS_IDLE);
        bed.setSort(i);
        careBedMapper.insert(bed);
    }
}

单房间改房型时,createSpace 在 type=房间 && layoutId!=null 时同样调用 createBedsForRoom。

bash 复制代码
@Override
@Transactional(rollbackFor = Exception.class)
public Long createSpace(CareSpaceSaveReqVO createReqVO) {
    CareSpaceDO space = BeanUtils.toBean(createReqVO, CareSpaceDO.class);
    fillParentAndAncestors(space);
    clearLayoutIfNotRoom(space);
    careSpaceMapper.insert(space);
    if (Objects.equals(space.getType(), TYPE_ROOM) && space.getLayoutId() != null) {
        createBedsForRoom(space);
    }
    return space.getId();
}

删改约束(业务层校验,无 DB 外键):有子节点不可删;房间已有床不可乱改父级/房型;在住床存在时不可改房间名。

图3 房态图 · GET /care/bed/room-state?floorId=

4. 删改床位与房态 JSON:isBedOccupied(占用判断)、getRoomState(Bridge 桥接)

hub 侧统一 isBedOccupied:在住、请假、预订、试住或 olderId!=null 时禁止删床、改房间、改养老/护理属性。

bash 复制代码
private static boolean isBedOccupied(CareBedDO bed) {
    return Objects.equals(bed.getStatus(), BED_STATUS_OCCUPIED)
            || Objects.equals(bed.getStatus(), BED_STATUS_LEAVE)
            || Objects.equals(bed.getStatus(), BED_STATUS_RESERVED)
            || Objects.equals(bed.getStatus(), BED_STATUS_TRYLIVE)
            || bed.getOlderId() != null;
}

@Override
public void updateBed(CareBedSaveReqVO updateReqVO) {
    CareBedDO exists = validateExists(updateReqVO.getId());
    if (isBedOccupied(exists) && !Objects.equals(updateReqVO.getRoomId(), exists.getRoomId())) {
        throw exception(BED_OCCUPIED);
    }
    // ...
}

getBedTree 把 care_space 与 care_bed 拼成「楼宇→楼层→房间→床位」树,供 床位管理 与筛选;房间节点附带 layoutName、区域名(CareAreaNameBridge 可选 SPI)。

房态图数据不在 hub 硬编码:CareBedRoomStateBridge 由 institution 实现,聚合 checkin、老人姓名、预订态等。

bash 复制代码
@Autowired(required = false)
private CareBedRoomStateBridge roomStateBridge;

@Override
public CareBedRoomStateRespVO getRoomState(Long floorId) {
    if (roomStateBridge == null) {
        throw exception(BED_ROOM_STATE_REQUIRES_INSTITUTION);
    }
    return roomStateBridge.buildRoomState(floorId);
}
  • required=false:仅 hub 演示包时房态接口会抛业务错,提示需引入 institution JAR(与 Maven 交付一致)。

institution 里 CareInstitutionBedRoomStateBridge 会 按楼层批量查 bed ,再 join 预订/试住/在住 checkin/请假,拼成前端房卡需要的 viewState(例如请假占床、住院展示态与维修态分开):

bash 复制代码
@Component
public class CareInstitutionBedRoomStateBridge implements CareBedRoomStateBridge {

    @Override
    public CareBedRoomStateRespVO buildRoomState(Long floorId) {
        List<CareBedDO> beds = careBedMapper.selectListByFloorId(floorId);
        // 统计空床/预订/在住数量 → setEmptyBedCount / setReserveCount / setLiveCount
        Map<Long, CareReserveDO> reserveByBed = /* state=0 的 care_reserve 按 bedId */;
        Map<Long, CareCheckinDO> checkinByBed = /* status=3 在住按 bedId */;
        for (CareSpaceDO room : rooms) {
            for (CareBedDO bed : bedsByRoom.get(room.getId())) {
                rn.getChildren().add(toRoomStateBed(bed, olderMap, reserveByBed, ...));
            }
        }
        return resp;
    }
}
  • 批量 Map:避免房态图一页 N+1 查老人姓名;姓名脱敏在前端或 VO 里做。
  • 预订态 :hub 只把 bed 标成 status=1,卡片上的计划入住时间来自 care_reserve(下一篇讲 CareReserveServiceImpl)。

图4 床位统计 · 汇总依赖 bed.status 与在住 checkin 一致

图5 空床从房态图进预订/入住 · 颜色与 bed.status 一致

5. 后台接口:CareSpaceController(楼宇)、CareBedController(床位)

空间与床位 REST 前缀固定,Swagger 分组「机构空间 / 床位」。

bash 复制代码
@RestController
@RequestMapping("/care/space")
public class CareSpaceController {

    @PostMapping("/builder")
    @PreAuthorize("@ss.hasPermission('care:space:create')")
    public CommonResult<Long> builder(@Valid @RequestBody CareSpaceBuilderReqVO reqVO) {
        return success(careSpaceService.builder(reqVO));
    }
}
bash 复制代码
@RestController
@RequestMapping("/care/bed")
public class CareBedController {

    @GetMapping("/room-state")
    @PreAuthorize("@ss.hasPermission('care:bed:query')")
    public CommonResult<CareBedRoomStateRespVO> getRoomState(@RequestParam("floorId") Long floorId) {
        return success(careBedService.getRoomState(floorId));
    }

    @GetMapping("/tree")
    @PreAuthorize("@ss.hasPermission('care:bed:query')")
    public CommonResult<List<CareBedTreeNodeVO>> getBedTree(@Valid CareBedTreeReqVO reqVO) {
        return success(careBedService.getBedTree(reqVO));
    }
}
接口 前端视图
POST /care/space/builder 楼宇设置 · 快速添加
GET /care/bed/tree 床位管理树
GET /care/bed/room-state 房态图按楼层刷新

6. curl 联调

快速添加(演示站等价于楼宇设置弹窗提交):

bash 复制代码
curl -X POST "http://localhost:48080/admin-api/care/space/builder" \
  -H "Authorization: Bearer {token}" \
  -H "tenant-id: 123" \
  -H "Content-Type: application/json" \
  -d '{"buildingName":"A栋","storeyNumber":3,"roomNumber":10,"layoutId":1}'

查床位树、房态:

bash 复制代码
curl -G "http://localhost:48080/admin-api/care/bed/tree" \
  -H "Authorization: Bearer {token}" \
  -H "tenant-id: 123" \
  --data-urlencode "layoutId="
bash 复制代码
curl -G "http://localhost:48080/admin-api/care/bed/room-state" \
  -H "Authorization: Bearer {token}" \
  -H "tenant-id: 123" \
  --data-urlencode "floorId=1001"

成功:{"code":0,"data":{"rooms":[...]}}。若 code 提示需 institution,检查 zhijian-server/pom.xml 是否依赖 zhijian-module-institution。

application-local.yaml

bash 复制代码
yudao:
  tenant:
    enable: true
spring:
  profiles:
    active: local

7. 谁改床位状态:institution(机构模块)写 bed.status,hub(中台)只管楼宇结构

同一张 care_bed 行会被 institution 在 预订 / 试住 / assign 在住 / 请假 / 腾床 时改 status 和 olderId;hub 不负责写 checkin,只负责 能不能删床、能不能改房间 。我线上查房态不对,一般是 bed.status 和 checkin 不同步 (例如手工改库只改了 checkin),其次才是 tenant-id 没带 看到别园数据。

8. 踩坑

  • 房态接口报需 institution :zhijian-server/pom.xml 没引 zhijian-module-institution,或 Bridge 实现没进 Spring 扫描。
  • 删房间报 SPACE_HAS_BEDS:房间下还有 bed 行;在住/预订要先走退住或退订释床。
  • 改房间号抛 BED_OCCUPIED :在住、预订、试住、请假占用都拦 roomId 变更------别和财务对账打架。
  • ancestors 手工改乱 :bed.buildingId/floorId 和 room 对不上,报表按楼汇总会错;尽量走 Service 改结构。
  • 快速添加后房态空 :确认 layoutId 对应房型 bedCount>0,且 floorId 与房态图下拉选的是同一层。

Gitee 社区版源码 :gitee.com/xinzexingji...

相关推荐
Lyy1 小时前
DevOps平台 — 第十三篇:规划的设计与实现
后端·devops
今天不在线1 小时前
旧 Java 项目接入 AI 实战系列(一):四层递进,从基础对话到流式输出
后端
卷无止境1 小时前
WebGIS生态全景丨从浏览器里的地图到背后的空间数据库
后端·python
JuiceFS2 小时前
JuiceFS 企业版 5.4:从千亿文件到百万客户端
后端
Lost of 程序猿2 小时前
命令模式实战:把“下单后的动作“打包成可执行的任务
后端·设计模式·c#·asp.net·命令模式
焦玉全2 小时前
Sharding-JDBC 分库分表实战:从 800 万订单表的查询优化说起
后端
王中阳Go2 小时前
面试官问"你怎么证明它有效",200个转AI的后端没几个答得上来
人工智能·后端·面试
深入云栈2 小时前
Netty 4.2.x 源码深度解析 (十四):KQueue 传输 —— macOS 高性能 IO 的 Netty 实现
java·后端