Java vue 养老系统源码详解:月账单 care_month_bill 与 Quartz 扣费 Job 实践

Java养老系统源码详解:月账单 care_month_bill 与 Quartz 扣费 Job 实践

在线演示 :管理端后台

标签:#Java #Spring Boot #MyBatis #养老系统 #月账单 #Quartz #care_org_config


1. 月账单何时生成、何时扣款

财务问「账单有了没、钱扣了没」------费用管理 · 月账单 对应 CareMonthBillServiceImpl.generate;定时 careMonthBillGenerateJob 月初跑生成,careMonthBillFullDeductJob 在机构扣费日调 executeDeduct。日扣门店另走 careDayFeeDeductJob ,和 计费设置 care_org_config.dayBilling 联动。

图1 月账单列表 · 生成/扣费

图2 扣费流水

2. CareMonthBillGenerateJob(月初生成)

复制代码
@Component("careMonthBillGenerateJob")
public class CareMonthBillGenerateJob implements JobHandler {

    @Override
    @TenantJob
    public String execute(String param) {
        String billMonth = StrUtil.blankToDefault(
                CareBillingJobMeta.resolveRuntimeParam(param), YearMonth.now().toString());
        CareMonthBillGenerateReqVO req = new CareMonthBillGenerateReqVO();
        req.setBillMonth(billMonth);
        CareMonthBillActionRespVO result = careMonthBillService.generate(req);
        return result.getMessage();
    }
}

Cron 建议 0 0 1 1 * ?;param 可传 yyyy-MM 补跑。

3. CareMonthBillServiceImpl.generate(扫生效合同)

复制代码
public CareMonthBillActionRespVO generate(CareMonthBillGenerateReqVO reqVO) {
    contracts = careContractMapper.selectListActive();
    for (CareContractDO contract : contracts) {
        if (generateOne(contract, ym, billMonth, true)) {
            success++;
        } else {
            skip++;
        }
    }
    int careExpanded = expandCareExecForBillMonth(ym);
    return new CareMonthBillActionRespVO(success, skip, fail, message);
}

合同生效时 generateForContractRange 铺首月区间,syncPaymentBill=false 时不重复登记单。

4. CareMonthBillFullDeductJob(扣费日整单扣)

复制代码
@Component("careMonthBillFullDeductJob")
public class CareMonthBillFullDeductJob implements JobHandler {
    @Override
    @TenantJob
    public String execute(String param) {
        if (careOrgConfigService.isDayBilling(rootDeptId)) {
            continue;
        }
        if (!Objects.equals(careOrgConfigService.getMonthDeductMode(rootDeptId),
                CareOrgConfigService.MONTH_DEDUCT_FULL)) {
            continue;
        }
        req.setUntilDate(ym.atEndOfMonth());
        careMonthBillService.executeDeduct(req);
    }
}

billingDayMonth 限制 1~28,避免 2 月无 29 日。

5. Controller 与 curl

复制代码
curl -X POST "http://localhost:48080/admin-api/care/month-bill/generate" \
  -H "Authorization: Bearer {token}" -H "tenant-id: 123" \
  -H "Content-Type: application/json" \
  -d '{"billMonth":"2026-09"}'

curl -X POST "http://localhost:48080/admin-api/care/month-bill/deduct" \
  -H "Authorization: Bearer {token}" -H "tenant-id: 123" \
  -d '{"billMonth":"2026-09","untilDate":"2026-09-30","rootDeptId":10}'

补充:日明细与 changeBiz

月账单头在 care_month_bill,日扣在 care_daily_charge;真正扣余额统一走 CareAccountService.changeBiz ,bizType=MONTH_BILL。

图3 机构计费设置 · 日扣/月扣、扣费日 1~28

图4 财务常对照在住人数与应扣笔数

复制代码
// CareMonthBillServiceImpl.executeDeduct(节选)
for (CareDailyChargeDO row : pendingRows) {
    careAccountService.changeBiz(olderId, WALLET_BALANCE, CHANGE_DEDUCT,
            row.getAmount(), row.getExpenseCode(), "MONTH_BILL", billId, remark, null);
    row.setStatus(DAILY_DONE);
    careDailyChargeMapper.updateById(row);
}

@TableName("care_month_bill")
public class CareMonthBillDO extends CareOrgBaseDO {
    private String billMonth;
    private Long contractId;
    private Long olderId;
    private BigDecimal amountDue;
    private BigDecimal amountPaid;
    private Integer status;
}
Job 名 作用
careMonthBillGenerateJob 月初生成账单
careMonthBillFullDeductJob 扣费日整月扣
careDayFeeDeductJob 日扣模式按日入账

图5 仅对在住且合同生效老人生成/扣费

6. 踩坑

  • Job 无 Bean :Quartz handlerName 须与 @Component 名一致。
  • 整单扣 skip :日扣院 isDayBilling=true 不走 full deduct。
  • 无生效合同:generate 抛 MONTH_BILL_NO_ACTIVE_CONTRACT。
  • 定时线程 tenant :必须 @TenantJob。

7. CareMonthBillDeductJob(日明细滚动扣)

除整单扣外,部分机构配置 careMonthBillDeductJob 按日滚动扣待扣明细(与 dayBilling 组合以源码为准)。运维在 基础设施 · 定时任务 查看 handlerName:

复制代码
careMonthBillGenerateJob   # 月初生成
careMonthBillFullDeductJob # 扣费日整单
careDayFeeDeductJob        # 日扣门店

手动触发示例(jobId 以库 infra_job 为准):

复制代码
curl -X PUT "http://localhost:48080/admin-api/infra/job/trigger?id=9101" \
  -H "Authorization: Bearer {token}" -H "tenant-id: 123"

8. getFlowDetail(对账)

复制代码
@GetMapping("/flow-detail")
public CommonResult<CareMonthBillFlowDetailRespVO> getFlowDetail(@RequestParam("id") Long id) {
    return success(careMonthBillService.getFlowDetail(id));
}

财务按 monthBillId 拉当月账户扣费/退费流水,与 费用管理 · 资金账户 明细交叉核对。

补充:日志与排错顺序

  1. 浏览器 Network 看 /admin-api 返回 code/msg。

  2. IDEA 断点 Service 入口,确认 tenant-id 与业务 id。

  3. 查库 status 与界面一致(勿只改 checkin 不改 bed)。

  4. 定时任务看 infra_job 日志与 @TenantJob 租户上下文。

    curl -X POST -H "tenant-id: 123" -H "Content-Type: application/json"
    -d "{"phone":"13900001001","password":"123456","accountType":2}"
    http://127.0.0.1:8086/app-api/care/family-auth/login

联调:application-local 与 tenant-id

本地 zhijian-server 联调时,管理端请求统一带头:

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

curl -H "Authorization: Bearer {token}" -H "tenant-id: 123" \
  "http://localhost:48080/admin-api/care/..."

403 先查角色是否勾选菜单按钮权限;401 查 token 是否过期。多园环境 rootDeptId 与登录部门一致,否则计费/报表可能看到空列表。

模块路径(Maven)

模块 典型包路径
institution zhijian-module-institution/.../service/
hub zhijian-module-hub/.../service/hub/
bi zhijian-module-bi/.../service/bi/
monitor zhijian-module-monitor/.../
community zhijian-module-community/.../
medical zhijian-module-medical/.../

交付裁剪时 zhijian-server/pom.xml 注释未购业态 JAR,并执行 care_maven_delivery_menu_crop.sql 隐藏菜单,避免「有菜单无 API」。

与 hub 中台衔接

老人主档 care_older 在 hub;机构业务 care_checkin / care_contract 在 institution。中台档案页不存 bed_id,床态以 care_bed.status 为准(见 24 篇)。跨模块只通过 olderId、SPI Bridge 或 REST,不在 hub 里直接 join institution 大表。

演示站对照

在线演示:管理端后台 --- 登录后左侧菜单与源码 @PreAuthorize 权限码一致;改 SQL 菜单后需重新登录刷新路由。


Gitee 社区版源码 :https://gitee.com/xinzexingjian-data_0/older_care

相关推荐
Wang's Blog5 小时前
Java 项目实战: 交友项目 - 安装开发工具与环境准备
java·开发语言·交友
用户21296835261686 小时前
开源了一个 Java 多智能体 LLM 框架 Baton:一个依赖,把 Agent 循环、工具调用、记忆、编排全配齐
java·llm
helmsail6 小时前
05 管理活动与选品
java
天远数科6 小时前
Java数据工程:利用天远人车核验加强版优化智慧高速ETC发行合规体验
java·人工智能
.Bryce.6 小时前
在智能体中使用A2A工具:让大模型自己决定“该找谁帮忙“
android·java·服务器·redis·后端·agent·a2a
xiaoqiMikko6 小时前
fastjson 1.2.84 的补丁关掉了一个缓存:addAccept 包前缀白名单下,反序列化慢了 13~28 倍
java·后端·性能优化
XIAEE6 小时前
转生到儿童编程异世界:我用 Vue 和 Scratch 建了一座 DREAM 学院
java·spring
XIAEE7 小时前
初次使用 Node + Koa + MongoDB 搭建服务器:从零到第一个 API
java·spring
m0_587383007 小时前
无人自助拼豆系统核心功能开发与部署落地全流程实战指南
java·经验分享·后端·struts·架构