# 养老院管理系统 - 快速参考指南 ## 📋 目录 1. [项目基本信息](#项目基本信息) 2. [核心技术栈](#核心技术栈) 3. [项目结构](#项目结构) 4. [关键模块](#关键模块) 5. [常用命令](#常用命令) 6. [性能问题](#性能问题) 7. [开发指南](#开发指南) 8. [常见问题](#常见问题) --- ## 项目基本信息 | 项目 | 信息 | |------|------| | **项目名称** | kyj-yanglao-end(养老院管理系统后端) | | **基础框架** | 芋道 RuoYi-Vue-Pro 2.1.0 | | **Java版本** | JDK 8+ | | **构建工具** | Maven 3.6+ | | **主要数据库** | MySQL 5.7+ | | **缓存存储** | Redis 5.0+ | | **工作流引擎** | Flowable 6.7+ | --- ## 核心技术栈 ### 后端技术 ``` Spring Boot 2.7.18 ├─ Spring Framework 5.3.x ├─ Spring Security 5.7.x ├─ Spring Data Redis ├─ Spring Data JPA └─ Spring Cloud (可选) MyBatis 3.5.x ├─ MyBatis Plus 3.5.x ├─ MyBatis Generator └─ Dynamic SQL 数据库 ├─ MySQL 5.7+ ├─ Oracle (可选) ├─ PostgreSQL (可选) └─ SQL Server (可选) 缓存 ├─ Redis 5.0+ ├─ Spring Cache └─ Caffeine (本地缓存) 工作流 ├─ Flowable 6.7.x └─ Activiti (可选) 其他 ├─ Lombok 1.18.30 ├─ MapStruct 1.5.5 ├─ EasyExcel 3.x ├─ Knife4j 3.x (API文档) ├─ Hutool (工具库) └─ Jackson (JSON处理) ``` ### 前端技术 ``` Vue 3 / Vue 2 ├─ Vue Router ├─ Vuex / Pinia ├─ Element Plus / Element UI ├─ Axios └─ ECharts Vben Admin (企业级模板) UniApp (跨平台移动) ``` --- ## 项目结构 ### 目录树 ``` kyj-yanglao-end/ ├── yudao-dependencies/ # 依赖版本管理 ├── yudao-framework/ # 框架层 │ ├── yudao-common/ │ ├── yudao-spring-boot-starter-*/ │ └── pom.xml ├── yudao-module-system/ # 系统模块(核心) │ ├── yudao-module-system-api/ │ ├── yudao-module-system-biz/ │ └── pom.xml ├── yudao-module-infra/ # 基础设施模块 ├── yudao-module-member/ # 会员模块 ├── yudao-module-bpm/ # 工作流模块 ├── yudao-module-report/ # 报表模块 ├── yudao-server/ # 主应用 ├── yudao-ui/ # 前端项目 ├── script/ # 脚本 ├── sql/ # 数据库脚本 └── pom.xml ``` ### 模块说明 | 模块 | 说明 | 状态 | |------|------|------| | **yudao-module-system** | 系统核心模块(长者、合同、费用、账单等) | ✅ 必需 | | **yudao-module-infra** | 基础设施模块(文件、字典、参数等) | ✅ 必需 | | **yudao-module-member** | 会员模块 | ⚠️ 可选 | | **yudao-module-bpm** | 工作流模块 | ⚠️ 可选 | | **yudao-module-report** | 报表模块 | ⚠️ 可选 | | **yudao-module-pay** | 支付模块 | ⚠️ 可选 | --- ## 关键模块 ### 1. 长者管理 (ElderlyInfo) **主要类**: - `ElderlyInfoDO` - 数据对象 - `ElderlyInfoMapper` - 数据访问 - `ElderlyInfoService` - 业务逻辑 - `ElderlyInfoController` - 控制器 **关键字段**: ```java id // 长者ID elder_name // 姓名 elder_sex // 性别 elder_age // 年龄 in_status // 在住状态 (1=在住, 2=退住) org_type // 机构类型 tenant_id // 租户ID bed_id // 床位ID build_id // 建筑ID nurse_level_name // 护理等级 ``` **常用API**: ``` GET /api/elderly/list # 查询列表 GET /api/elderly/{id} # 查询详情 POST /api/elderly/create # 创建 PUT /api/elderly/update # 更新 DELETE /api/elderly/{id} # 删除 POST /api/elderly/check-in # 入住 POST /api/elderly/check-out # 退住 ``` --- ### 2. 合同管理 (ElderlyContract) **主要类**: - `ElderlyContractDO` - 数据对象 - `ElderlyContractMapper` - 数据访问 - `ElderlyContractService` - 业务逻辑 - `ElderlyContractController` - 控制器 **关键字段**: ```java id // 合同ID elder_id // 长者ID contract_number // 合同号 begin_time // 开始时间 expire_time // 结束时间 contract_term // 合同期限(月) ``` **常用API**: ``` GET /api/contract/list # 查询列表 GET /api/contract/{id} # 查询详情 POST /api/contract/create # 创建 PUT /api/contract/update # 更新 DELETE /api/contract/{id} # 删除 POST /api/contract/renew # 续签 ``` --- ### 3. 费用管理 (Expense) **主要类**: - `ExpenseDO` - 数据对象 - `ExpenseItemDO` - 费用项数据对象 - `ExpenseMapper` - 数据访问 - `ExpenseService` - 业务逻辑 - `ExpenseController` - 控制器 **关键字段**: ```java // Expense id // 费用ID elder_id // 长者ID created_time // 创建时间 // ExpenseItem id // 费用项ID expense_id // 费用ID item_category_id // 费用分类ID type // 费用类型 (1=床位费, 2=护理费, 3=餐费, 4=服务费) amount // 金额 ``` **常用API**: ``` GET /api/expense/list # 查询列表 GET /api/expense/{id} # 查询详情 POST /api/expense/create # 创建 PUT /api/expense/update # 更新 DELETE /api/expense/{id} # 删除 GET /api/expense/statistics # 统计 ``` --- ### 4. 账单管理 (ExpenseOrder) ⚠️ 需要优化 **主要类**: - `ExpenseOrderDO` - 数据对象 - `ExpenseOrderItemDO` - 账单项数据对象 - `ExpenseOrderMapper` - 数据访问 - `ExpenseOrderService` - 业务逻辑 - `OrderApiImpl` - API实现 **【性能问题】** - `ExpenseOrderController` - 控制器 **关键字段**: ```java // ExpenseOrder id // 账单ID elder_id // 长者ID billing_month // 账单月份 (YYYY-MM) pay_status // 缴费状态 (0=未缴, 1=已缴) payable_amount // 应付金额 actual_amount // 实付金额 arrears_note // 欠费备注 // ExpenseOrderItem id // 账单项ID expense_order_id // 账单ID expense_source // 费用来源 (1=月度费用, 2=日常开支) source_expense_item_id // 来源ID total_amount // 总金额 actual_price // 实付金额 pay_status // 缴费状态 ``` **常用API**: ``` GET /api/order/list # 查询列表 GET /api/order/{id} # 查询详情 POST /api/order/create # 创建 PUT /api/order/update # 更新 DELETE /api/order/{id} # 删除 GET /api/order/by-month # 按月查询 GET /api/order/total-by-month # 按月总计 GET /api/order/receivable-list # 应收列表 GET /api/order/receivable-total # 应收总计 【性能问题】 POST /api/order/mark-paid # 标记已缴 ``` --- ### 5. 日常开支 (DailyExpenses) **主要类**: - `DailyExpensesDO` - 数据对象 - `DailyExpensesMapper` - 数据访问 - `DailyExpensesService` - 业务逻辑 - `DailyExpensesController` - 控制器 **关键字段**: ```java id // 开支ID elder_id // 长者ID item_name // 项目名称 amount // 金额 round_amount // 四舍五入后金额 type // 开支类型 (2=调整, 3=其他) created_time // 创建时间 ``` **常用API**: ``` GET /api/daily-expenses/list # 查询列表 GET /api/daily-expenses/{id} # 查询详情 POST /api/daily-expenses/create # 创建 PUT /api/daily-expenses/update # 更新 DELETE /api/daily-expenses/{id} # 删除 GET /api/daily-expenses/statistics # 统计 ``` --- ## 常用命令 ### Maven 命令 ```bash # 清理项目 mvn clean # 编译项目 mvn compile # 运行测试 mvn test # 打包项目 mvn package # 跳过测试打包 mvn package -DskipTests # 安装到本地仓库 mvn install # 清理并打包 mvn clean package # 查看依赖树 mvn dependency:tree # 更新依赖 mvn dependency:update-snapshots ``` ### 应用启动 ```bash # IDE 中直接运行 # 找到 yudao-server 模块下的 YudaoServerApplication.java # 右键 Run 或 Debug # 命令行启动 cd yudao-server mvn spring-boot:run # 使用 jar 包启动 java -jar yudao-server.jar # 指定配置文件启动 java -jar yudao-server.jar --spring.config.location=application-prod.yml ``` ### 数据库初始化 ```bash # 1. 创建数据库 CREATE DATABASE yudao DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_unicode_ci; # 2. 执行初始化脚本 # 在 sql/mysql 目录下找到 ruoyi-vue-pro.sql # 使用 MySQL 客户端执行 # 3. 执行定时任务脚本 # sql/mysql/quartz.sql # 4. 验证数据库 SHOW TABLES; SELECT COUNT(*) FROM sys_user; ``` --- ## 性能问题 ### 🔴 OrderApiImpl.getOrderReceivableTotal() 方法 **问题描述**: - 循环12次调用 `getReceivableListByMonth()` - 每次调用执行4次数据库查询 - 总共执行 **48 次数据库查询** ❌ **问题代码**: ```java @Override public OrderItemTotalRespVO getOrderReceivableTotal(Integer orgType, Long tenantId, String billingMonth) { // ... 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() └─ getListByMonth() ├─ Query 1: selectListByMonth() ├─ Query 2: selectList() ├─ Query 3: selectBatchIds() └─ Query 4: selectBatchIds() ``` **优化方案**: ```java // 1. 创建 selectListByYear() 方法 List selectListByYear(Integer orgType, Long tenantId, String year); // 2. 修改 getOrderReceivableTotal() @Override public OrderItemTotalRespVO getOrderReceivableTotal(Integer orgType, Long tenantId, String billingMonth) { // 一次性查询全年数据 List yearData = orderMapper.selectListByYear(orgType, tenantId, year); // 在内存中按月份分组 Map> monthlyData = yearData.stream() .collect(Collectors.groupingBy(OrderItemRespDTO::getBillingMonth)); // 循环12个月进行累加 for (int i = 1; i <= 12; i++) { String month = year + "-" + String.format("%02d", i); List monthItems = monthlyData.getOrDefault(month, new ArrayList<>()); // 处理数据... } return orderItemTotalRespVO; } ``` **性能提升**: - 查询次数: 48 → 4 (减少 92%) - 响应时间: 预计减少 80-90% --- ## 开发指南 ### 添加新的业务模块 #### 步骤 1: 创建数据对象 (DO) ```java // 在 dal/dataobject/biz 目录下创建 @Data @EqualsAndHashCode(callSuper = true) @ToString(callSuper = true) @Builder @NoArgsConstructor @AllArgsConstructor @TableName("table_name") public class MyBusinessDO extends BaseDO { @TableId private Long id; private String name; private BigDecimal amount; private Integer status; private Long tenantId; // ... 其他字段 } ``` #### 步骤 2: 创建 Mapper 接口 ```java // 在 dal/mysql/biz 目录下创建 @Mapper public interface MyBusinessMapper extends BaseMapperX { // 自定义查询方法 List selectByTenantId(@Param("tenantId") Long tenantId); } ``` #### 步骤 3: 创建 Mapper XML ```xml ``` #### 步骤 4: 创建 Service 接口 ```java // 在 service/biz 目录下创建 public interface MyBusinessService { MyBusinessDO getById(Long id); List listByTenantId(Long tenantId); void create(MyBusinessDO myBusiness); void update(MyBusinessDO myBusiness); void delete(Long id); } ``` #### 步骤 5: 创建 Service 实现 ```java // 在 service/biz 目录下创建 @Service @Slf4j public class MyBusinessServiceImpl implements MyBusinessService { @Resource private MyBusinessMapper myBusinessMapper; @Override public MyBusinessDO getById(Long id) { return myBusinessMapper.selectById(id); } @Override public List listByTenantId(Long tenantId) { return myBusinessMapper.selectByTenantId(tenantId); } @Override @Transactional public void create(MyBusinessDO myBusiness) { myBusinessMapper.insert(myBusiness); } @Override @Transactional public void update(MyBusinessDO myBusiness) { myBusinessMapper.updateById(myBusiness); } @Override @Transactional public void delete(Long id) { myBusinessMapper.deleteById(id); } } ``` #### 步骤 6: 创建 Controller ```java // 在 controller/admin/biz 目录下创建 @RestController @RequestMapping("/api/my-business") @Slf4j public class MyBusinessController { @Resource private MyBusinessService myBusinessService; @GetMapping("/list") public CommonResult> list( @RequestParam Long tenantId) { List list = myBusinessService.listByTenantId(tenantId); return success(BeanUtils.toList(list, MyBusinessRespVO.class)); } @GetMapping("/{id}") public CommonResult get(@PathVariable Long id) { MyBusinessDO myBusiness = myBusinessService.getById(id); return success(BeanUtils.toBean(myBusiness, MyBusinessRespVO.class)); } @PostMapping("/create") public CommonResult create(@RequestBody MyBusinessCreateReqVO createReqVO) { MyBusinessDO myBusiness = BeanUtils.toBean(createReqVO, MyBusinessDO.class); myBusinessService.create(myBusiness); return success(myBusiness.getId()); } @PutMapping("/update") public CommonResult update(@RequestBody MyBusinessUpdateReqVO updateReqVO) { MyBusinessDO myBusiness = BeanUtils.toBean(updateReqVO, MyBusinessDO.class); myBusinessService.update(myBusiness); return success(true); } @DeleteMapping("/{id}") public CommonResult delete(@PathVariable Long id) { myBusinessService.delete(id); return success(true); } } ``` #### 步骤 7: 创建 VO 对象 ```java // 创建请求 VO @Data public class MyBusinessCreateReqVO { @NotBlank(message = "名称不能为空") private String name; @NotNull(message = "金额不能为空") private BigDecimal amount; private Integer status; } // 创建响应 VO @Data public class MyBusinessRespVO { private Long id; private String name; private BigDecimal amount; private Integer status; private LocalDateTime createdTime; } ``` --- ## 常见问题 ### Q1: 如何添加新的权限? **A**: 1. 在 `sys_menu` 表中添加菜单记录 2. 在 `sys_role_menu` 表中关联角色和菜单 3. 使用 `@PreAuthorize("@ss.hasPermi('system:user:list')")` 注解 ```java @PreAuthorize("@ss.hasPermi('system:user:list')") @GetMapping("/list") public CommonResult> list() { // ... } ``` ### Q2: 如何实现多租户隔离? **A**: 系统已内置多租户支持,自动隔离数据: ```java // 在 Service 中自动获取当前租户 Long tenantId = TenantContextHolder.getTenantId(); // 在查询时添加租户条件 List list = myBusinessMapper.selectList( new LambdaQueryWrapperX() .eq(MyBusinessDO::getTenantId, tenantId) ); ``` ### Q3: 如何添加缓存? **A**: ```java @Service public class MyBusinessServiceImpl implements MyBusinessService { @Resource private MyBusinessMapper myBusinessMapper; @Cacheable(value = "myBusiness", key = "#id") @Override public MyBusinessDO getById(Long id) { return myBusinessMapper.selectById(id); } @CacheEvict(value = "myBusiness", key = "#myBusiness.id") @Override @Transactional public void update(MyBusinessDO myBusiness) { myBusinessMapper.updateById(myBusiness); } } ``` ### Q4: 如何处理事务? **A**: ```java @Service public class MyBusinessServiceImpl implements MyBusinessService { @Transactional public void complexOperation() { // 多个数据库操作 // 如果任何一个失败,都会回滚 } @Transactional(rollbackFor = Exception.class) public void operationWithException() throws Exception { // 指定回滚异常类型 } } ``` ### Q5: 如何进行日志记录? **A**: ```java @Service @Slf4j public class MyBusinessServiceImpl implements MyBusinessService { public void create(MyBusinessDO myBusiness) { log.info("创建业务对象: {}", myBusiness.getId()); try { myBusinessMapper.insert(myBusiness); log.info("创建成功"); } catch (Exception e) { log.error("创建失败", e); throw e; } } } ``` ### Q6: 如何处理异常? **A**: ```java @RestController public class MyBusinessController { @GetMapping("/{id}") public CommonResult get(@PathVariable Long id) { MyBusinessDO myBusiness = myBusinessService.getById(id); if (myBusiness == null) { return fail(ErrorCodeConstants.MY_BUSINESS_NOT_FOUND); } return success(BeanUtils.toBean(myBusiness, MyBusinessRespVO.class)); } } ``` ### Q7: 如何进行单元测试? **A**: ```java @SpringBootTest public class MyBusinessServiceTest { @Resource private MyBusinessService myBusinessService; @Test public void testGetById() { MyBusinessDO myBusiness = myBusinessService.getById(1L); assertNotNull(myBusiness); assertEquals("test", myBusiness.getName()); } } ``` ### Q8: 如何导出数据? **A**: ```java @PostMapping("/export") public void export(@RequestBody MyBusinessExportReqVO exportReqVO, HttpServletResponse response) throws IOException { List list = myBusinessService.list(exportReqVO); List excelList = BeanUtils.toList(list, MyBusinessExcelVO.class); EasyExcel.write(response.getOutputStream(), MyBusinessExcelVO.class) .sheet("数据") .doWrite(excelList); } ``` ### Q9: 如何导入数据? **A**: ```java @PostMapping("/import") public CommonResult importData(@RequestParam("file") MultipartFile file) throws IOException { List list = EasyExcel.read(file.getInputStream()) .head(MyBusinessExcelVO.class) .sheet() .doReadSync(); for (MyBusinessExcelVO excelVO : list) { MyBusinessDO myBusiness = BeanUtils.toBean(excelVO, MyBusinessDO.class); myBusinessService.create(myBusiness); } return success(true); } ``` ### Q10: 如何调试应用? **A**: ``` 1. 在 IDE 中设置断点 2. 右键选择 Debug 运行 3. 使用 F6 (单步执行) 或 F8 (继续执行) 4. 在 Variables 窗口查看变量值 5. 在 Console 窗口查看日志输出 ``` --- ## 📞 联系方式 - **项目地址**: https://github.com/YunaiV/ruoyi-vue-pro - **文档地址**: https://doc.iocoder.cn - **问题反馈**: 提交 Issue 或 Pull Request --- **文档生成时间**: 2025-12-08 **文档版本**: 1.0 **作者**: AI Assistant