# 养老院管理系统(KYJ-YangLao)工程分析报告 ## 一、项目概述 ### 1.1 项目基本信息 - **项目名称**: kyj-yanglao-end(养老院管理系统后端) - **基础框架**: 芋道 RuoYi-Vue-Pro 2.1.0(JDK 8) - **构建工具**: Maven - **主要技术栈**: Spring Boot 2.7.18 + MyBatis + MySQL ### 1.2 项目定位 这是一个针对养老院(长者护理机构)的综合管理系统,涵盖: - 长者信息管理 - 合同管理 - 账单与费用管理 - 日常开支管理 - 员工管理 - 设备管理 - 工作流(BPM) - 数据报表 --- ## 二、工程架构 ### 2.1 整体结构(多模块架构) ``` kyj-yanglao-end/ ├── yudao-dependencies/ # 依赖版本管理(BOM) ├── yudao-framework/ # 核心框架层 │ ├── yudao-common/ # 通用工具类 │ ├── yudao-spring-boot-starter-*/ # 各种功能启动器 │ │ ├── biz-data-permission/ # 数据权限 │ │ ├── biz-ip/ # IP地址解析 │ │ ├── biz-tenant/ # 多租户支持 │ │ ├── excel/ # Excel导入导出 │ │ ├── job/ # 定时任务 │ │ ├── monitor/ # 监控工具 │ │ ├── mq/ # 消息队列 │ │ ├── mybatis/ # MyBatis增强 │ │ ├── protection/ # 服务保障 │ │ ├── redis/ # Redis缓存 │ │ ├── security/ # Spring Security │ │ ├── test/ # 测试框架 │ │ ├── web/ # Web框架 │ │ └── websocket/ # WebSocket │ └── pom.xml ├── yudao-module-system/ # 系统模块(核心业务) │ ├── yudao-module-system-api/ # API接口定义 │ ├── yudao-module-system-biz/ # 业务实现 │ └── pom.xml ├── yudao-module-infra/ # 基础设施模块 ├── yudao-module-member/ # 会员模块 ├── yudao-module-bpm/ # 工作流模块 ├── yudao-module-report/ # 报表模块 ├── yudao-module-crm/ # CRM模块(可选) ├── yudao-module-erp/ # ERP模块(可选) ├── yudao-module-mall/ # 商城模块(可选) ├── yudao-module-pay/ # 支付模块(可选) ├── yudao-server/ # 主应用程序 │ ├── src/main/java/ │ ├── src/main/resources/ │ ├── Dockerfile │ └── pom.xml ├── yudao-ui/ # 前端项目集合 │ ├── yudao-ui-admin-vue3/ # 管理后台(Vue3) │ ├── yudao-ui-admin-vue2/ # 管理后台(Vue2) │ ├── yudao-ui-admin-vben/ # 管理后台(Vben) │ ├── yudao-ui-admin-uniapp/ # 管理后台(UniApp) │ ├── yudao-ui-mall-uniapp/ # 商城前端(UniApp) ├── script/ # 脚本文件 │ ├── docker/ # Docker配置 │ ├── jenkins/ # Jenkins配置 │ └── shell/ # 部署脚本 ├── sql/ # 数据库脚本 │ ├── mysql/ # MySQL脚本 │ ├── oracle/ # Oracle脚本 │ ├── postgresql/ # PostgreSQL脚本 │ ├── sqlserver/ # SQL Server脚本 │ ├── dm/ # 达梦数据库脚本 │ └── kingbase/ # 国产数据库脚本 └── pom.xml # 根POM文件 ``` ### 2.2 分层架构 ``` ┌─────────────────────────────────────────────┐ │ Controller(控制层) │ │ - 请求处理、参数验证、响应返回 │ └────────────────┬────────────────────────────┘ │ ┌─────────────────────────────────────────────┐ │ Service(业务层) │ │ - 业务逻辑处理、事务管理 │ └────────────────┬────────────────────────────┘ │ ┌─────────────────────────────────────────────┐ │ Mapper(数据访问层) │ │ - 数据库操作、SQL执行 │ └────────────────┬────────────────────────────┘ │ ┌─────────────────────────────────────────────┐ │ Database(数据库层) │ │ - MySQL数据存储 │ └─────────────────────────────────────────────┘ ``` --- ## 三、核心模块详解 ### 3.1 yudao-module-system(系统模块) 这是项目的核心业务模块,包含养老院管理的所有主要功能。 #### 3.1.1 模块结构 ``` yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/ ├── api/ # API接口实现 │ ├── order/ # 订单API │ │ ├── OrderApi.java # 接口定义 │ │ └── OrderApiImpl.java # 实现类(需优化) │ ├── bpm/ # 工作流API │ └── ... ├── controller/ # 控制层 │ ├── admin/ │ │ ├── biz/ # 业务相关控制器 │ │ │ ├── ExpenseOrderController.java # 账单管理 │ │ │ ├── ExpenseController.java # 费用管理 │ │ │ ├── ElderlyInfoController.java # 长者信息 │ │ │ ├── ElderlyContractController.java # 合同管理 │ │ │ ├── DailyExpensesController.java # 日常开支 │ │ │ ├── RefundSettlementOrderController.java # 退款结算 │ │ │ └── ... │ │ ├── yk/ # 养老院相关控制器 │ │ ├── restaurant/ # 餐饮相关控制器 │ │ ├── discount/ # 折扣管理 │ │ └── ... │ └── ... ├── service/ # 业务层 │ ├── biz/ # 业务服务 │ │ ├── ExpenseOrderService.java │ │ ├── ExpenseOrderServiceImpl.java │ │ ├── ExpenseService.java │ │ ├── ExpenseServiceImpl.java │ │ ├── DailyExpensesService.java │ │ ├── DailyExpensesServiceImpl.java │ │ ├── ElderlyInfoService.java │ │ ├── ElderlyInfoServiceImpl.java │ │ ├── ElderlyContractService.java │ │ ├── ElderlyContractServiceImpl.java │ │ ├── DepositService.java │ │ ├── DepositServiceImpl.java │ │ ├── RefundSettlementOrderService.java │ │ ├── RefundSettlementOrderServiceImpl.java │ │ ├── PriceChangeRecordServiceImpl.java │ │ ├── NurseChangeRecordServiceImpl.java │ │ └── ... │ ├── yk/ # 养老院服务 │ ├── restaurant/ # 餐饮服务 │ ├── discount/ # 折扣服务 │ ├── permission/ # 权限服务 │ ├── tenant/ # 租户服务 │ ├── user/ # 用户服务 │ ├── sms/ # 短信服务 │ └── ... ├── dal/ # 数据访问层 │ ├── dataobject/ # 数据对象 │ │ ├── biz/ │ │ │ ├── ExpenseOrderDO.java │ │ │ ├── ExpenseOrderItemDO.java │ │ │ ├── ExpenseDO.java │ │ │ ├── DailyExpensesDO.java │ │ │ ├── ElderlyInfoDO.java │ │ │ ├── ElderlyContractDO.java │ │ │ ├── DepositRecordDO.java │ │ │ ├── ElderlyChangeRecordDO.java │ │ │ ├── ElderlyAskLeaveDO.java │ │ │ ├── RefundSettlementOrderDO.java │ │ │ └── ... │ │ ├── yk/ │ │ ├── restaurant/ │ │ └── ... │ └── mysql/ # MyBatis Mapper │ ├── biz/ │ │ ├── ExpenseOrderMapper.java │ │ ├── ExpenseOrderItemMapper.java │ │ ├── ExpenseMapper.java │ │ ├── DailyExpensesMapper.java │ │ ├── ElderlyInfoMapper.java │ │ ├── ExpenseItemMapper.java │ │ └── ... │ ├── yk/ │ └── ... ├── enums/ # 枚举类 │ └── change/ │ └── BusinessConstants.java ├── job/ # 定时任务 │ └── ElderContractUpdateJob.java ├── util/ # 工具类 │ ├── BizUtil.java │ ├── StringUtil.java │ ├── SignUtil.java │ ├── SSLClient.java │ ├── http/ │ │ ├── HttpClientUtil.java │ │ └── HttpClientResult.java │ ├── oauth2/ │ ├── device/ │ └── ... └── resources/ ├── mapper/ # MyBatis XML映射文件 │ ├── ExpenseOrderMapper.xml │ ├── ExpenseOrderItemMapper.xml │ ├── ExpenseMapper.xml │ ├── DailyExpensesMapper.xml │ ├── ElderlyInfoMapper.xml │ └── ...(共60+个Mapper.xml文件) ├── images/ # 验证码图片 └── META-INF/ ``` #### 3.1.2 主要业务对象 | 对象 | 说明 | 关键字段 | |------|------|--------| | **ExpenseOrder** | 账单 | id, elder_id, billing_month, pay_status, payable_amount, actual_amount | | **ExpenseOrderItem** | 账单项目 | id, expense_order_id, expense_source, total_amount, actual_price, pay_status | | **Expense** | 费用 | id, elder_id, created_time | | **ExpenseItem** | 费用项 | id, expense_id, item_category_id, type, amount | | **DailyExpenses** | 日常开支 | id, elder_id, item_name, amount, round_amount, type | | **ElderlyInfo** | 长者信息 | id, elder_name, in_status, org_type, tenant_id, bed_id | | **ElderlyContract** | 合同 | id, elder_id, contract_number, begin_time, expire_time, contract_term | | **DepositRecord** | 押金记录 | id, elder_id, deposit_amount, refund_amount | | **ElderlyChangeRecord** | 长者变更记录 | id, elder_id, change_type, change_content | | **RefundSettlementOrder** | 退款结算单 | id, elder_id, settlement_amount | #### 3.1.3 关键API接口 ```java // OrderApi.java - 订单相关API public interface OrderApi { // 按月获取订单列表 List getListByMonth(Integer orgType, Long tenantId, String billMonth, Integer inStatus); // 按月获取总计 OrderItemTotalRespVO getTotalByMonth(Integer orgType, Long tenantId, String billingMonth); // 获取应收列表(按月) List getReceivableListByMonth(Integer orgType, Long tenantId, String queryFLag, String billingMonth); // 获取应收总计(按年)- 【需要优化的方法】 OrderItemTotalRespVO getOrderReceivableTotal(Integer orgType, Long tenantId, String billingMonth); } ``` --- ## 四、性能问题分析 ### 4.1 OrderApiImpl.getOrderReceivableTotal() 方法的性能问题 #### 问题描述 `getOrderReceivableTotal()` 方法存在严重的 N+1 查询问题,导致数据库查询次数过多。 #### 问题代码分析 ```java @Override public OrderItemTotalRespVO getOrderReceivableTotal(Integer orgType, Long tenantId, String billingMonth) { OrderItemTotalRespVO orderItemTotalRespVO = new OrderItemTotalRespVO(); // ... // 循环12个月,每个月调用一次 getReceivableListByMonth for (int i = 0; i < 12; i++) { String queryFlag = String.valueOf(i + 1); List receivableList = getReceivableListByMonth(orgType, tenantId, queryFlag, billingMonth); // 处理数据... } return orderItemTotalRespVO; } ``` #### 查询链路分析 ``` getOrderReceivableTotal() └─ for i=1 to 12: └─ getReceivableListByMonth(queryFlag=i, billingMonth) └─ getListByMonth(orgType, tenantId, billingMonth, null) ├─ orderMapper.selectListByMonth() // 查询1:订单列表 ├─ orderItemMapper.selectList() // 查询2:订单项目 ├─ itemMapper.selectBatchIds() // 查询3:费用项 └─ dailyExpensesMapper.selectBatchIds() // 查询4:日常开支 ``` #### 性能影响 | 指标 | 值 | |------|-----| | **每次调用的数据库查询数** | 4次 | | **循环次数** | 12次 | | **总查询数** | 48次 | | **问题根源** | 重复查询相同数据,缺乏缓存 | ### 4.2 其他潜在性能问题 1. **getTotalByMonth() 方法** - 调用两次 `getListByMonth()`(分别查询在住和退住) - 每次调用都会重新查询数据库 2. **getListByMonth() 方法** - 虽然已经优化了批量查询(避免N+1),但仍有改进空间 - 可以考虑添加缓存机制 3. **ElderlyInfoMapper.selectCountByParam() 方法** - 在 `getTotalByMonth()` 中调用4次 - 可以合并为一次查询 --- ## 五、数据库设计 ### 5.1 核心表关系图 ``` elderly_info (长者信息) ├─ elderly_contract (合同) ├─ elderly_expense_order (账单) │ └─ elderly_expense_order_item (账单项) │ ├─ elderly_expense_item (费用项) │ └─ daily_expenses (日常开支) ├─ elderly_expense (费用) │ └─ elderly_expense_item (费用项) ├─ daily_expenses (日常开支) ├─ deposit_record (押金) ├─ elderly_change_record (变更记录) └─ elderly_balance (余额) ``` ### 5.2 关键表设计 #### elderly_info(长者信息表) ```sql CREATE TABLE elderly_info ( id BIGINT PRIMARY KEY, elder_name VARCHAR(100), elder_sex INT, elder_age INT, in_status INT, -- 1: 在住, 2: 退住 org_type INT, tenant_id BIGINT, bed_id BIGINT, build_id BIGINT, nurse_level_name VARCHAR(100), address VARCHAR(500), created_time DATETIME, updated_time DATETIME ); ``` #### elderly_expense_order(账单表) ```sql CREATE TABLE elderly_expense_order ( id BIGINT PRIMARY KEY, elder_id BIGINT, billing_month VARCHAR(10), -- 格式: YYYY-MM pay_status INT, -- 0: 未缴费, 1: 已缴费 payable_amount DECIMAL(10,2), actual_amount DECIMAL(10,2), arrears_note VARCHAR(500), created_time DATETIME, updated_time DATETIME, tenant_id BIGINT, org_type INT ); ``` #### elderly_expense_order_item(账单项表) ```sql CREATE TABLE elderly_expense_order_item ( id BIGINT PRIMARY KEY, expense_order_id BIGINT, expense_source INT, -- 1: 月度费用, 2: 日常开支 source_expense_item_id BIGINT, total_amount DECIMAL(10,2), actual_price DECIMAL(10,2), pay_status INT, -- 0: 未缴费, 1: 已缴费 created_time DATETIME, updated_time DATETIME ); ``` --- ## 六、技术栈详解 ### 6.1 后端技术栈 | 技术 | 版本 | 用途 | |------|------|------| | **Spring Boot** | 2.7.18 | 应用框架 | | **Spring Security** | 5.7.x | 权限认证 | | **MyBatis** | 3.5.x | ORM框架 | | **MyBatis Plus** | 3.5.x | MyBatis增强 | | **MySQL** | 5.7+ | 关系型数据库 | | **Redis** | 5.0+ | 缓存存储 | | **Lombok** | 1.18.30 | 代码生成 | | **MapStruct** | 1.5.5 | 对象映射 | | **Flowable** | 6.7.x | 工作流引擎 | | **EasyExcel** | 3.x | Excel处理 | | **Knife4j** | 3.x | API文档 | ### 6.2 前端技术栈 | 技术 | 说明 | |------|------| | **Vue 3** | 现代前端框架 | | **Vue 2** | 传统前端框架 | | **Vben Admin** | 企业级后台模板 | | **UniApp** | 跨平台移动应用 | ### 6.3 基础设施 | 组件 | 用途 | |------|------| | **Docker** | 容器化部署 | | **Jenkins** | CI/CD流程 | | **MySQL** | 数据存储 | | **Redis** | 缓存层 | | **Nginx** | 反向代理 | --- ## 七、关键业务流程 ### 7.1 账单生成流程 ``` 1. 定时任务触发(ElderContractUpdateJob) ↓ 2. 查询所有有效合同的长者 ↓ 3. 根据合同和费用项生成账单(ExpenseOrder) ↓ 4. 创建账单项(ExpenseOrderItem) ├─ 月度费用项(床位费、护理费、餐费、服务费) └─ 日常开支项(点餐、医疗护理、身体护理等) ↓ 5. 计算应付金额和实付金额 ↓ 6. 保存到数据库 ``` ### 7.2 费用统计流程 ``` getOrderReceivableTotal(orgType, tenantId, billingMonth) ↓ 循环12个月 (i=1 to 12) ├─ getReceivableListByMonth(queryFlag=i, billingMonth) │ ├─ 获取该月订单列表 │ ├─ 获取订单项目 │ ├─ 获取费用项详情 │ ├─ 获取日常开支详情 │ └─ 组装返回数据 │ └─ 累加各项金额 ├─ 床位费 ├─ 护理费 ├─ 餐费 ├─ 服务费 └─ 调整金额 ↓ 返回年度统计总计 ``` --- ## 八、开发规范 ### 8.1 包结构规范 ``` cn.iocoder.yudao.module.system ├── api/ # 对外API接口 ├── controller/ # HTTP控制层 ├── service/ # 业务逻辑层 │ ├── biz/ # 业务服务 │ ├── permission/ # 权限服务 │ ├── tenant/ # 租户服务 │ └── ... ├── dal/ # 数据访问层 │ ├── dataobject/ # 数据对象(DO) │ └── mysql/ # MyBatis Mapper ├── enums/ # 枚举类 ├── job/ # 定时任务 ├── util/ # 工具类 └── framework/ # 框架相关 ``` ### 8.2 命名规范 | 对象类型 | 后缀 | 示例 | |---------|------|------| | 数据对象 | DO | ExpenseOrderDO | | 请求对象 | ReqVO | ExpenseOrderCreateReqVO | | 响应对象 | RespVO | ExpenseOrderRespVO | | DTO对象 | DTO | OrderItemRespDTO | | 服务接口 | Service | ExpenseOrderService | | 服务实现 | ServiceImpl | ExpenseOrderServiceImpl | | 数据访问 | Mapper | ExpenseOrderMapper | | 控制器 | Controller | ExpenseOrderController | ### 8.3 代码规范 - 使用 Lombok 简化代码(@Data, @Slf4j等) - 使用 MapStruct 进行对象转换 - 使用 LambdaQueryWrapperX 进行动态SQL构建 - 遵循单一职责原则 - 避免在循环中进行数据库查询(N+1问题) - 使用批量查询替代单条查询 --- ## 九、部署架构 ### 9.1 Docker部署 ```yaml # docker-compose.yml services: mysql: image: mysql:5.7 ports: - "3306:3306" environment: MYSQL_ROOT_PASSWORD: root redis: image: redis:5 ports: - "6379:6379" yudao-server: build: . ports: - "8080:8080" depends_on: - mysql - redis environment: SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/yudao SPRING_REDIS_HOST: redis ``` ### 9.2 应用配置 ```yaml # application.yml spring: datasource: url: jdbc:mysql://localhost:3306/yudao username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver jpa: hibernate: ddl-auto: validate redis: host: localhost port: 6379 database: 0 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: cn.iocoder.yudao.module.system.dal.dataobject configuration: map-underscore-to-camel-case: true ``` --- ## 十、优化建议 ### 10.1 紧急优化(OrderApiImpl) **优先级: 高** 1. **合并数据库查询** - 创建 `selectListByYear()` 方法,一次性查询全年数据 - 在内存中按月份分组,避免12次循环查询 2. **实现缓存机制** - 对月度数据进行缓存 - 设置合理的缓存过期时间 3. **优化SQL查询** - 使用JOIN替代多次查询 - 添加必要的数据库索引 ### 10.2 中期优化 **优先级: 中** 1. **异步处理** - 使用异步任务处理费用统计 - 提高API响应速度 2. **分页处理** - 对大数据量进行分页 - 减少内存占用 3. **索引优化** - 在 `billing_month`, `elder_id`, `tenant_id` 上添加索引 - 优化查询性能 ### 10.3 长期优化 **优先级: 低** 1. **数据仓库** - 建立数据仓库进行离线分析 - 减少在线系统压力 2. **微服务化** - 将费用模块独立为微服务 - 提高系统可扩展性 3. **消息队列** - 使用消息队列处理费用更新 - 提高系统解耦性 --- ## 十一、关键文件清单 ### 11.1 核心业务文件 | 文件路径 | 说明 | |---------|------| | `OrderApiImpl.java` | 订单API实现(**需优化**) | | `ExpenseOrderService.java` | 账单业务接口 | | `ExpenseOrderServiceImpl.java` | 账单业务实现 | | `ExpenseService.java` | 费用业务接口 | | `ExpenseServiceImpl.java` | 费用业务实现 | | `DailyExpensesService.java` | 日常开支业务接口 | | `DailyExpensesServiceImpl.java` | 日常开支业务实现 | | `ElderlyInfoService.java` | 长者信息业务接口 | | `ElderlyInfoServiceImpl.java` | 长者信息业务实现 | | `ElderlyContractService.java` | 合同业务接口 | | `ElderlyContractServiceImpl.java` | 合同业务实现 | ### 11.2 数据访问文件 | 文件路径 | 说明 | |---------|------| | `ExpenseOrderMapper.java` | 账单Mapper接口 | | `ExpenseOrderMapper.xml` | 账单SQL映射(**需优化**) | | `ExpenseOrderItemMapper.java` | 账单项Mapper接口 | | `ExpenseOrderItemMapper.xml` | 账单项SQL映射 | | `ExpenseMapper.java` | 费用Mapper接口 | | `ExpenseMapper.xml` | 费用SQL映射 | | `DailyExpensesMapper.java` | 日常开支Mapper接口 | | `DailyExpensesMapper.xml` | 日常开支SQL映射 | | `ElderlyInfoMapper.java` | 长者信息Mapper接口 | | `ElderlyInfoMapper.xml` | 长者信息SQL映射 | ### 11.3 控制器文件 | 文件路径 | 说明 | |---------|------| | `ExpenseOrderController.java` | 账单管理控制器 | | `ExpenseController.java` | 费用管理控制器 | | `DailyExpensesController.java` | 日常开支控制器 | | `ElderlyInfoController.java` | 长者信息控制器 | | `ElderlyContractController.java` | 合同管理控制器 | | `RefundSettlementOrderController.java` | 退款结算控制器 | --- ## 十二、常见问题解答 ### Q1: 系统支持多租户吗? **A**: 是的,系统内置多租户支持(yudao-spring-boot-starter-biz-tenant)。每个租户有独立的数据隔离。 ### Q2: 如何添加新的费用项? **A**: 通过 `ExpenseItemService` 添加新的费用项,然后在账单生成时会自动包含。 ### Q3: 账单如何生成? **A**: 通过定时任务 `ElderContractUpdateJob` 自动生成,也可以手动触发。 ### Q4: 系统支持哪些数据库? **A**: 支持 MySQL、Oracle、PostgreSQL、SQL Server、达梦、国产数据库等。 ### Q5: 如何处理退住长者的账单? **A**: 系统会根据 `in_status` 字段区分在住和退住,分别统计。 --- ## 十三、总结 ### 13.1 项目优势 ✅ 基于成熟的开源框架(RuoYi-Vue-Pro) ✅ 完整的多模块架构 ✅ 支持多租户和权限管理 ✅ 包含工作流和报表功能 ✅ 支持多种数据库 ✅ 有完整的前后端分离 ### 13.2 改进方向 ⚠️ 需要优化 `getOrderReceivableTotal()` 方法的性能 ⚠️ 可以添加更多缓存机制 ⚠️ 需要完善错误处理和日志记录 ⚠️ 可以增加更多的单元测试 ### 13.3 建议 1. **立即行动**: 优化 `OrderApiImpl.getOrderReceivableTotal()` 方法 2. **短期计划**: 添加缓存机制和数据库索引 3. **中期计划**: 实现异步处理和消息队列 4. **长期规划**: 考虑微服务化和数据仓库 --- **文档生成时间**: 2025-12-08 **文档版本**: 1.0 **作者**: AI Assistant