
在线演示管理端后台标签#Java #Spring Boot #养老系统 #费用管理 #资金账户 #月账单1. 机构费用子模块账户 流水是财务对账真相源养老院财务每天关心两件事老人账户余额对不对、每笔充扣能不能追溯到业务单。全链式智慧养老云平台在 institution 模块用care_account存余额/押金/账户状态用care_account_flow记每一笔变动月账单care_month_bill批量应扣定时 Job 调用changeBiz扣费。入住 flow「费用计算」提交、核算单缴费、退住结算最终都落到同一套 change 内核。下文拆解CareAccountServiceImpl子域openAccount/getOrOpen→change手工充扣退→doChange余额变更 插流水→ 列表分页与 status 联动care_checkin。代码路径zhijian-module-institution/.../service/account/。图1 资金账户列表 · 余额、欠费态、入住状态筛选2. 表模型与常量表职责care_account每老人一户balance/deposit/statuscare_account_flow流水changeType、amount、bizType、bizId、payWaycare_month_bill月汇总单care_month_bill_item账单明细行CareAccountServiceImpl内部常量对齐旧系统private static final int WALLET_BALANCE 1; private static final int WALLET_DEPOSIT 2; private static final int CHANGE_RECHARGE 1; // 充值 ↑ private static final int CHANGE_DEDUCT 2; // 扣费 ↓ private static final int CHANGE_REFUND 3; // 退费 ↑冲正 private static final int CHANGE_MONEY_REFUND 4; // 退款 ↓退给家属 private static final int STATUS_NORMAL 0; private static final int STATUS_ARREARS 1; private static final int STATUS_FROZEN 2; private static final int STATUS_CLEAN 3;changeType方向典型场景1 充值余额增加前台收款、核算单缴费2 扣费余额减少月账单扣费、手工扣费3 退费余额增加多扣冲正4 退款余额减少退住退预交金给家属3. openAccount 与 getOrOpen合同生效、核算单缴费后开户同一老人不能重复开户。Override Transactional(rollbackFor Exception.class) public Long openAccount(CareAccountOpenReqVO reqVO) { validateOlder(reqVO.getOlderId()); if (careAccountMapper.selectByOlderId(reqVO.getOlderId()) ! null) { throw exception(ACCOUNT_ALREADY_EXISTS); } CareAccountDO account new CareAccountDO(); account.setOlderId(reqVO.getOlderId()); account.setBalance(BigDecimal.ZERO); account.setDeposit(BigDecimal.ZERO); account.setStatus(STATUS_NORMAL); account.setRemark(reqVO.getRemark()); careAccountMapper.insert(account); return account.getId(); } Override Transactional(rollbackFor Exception.class) public Long getOrOpen(Long olderId) { CareAccountDO exists careAccountMapper.selectByOlderId(olderId); if (exists ! null) { return exists.getId(); } CareAccountOpenReqVO req new CareAccountOpenReqVO(); req.setOlderId(olderId); return openAccount(req); }insert时MetaObjectHandler自动写createTime、creator、deptId见第 20 篇。开户本身不产生流水首笔充值走change。图2 账户列表可按 checkinStatus 筛在住老人 · 与 care_checkin.status 联动4. change前台充值/扣费/退款入口Override Transactional(rollbackFor Exception.class) public Long change(CareAccountChangeReqVO reqVO) { String remark reqVO.getRemark(); String payWay null; if (Objects.equals(reqVO.getChangeType(), CHANGE_RECHARGE) || Objects.equals(reqVO.getChangeType(), CHANGE_MONEY_REFUND)) { if (StrUtil.isNotBlank(reqVO.getPayWay())) { payWay reqVO.getPayWay().trim(); String label DictFrameworkUtils.parseDictDataLabel(care_pay_way, payWay); remark StrUtil.isBlank(remark) ? label : remark label; } } return doChange(reqVO.getOlderId(), reqVO.getWalletType(), reqVO.getChangeType(), reqVO.getAmount(), reqVO.getExpenseItem(), MANUAL, null, remark, payWay); }对齐旧系统充值、退款必须选交易方式care_pay_way现金/微信/银行卡…字典翻译后写入流水 remark收据打印与财务对账用。业务模块内部扣费不经过 Controller 手工 change而是public Long changeBiz(Long olderId, Integer walletType, Integer changeType, BigDecimal amount, String expenseItem, String bizType, Long bizId, String remark) { return doChange(olderId, walletType, changeType, amount, expenseItem, bizType, bizId, remark, null); }bizTypebizId关联月账单、核算单、退住结算单——反查账务靠流水不靠备注。5. doChange 内核余额 流水同事务private Long doChange(Long olderId, Integer walletType, Integer changeType, BigDecimal amount, String expenseItem, String bizType, Long bizId, String remark, String payWay) { // 1. 校验金额、账户存在或 getOrOpen // 2. 根据 changeType 判断 increase / decrease boolean increase Objects.equals(changeType, CHANGE_RECHARGE) || Objects.equals(changeType, CHANGE_REFUND); // 3. 更新 care_account.balance 或 deposit // 4. insert care_account_flow // 5. 余额 0 → status STATUS_ARREARS }要点整段在Transactional内任一步失败回滚避免「流水有、余额没变」。扣费前校验余额不足可抛ACCOUNT_BALANCE_NOT_ENOUGH月账单 Job 可标欠费继续跑。changeBizIsolated用REQUIRES_NEW供退住结算等需要独立事务的场景。图3 套餐月费科目展开为 bill_item扣费时 expenseItem 可追溯6. 账户列表分页与老人、入住状态联查private SetLong resolveAccountOlderFilter(CareAccountPageReqVO pageReqVO) { SetLong olderIds null; if (StrUtil.isNotBlank(pageReqVO.getOlderNo()) || StrUtil.isNotBlank(pageReqVO.getOlderName())) { // care_older 模糊 → olderIds } if (pageReqVO.getCheckinStatus() ! null) { ListCareCheckinDO checkins careCheckinMapper.selectList( new LambdaQueryWrapperXCareCheckinDO() .eq(CareCheckinDO::getStatus, pageReqVO.getCheckinStatus()) .inIfPresent(CareCheckinDO::getOlderId, olderIds)); return checkins.stream().map(CareCheckinDO::getOlderId).collect(Collectors.toSet()); } return olderIds; }财务筛「在住且欠费」checkinStatus3accountStatus1先缩小 olderId 再分页care_account——模式与入住一览resolveOlderFilter相同见第 15、17 篇。图4 在住老人才产生日常月扣 · 与 checkin.status3 一致7. 账户状态机freeze / settle / reopenpublic void updateStatus(CareAccountUpdateStatusReqVO reqVO) { switch (action) { case freeze: newStatus STATUS_FROZEN; break; case unfreeze: newStatus 余额0 ? STATUS_ARREARS : STATUS_NORMAL; break; case settle: // 余额与押金均为 0 才可销户 case reopen: newStatus STATUS_NORMAL; break; } careAccountMapper.updateById(update); }退住结算后账户销户STATUS_CLEAN老人再次入住需reopen或重新开户策略由业务配置。图6 中台档案可查看消费类通道汇总 · 机构流水为数据源8. 月账单与定时扣费概要生成扫描在住老人按套餐展开care_month_bill_item请假停费科目在 Service 层置零。扣费Job 读care_billing_setting日扣/月扣、扣费日 1–28TenantUtils.execute按租户执行。入账changeBiz(olderId, WALLET_BALANCE, CHANGE_DEDUCT, amount, itemName, MONTH_BILL, billId, remark)。同合同同月账单重复ServiceselectByContractAndMonth判重表不设 UNIQUE软删场景由应用保证。图5 经营侧床位与在住人数 · 与账户在住筛选口径一致图7 评估等级变更影响套餐月费 · 费用与照护配置联动9. CareAccountController 与权限PostMapping(/change) PreAuthorize(ss.hasPermission(care:account:change)) public CommonResultLong change(Valid RequestBody CareAccountChangeReqVO reqVO) { return success(careAccountService.change(reqVO)); } GetMapping(/page) PreAuthorize(ss.hasPermission(care:account:query)) public CommonResultPageResultCareAccountRespVO getPage(Valid CareAccountPageReqVO reqVO) { return success(careAccountService.getAccountPage(reqVO)); }导出流水、批量扣费、提醒缴费等接口同样挂care:account:*权限码与菜单 SQL 初始化一致。10. curl 联调curl -X POST http://localhost:48080/admin-api/care/account/change \ -H Authorization: Bearer {token} \ -H tenant-id: 1 \ -H Content-Type: application/json \ -d { olderId: 1001, walletType: 1, changeType: 1, amount: 5000, payWay: cash, remark: 预交金充值 }成功返回data为流水 id。再查/care/account/flow/page?olderId1001应对齐余额。11. 排错余额与流水不一致是否绕过doChange直接 update 余额。重复扣费月账单 Job 是否判断「已扣费」状态。交易方式缺失充值/退款未传payWay收据与旧系统不一致。销户失败settle时余额/押金未清零。开户重复ACCOUNT_ALREADY_EXISTS应用getOrOpen。12. 小结机构「钱」的代码中枢是CareAccountServiceImpl.doChange所有充扣退都落流水业务单用 bizType/bizId 关联。读懂开户 change 列表联查就能接上核算单、月账单、退住结算全链路。Gitee 社区版源码https://gitee.com/xinzexingjian-data_0/older_care