Files
jcgl/docs/04-核心业务流程.md

14 KiB
Raw Permalink Blame History

04 核心业务流程

本文按“人”和“房间”两条主线梳理:人从入寓到离寓,房间从占用到释放;同时说明叫乘任务如何被创建、执行和补偿。

1. 入寓登记

入口有两类:

  • 设备端:POST /jcgl/processCheckInRegistration(签到机)
  • 值班端:POST /jcgl/submitAll → JcglService.submit_rydj

1.1 设备端查询/建档(processCheckInRegistration)

code 行为
1 按 ryid 查询当前在寓记录、离寓快照、人员资料,返回给设备;用于设备判断“是否已入住/是否可入住”
2 由人员资料 + 选择的交路(jlid)或班次构造入住信息,通过 WebSocket {"lx":"ry","content":...} 推送值班端确认;房间号仅作为协议字段,不在此步写库
3 按 id 查询指定记录

设备端只负责“采集 + 推送”,真正的写入统一走值班端确认后的 submit_rydj。

1.2 值班端提交(submit_rydj,@Transactional)

按顺序执行:

  1. 重复入住校验:isExistCallDutyInfo(ryid, gyid),已有在寓记录直接失败。
  2. 房间校验:按 gyid + room_no 精确查房,房间不存在/状态不可入住/床位已满则失败。
  3. 交路处理:
    • jllx='0' 表示按交路入住。
    • 若传了 jlid(固定交路),从交路表取车次、机车号、起点、到达车次/时间,回填到入住记录。
    • 若 cc 包含“便乘/驻在”,jllx='1',不生成标准叫乘;否则调用 populateJcXxByJlXx 生成叫乘时间。
  4. 开车日期校验:有车次时必须传 kcrq,且格式为 yyyy-MM-dd。
  5. 补助计算:
    • 固定交路:getTrainAllowanceInfo(cc, sfffcb, gyid, jlid)。
    • 非固定交路:getAllowanceInfo(gz, sfffcb, sjrysj, gyid)(按工种、是否发补助、入住时间规则)。
    • changeRemainder 生成/更新余额与补助明细;节假日可能跳过余额与补助写入。
  6. 写入住记录:insertJcxx,ryzt='0'、finish_ly='0'、sjrysj=入住时间,并记录房间号。
  7. 设备指令:
    • 向签到机下发指令 5;
    • 向房间设备下发指令 1(刷新房间屏)。
  8. 占用重算:room_rydj → refreshRoomOccupancy,从有效在寓明细重算 live_person、person_name,再按床位计算房态。
  9. 余额/补助落账:非节假日执行 updateRemainder + insert_allowance。
  10. 叫乘任务:固定交路且 sfjc='1' 且非便乘/驻在时,事务提交后创建 WakeUpJob,并置 is_yaxx='1'。
  11. 推送:WebSocket {"lx":"refresh"}。

失败时统一回滚并写业务日志。

1.3 房间推荐(getRoomRecommendations)

  • 仅对“可入住”的房间给出推荐顺序;
  • 排序依据:同行房间(同车次/同批人)→ 历史房间(该人员曾住过);
  • 推荐结果不作为最终校验,提交时仍由房间状态与床位兜底。

2. 叫乘状态机

2.1 人员状态 ryzt

值 含义 进入方式
0 待叫乘/未叫乘 入住初始化、重置交路、任务取消
1 叫乘中(已叫/复叫) 任务执行、设备上报 status=1/2
2 叫乘报警(未应答) 报警任务、设备上报 status=3
3 已应答,待离寓 应答(status=5)或通话结束
4 离寓报警(超时未离寓) 超时检查、设备上报 status=4
5 正在通话 Asterisk 接通后临时状态
-1 查询用合成状态(未匹配/特殊行) SQL 构造,不落库
stateDiagram-v2
    state "0 待叫乘" as S0
    state "1 叫乘中" as S1
    state "2 叫乘报警" as S2
    state "3 已应答待离寓" as S3
    state "4 离寓报警" as S4
    state "5 通话中" as S5

    [*] --> S0
    S0 --> S1: JC 任务 / 设备 status=1
    S1 --> S1: FJ 复叫 / status=2
    S1 --> S5: 电话接通
    S5 --> S3: 挂断 / 应答
    S1 --> S2: 未应答 / status=3
    S2 --> S3: 人工应答 / status=5
    S3 --> S4: 超时未离寓
    S3 --> [*]: 办理离寓
    S4 --> [*]: 办理离寓

配套字段:

  • jcsj_s / fjsj_s / jcbjsj_s:实际叫乘/复叫/报警时间。
  • hjxysj:呼叫应答时间。
  • finish_jc:叫乘是否结束(1 结束)。
  • finish_ly:是否离寓(1 离寓)。
  • stoped:是否人工停止叫乘。
  • is_jcbj / is_lybj:叫乘报警/离寓报警标记。

2.2 设备上报(processCallDuty)

设备 POST /jcgl/processCallDuty,status 语义:

status 动作
1 开始叫乘:ryzt=1、记录 jcsj_s
2 开始复叫:记录 fjsj_s(状态保持叫乘中)
3 叫乘报警:若原状态 < 2 则 ryzt=2,is_jcbj=1,记录 jcbjsj_s,写报警记录
4 离寓报警:triggerLeaveAlarm(条件更新,防重复),写报警记录
5 呼叫应答:ryzt=3、hjxysj、finish_jc=1、stoped=0

临时叫乘走 /jcgl/processTempCallDuty,语义相同,但会额外刷新对应房间设备。

2.3 自动叫乘(Quartz)

入住/修改交路时为一个人创建 3 个一次性任务:

任务 触发时间 行为
JC jcsj 开始叫乘:状态置 1,TTS 呼叫值班台 + 房间;未接通则按规则转报警
FJ fjsj(叫乘 +10 分钟) 复叫:再次呼叫;仍未接通转报警
JCJS jcbjsj(叫乘 +15 分钟) 叫乘结束:挂断会议桥、状态置 2、写报警记录

任务键:ryid + "JC"/"FJ"/"JCJS",组:gyid + fjh;同一房间/车次会合并为一个执行组,组长优先呼叫。任务执行前会校验 jcid/fjh/时间 是否仍与库中一致,避免过期任务误叫。

补偿:

  • SchedulerManager 统一创建/取消/重建;
  • WakeupJobRecoveryRunner 在应用启动后恢复未来任务;
  • 会议桥忙时延迟 60 秒再试;
  • 人工“停止叫乘”会取消任务、挂断通话、置 stoped=1。

2.4 叫乘时间规则

叫乘时间统一由开车时间 + 本地时间规则计算(运安下发的 jcsj 仅保留读取,不作为最终依据):

  • 规则来自 GY_SJGZ(按 gyid + 单位):
    • zjjcsjgz:站接叫乘提前分钟数;
    • kjjcsjgz:库出叫乘提前分钟数;
    • lysjgz:离寓时间规则。
  • 站接:jcsj = kcsj - zjjcsjgz;库出:jcsj = kcsj - kjjcsjgz。
  • fjsj = jcsj + 10 分钟;jcbjsj = jcsj + 15 分钟;ylysj = jcsj + lysjgz。
  • 若计算出的叫乘时间已过期:只发 TTS 提醒,不生成报警记录(adjustPastCallDutyTimes)。

3. 运安交路同步与匹配

3.1 推送式同步(POST /jcgl/synCallDutyInfo)

  1. 校验来源 IP 是否在 baseConfig.synJcXxIp 白名单。
  2. 解析 JSON 快照,校验 id、workno、deptime 必填,id 唯一。
  3. 获取 CallDutyMatchLock.tryAcquireSync()(数据库协调行 + 租约),避免与抓取/匹配并发。
  4. TRUNCATE GY_SYN_JCXX → 批量插入 → GY_SYN_JCXX_STATUS='1'。
  5. 事务提交后释放锁;返回 {code,message,result}。

3.2 主动抓取(ByxxResourceService.fetchJlXX)

  1. 获取同步锁。
  2. 加载 classpath:/wsdl/PbResource.wsdl,创建 SOAP 客户端。
  3. 清空 GY_SYN_JCXX。
  4. 调用运安 getDriverPlanByTimeStr(start, end),把司机 1–4 的计划分别写入快照。
  5. 更新同步状态为 1,记录日志。

mergeSynJcXx 已停用:历史数据存在同一 id 对应多名司机,按 id MERGE 会重复匹配。

3.3 匹配(/jcgl/synUnMatchJcXx → matchCallDutyUnderLock)

前置条件:GY_SYN_JCXX_STATUS='1'(同步完成),且配置了 baseConfig.gyid(匹配只处理该公寓)。

流程:

  1. 获取数据库行锁,串行匹配。
  2. 查询 jllx='0' 的有效在寓记录,跳过人工锁定交路的记录;若配置不允许“叫乘完成后变更交路”,则跳过已完成叫乘且未离寓的人员。
  3. 以人员 card_id(工号)匹配 GY_SYN_JCXX.workno。
  4. 命中且交路有变化:按开车时间 + 本地规则重算叫乘/复叫/报警/离寓时间,更新当前入住记录,取消并重建 3 个 Quartz 任务;事务提交后写“交路变更/首次自动匹配/重新匹配”事件日志并 TTS 提醒。
  5. 命中但交路未变:检查任务是否齐全,缺失则补建。
  6. 未命中且该人有开车时间:清空交路与时间、取消任务、TTS 通知本人。
  7. 汇总写同步日志,WebSocket "all" 刷新。

3.4 归档

POST /jcgl/syncJcXxData:

  • 条件:finish_ly='1' AND sjlysj <= sysdate - 7,且属于当前公寓。
  • 分批 500 条:复制到 GY_JCXX_HIS → 从 GY_JCXX 删除。
  • 统计冲突/无效条数并写日志。
  • 每日 00:00 也有自动归档任务。

4. 离寓

4.1 刷脸离寓(processCheckOutRegistration)

code 行为
1 按 ryid + gyid 查询在寓记录与人员资料,返回离寓页面数据
2 刷脸离寓:校验事件幂等 → 保存抓拍 → 更新离寓字段 → 写离寓事件 → 更新房间 → 推送刷新

code=2 的关键点:

  1. 幂等:deviceCode + deviceEventId 已存在则直接返回“已经处理”;缺失事件号时只记录日志、不阻断(兼容旧设备)。
  2. 抓拍:Base64 只接受 JPEG;按 leaveSnapshot 配置保存到本地目录,生成 /yktgl/photo/leave-snapshots/... URL;未开启抓拍时按 requiredForDevice 决定是否报错。
  3. 服务评分:请求可带 kffwpf/jcfwpf/stfwpf,写入离寓记录。
  4. 事件证据:recordLeaveEvent 写 GYGL_LY_EVENT(类型 FACE,含 deviceEventId、snapshotUrl、操作人、备注)。
  5. 提交离寓:submit_ccly → 更新 GY_JCXX(finish_ly=1、sjlysj)→ room_ccly 释放房间 → 房间设备刷新 → remakeRemainder 处理余额(如退补助)。
  6. 失败回滚:异常时删除已保存快照并回滚事务。

4.2 值班端离寓

POST /jcgl/handleCheckOut 走同一套 submit_ccly,离寓类型记为 MANUAL,无设备事件号与抓拍。

4.3 超时未离寓

JcSchedule 每 15 秒检查:finish_jc='1'、ryzt='3'、ylysj <= now、未触发过的记录 → triggerLeaveAlarm(条件更新 ryzt=4、lybjsj、is_lybj=1)→ 写报警记录。

5. 房间与保洁

5.1 房态

room_status 含义 计算/设置
1 空闲 结束保洁、恢复可入住
2 住满 live_person >= bed_numbers
3 待清扫 空房(live_person=0),退房后的默认状态
4 不能入住 人工设置(要求房间无人)
5 未满 0 < live_person < bed_numbers
6 保洁中 保洁开始;结束后回到 1

约束:

  • 房间占用始终由 GY_JCXX 有效在寓明细重算,不直接 ±1。
  • 入住校验会重新计算房态,拒绝 4(不能入住)、6(保洁中)、满员房间。
  • 保洁中不允许直接改房态;需先结束保洁。

5.2 换房(/jcgl/exchangeRoom)

  1. 校验新房间可入住;
  2. 校验人员当前房间未变化(防并发);
  3. 更新入住记录 fjh;
  4. 重算旧房、新房占用;
  5. 向两个房间设备下发刷新指令。

6. 卡务:补助、充值、消费

6.1 补助

  • 入住时按规则计算补助,写 GY_YKT_ALLOWANCE 并更新 GY_YKT_REMAINDER 的补助余额。
  • 固定交路按车次查固定补助;非固定交路按工种/时间规则。
  • 节假日策略:isHoliday() 命中时不发补助、不入账。

6.2 现金充值(RechargeService)

设备协议 code:

code 行为
1 查询余额
2 创建充值申请(GY_YKT_CASH_REQUEST,状态 PENDING),WebSocket "cz" 通知值班端
3 查询申请状态
4 取消申请(CANCELLED)

值班端处理 /recharge/handleRecharge:

  • 金额必须 > 0 且 < 100,最多 2 位小数;
  • 通过条件更新把申请从 PENDING 置为 COMPLETED(与取消互斥);
  • 更新余额、写充值记录、向设备下发指令 6。

6.3 消费

  • 消费机:PayService,code 1 查余额,code 2 扣款;节假日金额按 0 处理;“内部人员”可透支。
  • 售货机:VendPayDeviceService,code 1 查余额,code 2 下单扣款,code 3 查询结果/退款;退款时恢复余额并标记 refunded=1。
  • 所有扣款都写 GY_YKT_CONSUME,并与余额更新同事务。

7. 设备心跳与指令

  1. 设备按类型调用 /callingDevice/heartbeat、/checkInDevice/heartbeat、/roomDevice/heartbeat、/payDevice/heartbeat、/registDevice/heartbeat、/vendPayDevice/heartbeat。
  2. 心跳上报:deviceCode、versionName、deviceType、ip、deviceUpdateState、lastUserUpdateTime、lastDeviceConfigUpdateTime、roomNumber 等。
  3. 服务端返回:devicePassword、apartmentName、serverTime、updateState、apkUrl、指令列表。
  4. 指令写入设备指令表,心跳时取走并置 deleted=1(一次性投递)。
  5. 叫乘/入住/离寓/充值/人员变更都会产生指令。

8. 流程与代码位置对照

流程 主要方法
入住提交 JcglService.submit_rydj
设备入住 JcglService.processCheckInRegistration
叫乘上报 JcglService.processCallDuty / processTempCallDuty
叫乘执行 quartz/WakeUpJob、quartz/SchedulerManager
电话/会议桥 jcgl/service/AsteriskCallUtil
离寓 JcglService.processCheckOutRegistration / submit_ccly
交路推送 JcglService.synCallDutyInfo
交路匹配 JcglService.matchCallDutyUnderLock
运安抓取 ByxxResourceService.fetchJlXX
归档 JcglService.syncJcXxData
房间 JcglService.getRoomRecommendations / modifyRoomStatus / exchangeRoom
充值 RechargeService
消费 PayService / VendPayDeviceService