这是一个针对养老院(长者护理机构)的综合管理系统,涵盖:
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文件
┌─────────────────────────────────────────────┐
│ Controller(控制层) │
│ - 请求处理、参数验证、响应返回 │
└────────────────┬────────────────────────────┘
│
┌─────────────────────────────────────────────┐
│ Service(业务层) │
│ - 业务逻辑处理、事务管理 │
└────────────────┬────────────────────────────┘
│
┌─────────────────────────────────────────────┐
│ Mapper(数据访问层) │
│ - 数据库操作、SQL执行 │
└────────────────┬────────────────────────────┘
│
┌─────────────────────────────────────────────┐
│ Database(数据库层) │
│ - MySQL数据存储 │
└─────────────────────────────────────────────┘
这是项目的核心业务模块,包含养老院管理的所有主要功能。
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/
| 对象 | 说明 | 关键字段 |
|---|---|---|
| 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 |
// OrderApi.java - 订单相关API
public interface OrderApi {
// 按月获取订单列表
List<OrderItemRespVO> getListByMonth(Integer orgType, Long tenantId, String billMonth, Integer inStatus);
// 按月获取总计
OrderItemTotalRespVO getTotalByMonth(Integer orgType, Long tenantId, String billingMonth);
// 获取应收列表(按月)
List<OrderItemRespVO> getReceivableListByMonth(Integer orgType, Long tenantId, String queryFLag, String billingMonth);
// 获取应收总计(按年)- 【需要优化的方法】
OrderItemTotalRespVO getOrderReceivableTotal(Integer orgType, Long tenantId, String billingMonth);
}
getOrderReceivableTotal() 方法存在严重的 N+1 查询问题,导致数据库查询次数过多。
@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<OrderItemRespVO> 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次 |
| 问题根源 | 重复查询相同数据,缺乏缓存 |
getTotalByMonth() 方法
getListByMonth()(分别查询在住和退住)getListByMonth() 方法
ElderlyInfoMapper.selectCountByParam() 方法
getTotalByMonth() 中调用4次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 (余额)
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
);
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
);
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
);
| 技术 | 版本 | 用途 |
|---|---|---|
| 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文档 |
| 技术 | 说明 |
|---|---|
| Vue 3 | 现代前端框架 |
| Vue 2 | 传统前端框架 |
| Vben Admin | 企业级后台模板 |
| UniApp | 跨平台移动应用 |
| 组件 | 用途 |
|---|---|
| Docker | 容器化部署 |
| Jenkins | CI/CD流程 |
| MySQL | 数据存储 |
| Redis | 缓存层 |
| Nginx | 反向代理 |
1. 定时任务触发(ElderContractUpdateJob)
↓
2. 查询所有有效合同的长者
↓
3. 根据合同和费用项生成账单(ExpenseOrder)
↓
4. 创建账单项(ExpenseOrderItem)
├─ 月度费用项(床位费、护理费、餐费、服务费)
└─ 日常开支项(点餐、医疗护理、身体护理等)
↓
5. 计算应付金额和实付金额
↓
6. 保存到数据库
getOrderReceivableTotal(orgType, tenantId, billingMonth)
↓
循环12个月 (i=1 to 12)
├─ getReceivableListByMonth(queryFlag=i, billingMonth)
│ ├─ 获取该月订单列表
│ ├─ 获取订单项目
│ ├─ 获取费用项详情
│ ├─ 获取日常开支详情
│ └─ 组装返回数据
│
└─ 累加各项金额
├─ 床位费
├─ 护理费
├─ 餐费
├─ 服务费
└─ 调整金额
↓
返回年度统计总计
cn.iocoder.yudao.module.system
├── api/ # 对外API接口
├── controller/ # HTTP控制层
├── service/ # 业务逻辑层
│ ├── biz/ # 业务服务
│ ├── permission/ # 权限服务
│ ├── tenant/ # 租户服务
│ └── ...
├── dal/ # 数据访问层
│ ├── dataobject/ # 数据对象(DO)
│ └── mysql/ # MyBatis Mapper
├── enums/ # 枚举类
├── job/ # 定时任务
├── util/ # 工具类
└── framework/ # 框架相关
| 对象类型 | 后缀 | 示例 |
|---|---|---|
| 数据对象 | DO | ExpenseOrderDO |
| 请求对象 | ReqVO | ExpenseOrderCreateReqVO |
| 响应对象 | RespVO | ExpenseOrderRespVO |
| DTO对象 | DTO | OrderItemRespDTO |
| 服务接口 | Service | ExpenseOrderService |
| 服务实现 | ServiceImpl | ExpenseOrderServiceImpl |
| 数据访问 | Mapper | ExpenseOrderMapper |
| 控制器 | Controller | ExpenseOrderController |
# 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
# 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
优先级: 高
合并数据库查询
selectListByYear() 方法,一次性查询全年数据实现缓存机制
优化SQL查询
优先级: 中
异步处理
分页处理
索引优化
billing_month, elder_id, tenant_id 上添加索引优先级: 低
数据仓库
微服务化
消息队列
| 文件路径 | 说明 |
|---|---|
OrderApiImpl.java |
订单API实现(需优化) |
ExpenseOrderService.java |
账单业务接口 |
ExpenseOrderServiceImpl.java |
账单业务实现 |
ExpenseService.java |
费用业务接口 |
ExpenseServiceImpl.java |
费用业务实现 |
DailyExpensesService.java |
日常开支业务接口 |
DailyExpensesServiceImpl.java |
日常开支业务实现 |
ElderlyInfoService.java |
长者信息业务接口 |
ElderlyInfoServiceImpl.java |
长者信息业务实现 |
ElderlyContractService.java |
合同业务接口 |
ElderlyContractServiceImpl.java |
合同业务实现 |
| 文件路径 | 说明 |
|---|---|
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映射 |
| 文件路径 | 说明 |
|---|---|
ExpenseOrderController.java |
账单管理控制器 |
ExpenseController.java |
费用管理控制器 |
DailyExpensesController.java |
日常开支控制器 |
ElderlyInfoController.java |
长者信息控制器 |
ElderlyContractController.java |
合同管理控制器 |
RefundSettlementOrderController.java |
退款结算控制器 |
A: 是的,系统内置多租户支持(yudao-spring-boot-starter-biz-tenant)。每个租户有独立的数据隔离。
A: 通过 ExpenseItemService 添加新的费用项,然后在账单生成时会自动包含。
A: 通过定时任务 ElderContractUpdateJob 自动生成,也可以手动触发。
A: 支持 MySQL、Oracle、PostgreSQL、SQL Server、达梦、国产数据库等。
A: 系统会根据 in_status 字段区分在住和退住,分别统计。
✅ 基于成熟的开源框架(RuoYi-Vue-Pro) ✅ 完整的多模块架构 ✅ 支持多租户和权限管理 ✅ 包含工作流和报表功能 ✅ 支持多种数据库 ✅ 有完整的前后端分离
⚠️ 需要优化 getOrderReceivableTotal() 方法的性能
⚠️ 可以添加更多缓存机制
⚠️ 需要完善错误处理和日志记录
⚠️ 可以增加更多的单元测试
OrderApiImpl.getOrderReceivableTotal() 方法文档生成时间: 2025-12-08 文档版本: 1.0 作者: AI Assistant