本文档整理了从Kafka获取的与JW(教务)系统相关的数据同步逻辑,以及相应的接口响应JSON格式。
| Topic名称 | 常量定义 | 说明 |
|---|---|---|
| eventBus | OLD_SYNC_TOPIC |
第三方对接主题,用于与旧系统(包括教务系统)数据同步 |
| sync_data_topic | SYNC_DATA_TOPIC |
一卡通内部操作双向同步主题 |
| sync_to_cloud | TO_CLOUD_TOPIC |
云平台和本地部署同步主题 |
| 发送者代码 | 常量名称 | 说明 |
|---|---|---|
| 002 | TRAIN |
教务系统 |
| 003 | HR |
人事系统 |
| 005 | YKT |
一卡通系统 |
| 事件类型代码 | 常量名称 | 说明 |
|---|---|---|
| 00200001 | TRAIN_CLASS_ADD |
班级增加 |
| 00200002 | TRAIN_CLASS_EDIT |
班级修改 |
| 00200003 | TRAIN_CLASS_DEL |
班级删除 |
| 00200004 | TRAINEE_ADD |
学员增加 |
| 00200005 | TRAINEE_EDIT |
学员修改 |
| 00200006 | TRAINEE_DEL |
学员删除 |
| 00500001 | CONSUME_RECORD |
消费记录(推送给教务) |
{
"header": {
"eventId": "事件唯一标识",
"sender": "发送者代码(002-教务, 003-人事, 005-一卡通)",
"eventType": "事件类型代码",
"tenantId": "租户ID"
},
"body": {
// 具体业务数据
}
}
处理类: TrainEventStrategyImpl
Kafka Consumer: KafkaCloudConsumer.kafkaEventBusHandler()
Topic: eventBus
Sender: 002
事件类型:
TRAIN_CLASS_ADD (00200001) - 班级增加TRAIN_CLASS_EDIT (00200002) - 班级修改TRAIN_CLASS_DEL (00200003) - 班级删除调用服务:
remoteKafkaSyncService.syncTrainClass()remoteKafkaSyncService.syncDelTrainClass()数据处理:
TrainUtils.getSycClass()TrainUtils.getSycDeleteClass()说明: 通过 del_flag 字段区分操作类型
del_flag = 0 或无此字段: 增加或修改(根据班级ID是否存在判断)del_flag = 1: 删除Kafka消息Body格式 (增加/修改):
{
"id": "班级ID",
"name": "班级名称",
"year": 2025,
"xq": "学期(0-上学期, 1-下学期)",
"bmStarttime": "报名开始时间",
"bdTime": "报到时间",
"kbTime": "开班时间(开课时间)",
"byTime": "毕业时间(结业时间)",
"studentNum": 100,
"noPayAllow": "是否允许未缴费报到(0-不允许, 1-允许)",
"tenantId": "租户ID"
}
字段说明 (增加/修改):
| 字段名 | 类型 | 必填 | 说明 | 示例 | 映射到内部字段 |
|---|---|---|---|---|---|
| id | String | 是 | 班级ID | "CLASS001" | dept_id |
| name | String | 是 | 班级名称 | "2025春季计算机培训班" | dept_name |
| year | Integer | 是 | 年份 | 2025 | year |
| xq | String | 是 | 学期(0-上学期, 1-下学期) | "0" | semester(转换为"上学期"或"下学期") |
| bmStarttime | String | 是 | 报名开始时间/缴费开始时间 | "2025-01-15 08:00:00" | payBegin |
| bdTime | String | 是 | 报到时间 | "2025-02-20 09:00:00" | checkDate |
| kbTime | String | 是 | 开班时间(开课时间) | "2025-02-21 08:00:00" | beginDate |
| byTime | String | 是 | 毕业时间(结业时间) | "2025-07-20 17:00:00" | endDate, payEnd |
| studentNum | Integer | 否 | 计划人数(默认100) | 100 | planCount |
| noPayAllow | String | 否 | 是否允许未缴费报到(0-不允许, 1-允许) | "0" | payCheck |
| tenantId | String | 否 | 租户ID(无则使用默认) | "000000" | tenantId |
固定值 (增加/修改):
chooseRoom: "0" (是否自主选房)canEat: "1" (是否就餐)operatorId: KAFKA_SYNC_ADMIN常量值Kafka消息Body格式 (删除):
{
"id": "班级ID",
"del_flag": "1",
"tenantId": "租户ID"
}
字段说明 (删除):
| 字段名 | 类型 | 必填 | 说明 | 示例 | 映射到内部字段 |
|---|---|---|---|---|---|
| id | String | 是 | 班级ID | "CLASS001" | dept_id |
| del_flag | String | 是 | 删除标志: "1"表示删除 | "1" | delFlag |
| tenantId | String | 否 | 租户ID(无则使用默认) | "000000" | tenantId |
业务逻辑:
事件类型:
TRAINEE_ADD (00200004) - 学员增加TRAINEE_EDIT (00200005) - 学员修改TRAINEE_DEL (00200006) - 学员删除调用服务:
remoteKafkaSyncService.syncTrainee()remoteKafkaSyncService.syncDelTrainee()数据处理:
TrainUtils.getSyncTrainee()TrainUtils.getSyncDeleteTrainee()说明: 通过事件类型区分操作,删除时数据结构不同
Kafka消息Body格式 (增加/修改):
{
"student": {
"id": "学员ID",
"name": "学员姓名",
"sex": "性别(1-男, 2-女)",
"phone": "联系电话",
"idCard": "身份证号",
"currentClassId": "当前班级ID"
},
"trainClassStudent": {
"studentId": "学员ID",
"classId": "班级ID",
"status": "学员状态(1-已报名, 2-已报到)"
},
"tenantId": "租户ID"
}
字段说明 (增加/修改):
student对象:
| 字段名 | 类型 | 必填 | 说明 | 示例 | 映射到内部字段 |
|---|---|---|---|---|---|
| id | String | 是 | 学员ID | "STU001" | userId |
| name | String | 是 | 学员姓名 | "张三" | realName |
| sex | String | 是 | 性别(1-男, 2-女) | "1" | sex |
| phone | String | 是 | 联系电话 | "13800138000" | phone |
| idCard | String | 否 | 身份证号 | "110101199001011234" | idNumber |
| currentClassId | String | 是 | 当前班级ID | "CLASS001" | deptId |
trainClassStudent对象:
| 字段名 | 类型 | 必填 | 说明 | 示例 | 映射到内部字段 |
|---|---|---|---|---|---|
| studentId | String | 是 | 学员ID | "STU001" | userId |
| classId | String | 是 | 班级ID | "CLASS001" | deptId |
| status | String | 是 | 学员状态 | "1" | delFlag(1或2为"0"表示有效, 其他为"2"表示删除) |
固定值 (增加/修改):
category: CATEGORY_TRAINEE常量值(学员类别)postCode: TRAINEE_CODE常量值(学员岗位编码)operatorId: KAFKA_SYNC_ADMIN常量值Kafka消息Body格式 (删除):
{
"id": "学员ID",
"classId": "班级ID",
"del_flag": "1",
"tenantId": "租户ID"
}
字段说明 (删除):
| 字段名 | 类型 | 必填 | 说明 | 示例 | 映射到内部字段 |
|---|---|---|---|---|---|
| id | String | 是 | 学员ID | "STU001" | userId |
| classId | String | 是 | 班级ID | "CLASS001" | deptId |
| del_flag | String | 是 | 删除标志: "1"表示删除 | "1" | - |
| tenantId | String | 否 | 租户ID(无则使用默认) | "000000" | tenantId |
业务逻辑:
处理类: BaseBusiness.sendConsumeToKafka()
Kafka Producer: kafkaNormalProducer.sendKafkaMessage()
Topic: eventBus
Sender: 005(一卡通系统)
EventType: CONSUME_RECORD (00500001)
当消费记录上传完成后,系统会将就餐打卡信息推送到Kafka,教务系统消费此消息实现就餐打卡功能。
API接口:
POST /v1/Consumes/Consume/kafka/{date}
POST /v1/Consumes/Consume/kafka/{beginDate}/{endDate}
Kafka消息Body格式:
{
"recordId": "消费记录ID",
"userId": "人员ID",
"userNumb": "学号",
"xm": "姓名",
"deptId": "部门ID",
"deptName": "部门名称",
"roomId": "消费地点ID",
"roomName": "地点名称",
"cardNo": "卡流水号",
"factoryFixId": "物理卡号",
"consumeValue": "消费金额",
"cardValue": "卡余额",
"consumeDate": "消费时间",
"mealTypeId": "餐类ID",
"mealName": "餐类名称",
"termNo": "机号",
"termName": "机器名称",
"category": "身份类别(0-系统内置 1-教师 2-学生 3-家长)",
"otherSysId": "其他业务系统人员ID(教务或人事的人员ID)",
"classId": "班级ID",
"termRecordID": "机器流水号",
"posRecordState": "消费记录标识",
"tenantId": "租户ID"
}
| 字段名 | 类型 | 说明 | 示例 |
|---|---|---|---|
| recordId | String | 消费记录ID | "123456" |
| userId | String | 人员ID | "789" |
| userNumb | String | 学号 | "2024001" |
| xm | String | 姓名 | "张三" |
| deptId | String | 部门ID | "100" |
| deptName | String | 部门名称 | "计算机学院" |
| roomId | String | 消费地点ID | "" |
| roomName | String | 地点名称 | "" |
| cardNo | String | 卡流水号 | "35193" |
| factoryFixId | String | 物理卡号 | "3656457030" |
| consumeValue | String | 消费金额 | "15.50" |
| cardValue | String | 卡余额 | "184.50" |
| consumeDate | String | 消费时间 | "2025-02-19 19:32:30" |
| mealTypeId | String | 餐类ID | "1" |
| mealName | String | 餐类名称 | "晚餐" |
| termNo | String | 机号 | "100" |
| termName | String | 机器名称 | "食堂1号窗口" |
| category | String | 身份类别 | "2" (学生) |
| otherSysId | String | 教务系统人员ID | "JW2024001" |
| classId | String | 班级ID | "CLASS001" |
| termRecordID | Long | 机器流水号 | 47309 |
| posRecordState | Integer | 消费记录标识 | 364 |
mealNameMap:
"1" -> "早餐"
"2" -> "午餐"
"3" -> "晚餐"
其他 -> "未知餐类"
条件: locationFlag = 'cloud'
@KafkaListener(topics = KafkaTopicConstants.OLD_SYNC_TOPIC, groupId = "old-to-cloud-group")
public void kafkaEventBusHandler(ConsumerRecord<String, String> record)
处理逻辑:
@KafkaListener(topics = KafkaTopicConstants.TO_CLOUD_TOPIC, groupId = "local-to-cloud-group")
public void kafkaToCloudHandler(ConsumerRecord<String, String> record)
条件: locationFlag = 'local'
@KafkaListener(topics = SYNC_DATA_TOPIC, groupId = "YTK_${spring.system.tenantId}")
public void cloudOperationSync(ConsumerRecord<String, String> record)
处理逻辑:
Service名称: 002
实现接口: IYktEventStrategy
@Service(EventSenderConstants.TRAIN) // "002"
public class TrainEventStrategyImpl implements IYktEventStrategy
处理的事件类型:
syncTrainClass() - 班级增加/修改syncTrainClass() - 班级增加/修改syncDelTrainClass() - 班级删除syncTrainee() - 学员增加/修改syncTrainee() - 学员增加/修改syncDelTrainee() - 学员删除说明:
Service名称: 003
实现接口: IYktEventStrategy
@Service(EventSenderConstants.HR) // "003"
public class TeacherEventStrategyImpl implements IYktEventStrategy
处理的事件类型:
syncTeacherDept()syncTeacherDept()syncDelTeacherDept()syncTeacher()syncTeacher()syncDelTeacher()Service名称: 120
实现接口: IYktEventStrategy
@Service(EventSenderConstants.CONSUME) // "120"
public class ConsumeEventStrategyImpl implements IYktEventStrategy
处理的事件类型:
remoteConsumeService.dealKafkaConsumeData()教务系统 → Kafka(eventBus) → KafkaCloudConsumer
→ TrainEventStrategyImpl → RemoteKafkaSyncService
→ 同步到云端数据库
消费机上传消费记录 → ConsumeController.uploadRecord()
→ ConsumeBusiness.postOrderAsync()
→ BaseBusiness.completeUploadRecord()
→ BaseBusiness.sendConsumeToKafka()
→ Kafka(eventBus) → 教务系统消费
API调用: POST /v1/Consumes/Consume/kafka/{date}
→ ConsumeController.consumeKafka()
→ BaseBusiness.sendToJwKafkaTest()
→ 查询未发送的消费记录
→ BaseBusiness.sendConsumeToKafka()
→ Kafka(eventBus) → 教务系统消费
输入数据 (ConsumptionBo):
{
"recordId": 123456,
"userId": 789,
"userNumb": "2024001",
"realName": "张三",
"deptName": "计算机学院",
"cardNo": 35193,
"factoryId": 3656457030,
"consumeMoney": 15.50,
"balance": 184.50,
"consumeDate": "2025-02-19 19:32:30",
"mealType": 3,
"termNo": 100,
"termName": "食堂1号窗口",
"termRecordId": 47309,
"recordStatus": 364
}
输出数据 (YcPushConsumeInfoVo):
{
"recordId": "123456",
"userId": "789",
"userNumb": "2024001",
"xm": "张三",
"deptId": "100",
"deptName": "计算机学院",
"roomId": "",
"roomName": "",
"cardNo": "35193",
"factoryFixId": "3656457030",
"consumeValue": "15.50",
"cardValue": "184.50",
"consumeDate": "2025-02-19 19:32:30",
"mealTypeId": "3",
"mealName": "晚餐",
"termNo": "100",
"termName": "食堂1号窗口",
"category": "2",
"otherSysId": "JW2024001",
"classId": "CLASS001",
"termRecordID": 47309,
"posRecordState": 364
}
# 应用配置
locationFlag: cloud # 或 local
# 租户配置
spring:
system:
tenantId: "000000"
| 常量类 | 路径 |
|---|---|
| KafkaTopicConstants | ruoyi-common-message/kafka/constant/KafkaTopicConstants.java |
| EventSenderConstants | ruoyi-common-message/kafka/constant/EventSenderConstants.java |
| EventTypeConstants | ruoyi-common-message/kafka/constant/EventTypeConstants.java |
系统会记录每条Kafka消息的消费状态:
记录表: RemoteSendMessageRecordBo
使用IXfOffsetService管理消费offset,防止重复消费:
POST /v1/Consumes/Consume/kafka/2025-02-19
POST /v1/Consumes/Consume/kafka/2025-02-19/2025-02-20
响应:
{
"code": 200,
"msg": "操作成功",
"data": null
}
文档版本: v1.0
生成日期: 2025-04-09
维护人员: 系统开发团队