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

相关推荐
AI你一生一世8 小时前
不止于时序存储:DolphinDB 如何成为 AI 时代的工业智能底座
java·人工智能·struts·时序数据库·工业智能·dolphindb·ai基础设施
布莱克6058 小时前
C/C++ 中变量的声明与定义:区别、联系与常见误区
c语言·开发语言·c++·声明·定义
云-缩影9 小时前
拆解 AI 图像生成应用的后端架构:ECS、OSS 和大模型 API 怎么配合
java·spring boot·后端
LabVIEW开发9 小时前
LabVIEW 局部变量为何引发竞争条件?四种同步机制
开发语言·labview
瑞克-桑切斯9 小时前
7、MySQL 慢查询排查与优化实战指南
java·服务器·数据库
benchmark_cc9 小时前
A 股、港股、美股如何建立统一的标的主数据格式?
开发语言·python·数据分析·pandas·量化交易·股票数据·quantdash
Zenova EdgeOS10 小时前
Go 工业边缘错误处理实战:用 errors 构建可追溯错误链
开发语言·数据库·golang
取名真是11 小时前
Python 算法知识点从基础到进阶
开发语言·python·算法
FRing0311 小时前
vue2干货总结3(computed计算属性、watch监听器)
开发语言·前端·vue.js·vue2
Java小卷12 小时前
JuanForm vs FormCreate!Vue3 表单引擎联动配置 PK!
vue.js·低代码