feat(emergency): 新增急救员建群接口,人员接口返回手机号

- 新增 GET /api/emergency/createGroupMagFirstAider(不发起通话,自动拉关爱/急救员)
- createGroupMag 原始逻辑不受影响,新方法独立实现
- OrderOperator.operatorType 注释同步数据库(新增3/4/5)
- EmergencyProfessionUser/GroupMemberVO 新增 phone 字段
- searchExpert/getGroupMemberList 填充手机号
- API文档-移动端接口.md 同步更新
This commit is contained in:
2026-07-06 17:14:48 +08:00
parent 4cb3411eca
commit fd7fd32c8d
8 changed files with 287 additions and 15 deletions
+36 -11
View File
@@ -39,7 +39,7 @@
"userId": "xxx", "realname": "张医生", "avatar": "https://...",
"type": "6", "centerId": "xxx",
"post": "主任医师", "school": "北京大学", "edu": "博士",
"goodAt": "心血管内科", "deptName": "急诊科"
"goodAt": "心血管内科", "deptName": "急诊科", "phone": "13800000001"
}
],
"total": 50, "size": 10, "current": 1
@@ -58,6 +58,7 @@
| edu | 学历 |
| goodAt | 擅长领域 |
| deptName | 科室 |
| phone | 手机号 |
### 1.2 搜索员工 `GET /order/searchEmployee`
@@ -105,9 +106,21 @@
| latitude | String | 是 | 纬度 |
| type | String | 否 | 类型 |
> 返回 `groupId`(群组ID),作为后续拉人接口的入参。
> 返回 `groupId`(群组ID),作为后续拉人接口的入参。**此接口会发起音视频通话。**
### 1.4 邀请进群 `POST /addGroupUser`
### 1.4 创建群聊-急救员 `GET /createGroupMagFirstAider` 🆕
> 不发起音视频通话,建群后自动拉入关爱联络员和急救员(通过安眼系统查询)。
| 参数 | 类型 | 必填 | 说明 |
|------|------|:---:|------|
| name | String | 是 | 群聊名称 |
| longitude | String | 是 | 经度 |
| latitude | String | 是 | 纬度 |
> 返回结构与 `createGroupMag` 一致(`CreateGroupMagVO`),群成员 = 发起人 + 操作人员 + 专业人员 + 关爱联络员 + 急救员1 + 急救员2。
### 1.5 邀请进群 `POST /addGroupUser`
拉专家或员工加入应急群聊。
@@ -133,7 +146,7 @@
> 已在群中的人员会自动跳过,不会报错。所有选中的人都在群中时返回失败。
### 1.5 移除群成员 `POST /removeGroupUser`
### 1.6 移除群成员 `POST /removeGroupUser`
```json
// Request Body
@@ -152,7 +165,7 @@
> 只能移除后续拉入的协助人员,不能移除发起人和初始操作/专业人员。
### 1.6 群成员列表 `GET /order/getGroupMemberList`
### 1.7 群成员列表 `GET /order/getGroupMemberList`
| 参数 | 类型 | 必填 | 说明 |
|------|------|:---:|------|
@@ -161,11 +174,11 @@
```json
// 响应 result
[
{ "orderId": "...", "userId": "发起人id", "userName": "李四", "avatar": "https://...", "isGroupMember": 1, "memberType": -1 },
{ "orderId": "...", "userId": "操作人员id", "userName": "王五", "avatar": "https://...", "isGroupMember": 1, "memberType": 0 },
{ "orderId": "...", "userId": "专业人员id", "userName": "张医生", "avatar": "https://...", "isGroupMember": 1, "memberType": 1 },
{ "orderId": "...", "userId": "员工id", "userName": "张三", "avatar": "https://...", "isGroupMember": 1, "memberType": 2 },
{ "orderId": "...", "userId": "其他专业人id", "userName": "赵医生", "avatar": "https://...", "isGroupMember": 0, "memberType": 1 }
{ "orderId": "...", "userId": "发起人id", "userName": "李四", "avatar": "https://...", "phone": "13800000001", "isGroupMember": 1, "memberType": -1 },
{ "orderId": "...", "userId": "操作人员id", "userName": "王五", "avatar": "https://...", "phone": "13800000002", "isGroupMember": 1, "memberType": 0 },
{ "orderId": "...", "userId": "专业人员id", "userName": "张医生", "avatar": "https://...", "phone": "13800000003", "isGroupMember": 1, "memberType": 1 },
{ "orderId": "...", "userId": "员工id", "userName": "张三", "avatar": "https://...", "phone": "13800000004", "isGroupMember": 1, "memberType": 2 },
{ "orderId": "...", "userId": "其他专业人id", "userName": "赵医生", "avatar": "https://...", "phone": "13800000005", "isGroupMember": 0, "memberType": 1 }
]
```
@@ -175,10 +188,11 @@
| userId | 用户ID |
| userName | 姓名 |
| avatar | 头像 |
| phone | 手机号 |
| isGroupMember | `1`=在群中, `0`=可邀请 |
| memberType | `-1`=发起人, `0`=操作人员, `1`=专业人员, `2`=员工 |
### 1.7 memberType 字典
### 1.8 memberType 字典
| 值 | 含义 | 场景 |
|:---:|------|------|
@@ -189,10 +203,21 @@
### 典型调用流程
**应急就医(带音视频通话)**
```
1. 创建群聊
GET /health-emergency/api/emergency/createGroupMag?name=xxx&longitude=xxx&latitude=xxx
2. 查看当前成员 → 搜索要拉的人 → 确认邀请 → 移除成员 → 刷新列表
(同下方急救员流程步骤 2-6)
```
**急救员(不发起通话,自动拉关爱/急救员)**
```
1. 创建群聊
GET /health-emergency/api/emergency/createGroupMagFirstAider?name=xxx&longitude=xxx&latitude=xxx
→ 自动拉入:发起人 + 操作人员 + 专业人员 + 关爱联络员 + 急救员1 + 急救员2
2. 查看当前成员
GET /health-emergency/api/emergency/order/getGroupMemberList?sessionId={groupId}
→ 展示群成员列表(按 memberType 区分标签展示)
@@ -130,4 +130,8 @@ public class EmergencyProfessionUser implements Serializable {
// 科室名称
@TableField(exist = false)
private String deptName;
// 手机号(非持久字段,查询时填充)
@TableField(exist = false)
private String phone;
}
@@ -35,9 +35,9 @@ public class OrderOperator implements Serializable {
private String orderId;
/**
* 人员类型(人员类型0:操作人员 1:专业人员)
* 人员类型(0:操作人员 1:专业人员 3:急救人员1 4:急救人员2 5:关爱联络员)
*/
@Schema(title = "人员类型(人员类型0:操作人员 1:专业人员 2:员工)")
@Schema(title = "人员类型(0:操作人员 1:专业人员 2:员工 3:急救人员1 4:急救人员2 5:关爱联络员)")
private Integer operatorType;
/**
@@ -48,4 +48,10 @@ public class GroupMemberVO {
@Schema(title = "成员类型(-1:发起人, 0:操作人员, 1:专业人员, 2:员工)")
private Integer memberType;
/**
* 手机号
*/
@Schema(title = "手机号")
private String phone;
}
@@ -104,6 +104,14 @@ public class EmergencyApiController {
return Result.OK(emergencyApiService.createGroupMag(name, longitude, latitude, type));
}
@Operation(summary = "创建群聊-应急就医-急救员(不发起音视频通话,自动拉入关爱联络员和急救员)")
@GetMapping(value = "/createGroupMagFirstAider")
public Result<CreateGroupMagVO> createGroupMagFirstAider(@RequestParam("name") String name,
@RequestParam("longitude") String longitude,
@RequestParam("latitude") String latitude) {
return Result.OK(emergencyApiService.createGroupMagFirstAider(name, longitude, latitude));
}
@Operation(summary = "创建群聊-干预")
@GetMapping(value = "/createGroupMagIntervene")
@@ -33,6 +33,11 @@ public interface EmergencyApiService {
CreateGroupMagVO createGroupMag(String name, String longitude, String latitude, String type);
/**
* 创建群聊-应急就医-急救员(不发起音视频通话,额外拉入关爱联络员和急救员)
*/
CreateGroupMagVO createGroupMagFirstAider(String name, String longitude, String latitude);
Result<Map<String, Object>> createGroupMagIntervene(String name, String longitude, String latitude, String type);
List<String> fixOrderStatus();
@@ -7,7 +7,11 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.renkang.anyan.client.AnYanClient;
import com.renkang.anyan.model.dto.EmergencyContactParam;
import com.renkang.anyan.model.vo.EmergencyContactVO;
import com.renkang.emergency.api.service.EmergencyApiService;
import com.renkang.emergency.bean.request.CallBack;
import com.renkang.emergency.bean.request.EmergencyCall;
@@ -37,6 +41,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.shiro.SecurityUtils;
import org.jeecg.bean.request.ListUser;
import org.jeecg.bean.response.BaseEmployeeInfo;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.constant.CommonConstant;
import org.jeecg.common.exception.ExceptionAssertsUtil;
@@ -45,6 +50,7 @@ import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.common.system.vo.LoginUserNew;
import org.jeecg.global.GlobalUtils;
import org.jeecg.modules.manager.ISysCache;
import org.jeecg.modules.system.entity.SysUser;
import org.parboiled.common.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -117,6 +123,8 @@ public class EmergencyApiServiceImpl implements EmergencyApiService {
private OrderOperatorMapper orderOperatorMapper;
@Autowired
private EmergencyProfessionUserMapper emergencyProfessionUserMapper;
@Autowired
private AnYanClient anYanClient;
@Override
@@ -887,6 +895,200 @@ public class EmergencyApiServiceImpl implements EmergencyApiService {
return createGroupMagVO;
}
@Transactional(rollbackFor = Exception.class)
@Override
public CreateGroupMagVO createGroupMagFirstAider(String name, String longitude, String latitude) {
CreateGroupMagVO createGroupMagVO = new CreateGroupMagVO();
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
//查是否有已经存在的应急工单,有则直接返回相关信息
Order order = emergencyResourceApiServiceImpl.sessionNowSession(sysUser.getId());
List<String> userList = new ArrayList<>();
if (order != null) {
createGroupMagVO.setUserId(sysUser.getId());
createGroupMagVO.setGroupId(order.getSessionId());
createGroupMagVO.setTfNew("0");
userList.add(order.getOperationUserId());
userList.add(order.getMajorUserId());
if (null != order.getMajorUserTwoId()) {
userList.add(order.getMajorUserTwoId());
}
createGroupMagVO.setMember(userList);
createGroupMagVO.setId(order.getId());
return createGroupMagVO;
}
// 查询当前应急中心值班人员
Call rs = this.officerOnDuty();
if (rs == null || CollectionUtils.isEmpty(rs.getMajors()) || CollectionUtils.isEmpty(rs.getOperators())) {
ExceptionAssertsUtil.fail("没有接受方人员!");
}
String majorsId = rs.getMajors().get(0).getUserId();
String operatorsId = rs.getOperators().get(0).getUserId();
String majorsTwoId = null;
userList.add(operatorsId);
userList.add(majorsId);
List<String> yesIds = new ArrayList<>();
yesIds.add(sysUser.getId());
yesIds.add(majorsId);
yesIds.add(operatorsId);
//获取参与应急的人员信息
ConSessionRequestDO conSessionRequestDO = selectConSessionRequestDO(yesIds, "", name);
conSessionRequestDO.setUserId(sysUser.getId());
//调用IM创建群组
Result<Map<String, Object>> imrs = iMHelloApi.creatGroupMagNew(conSessionRequestDO);
if (!imrs.isSuccess()) {
ExceptionAssertsUtil.fail("应急单创建失败");
}
String sessionId = (String) imrs.getResult().get("groupId");
createGroupMagVO.setGroupId(sessionId);
//直接保存应急工单(不发起点对点音视频通话)
String orderId = insertOrderForFirstAider(sysUser, Double.valueOf(longitude), Double.valueOf(latitude), rs.getCenterId());
//拉入关爱联络员和急救人员
addCareUsersToGroup(sysUser.getId(), sessionId, orderId);
//保存参与应急的人员信息
saveOderOperationInfo(orderId, sessionId, majorsId, operatorsId, majorsTwoId, rs.getCenterId(), conSessionRequestDO.getAccountList());
imrs.getResult().put("member", userList);
imrs.getResult().put("tfNew", "1");
imrs.getResult().put("id", orderId);
createGroupMagVO.setMember(userList);
createGroupMagVO.setTfNew("1");
createGroupMagVO.setId(orderId);
return createGroupMagVO;
}
/**
* 急救员场景插入工单(不触发大屏推送和短信)
*/
private String insertOrderForFirstAider(LoginUser loginUser, Double longitude, Double latitude, String centerId) {
Order order = new Order();
order.setInitiatorType("4");
order.setIsFill(EmergencyConstants.ORDER_IS_FILL_0);
// 发起人
order.setInitiatorUserId(loginUser.getId());
order.setInitiatorUserSex(String.valueOf(loginUser.getSex()));
order.setInitiatorUserName(loginUser.getRealname());
order.setInitiatorUserMobile(loginUser.getPhone());
order.setInitiatorUserIdCard(loginUser.getIdCard());
order.setInitiatorLongitude(BigDecimal.valueOf(longitude));
order.setInitiatorLatitude(BigDecimal.valueOf(latitude));
order.setInitiatorUserDepart(loginUser.getOrgCode());
order.setInitiatorUserAvatar(loginUser.getAvatar());
// 救助人
order.setSalvageUserId(loginUser.getId());
order.setSalvageUserSex(String.valueOf(loginUser.getSex()));
order.setSalvageUserName(loginUser.getRealname());
order.setSalvageUserMobile(loginUser.getPhone());
order.setSalvageUserIdCard(loginUser.getIdCard());
order.setSalvageUserDepart(loginUser.getOrgCode());
order.setSalvageUserAvatar(loginUser.getAvatar());
order.setCenterId(centerId);
orderMapper.insert(order);
return order.getId();
}
/**
* 拉入关爱联络员和急救人员到群聊
* <p>通过手机号匹配安眼系统联系人 → 本系统用户ID</p>
*
* @param userId 发起人ID
* @param groupId IM群组ID
* @param orderId 应急工单ID
*/
private void addCareUsersToGroup(String userId, String groupId, String orderId) {
try {
// 1. 获取发起人工号
List<BaseEmployeeInfo> employeeInfos = sysBaseAPI.queryBaseEmployeeInfo(Collections.singleton(userId));
if (CollUtil.isEmpty(employeeInfos)) {
log.warn("未找到发起人的员工信息,跳过拉入关爱/急救人员, userId={}", userId);
return;
}
String workNo = employeeInfos.get(0).getWorkNo();
if (StrUtil.isBlank(workNo)) {
log.warn("发起人工号为空,跳过拉入关爱/急救人员, userId={}", userId);
return;
}
// 2. 查安眼系统获取关爱联络员和急救人员信息
EmergencyContactParam param = new EmergencyContactParam();
param.setEmployeeNum(workNo);
IPage<EmergencyContactVO> page = anYanClient.queryEmergencyContact(param);
if (page == null || CollUtil.isEmpty(page.getRecords())) {
log.warn("安眼系统未查到关爱/急救人员信息, workNo={}", workNo);
return;
}
EmergencyContactVO contact = page.getRecords().get(0);
// 3. 收集3个人的手机号 → 角色映射
Map<String, Integer> phoneRoleMap = new LinkedHashMap<>();
if (StrUtil.isNotBlank(contact.getRelationEmpPhone())) {
phoneRoleMap.put(contact.getRelationEmpPhone(), 5); // 关爱联络员
}
if (StrUtil.isNotBlank(contact.getFirstAiderPhone())) {
phoneRoleMap.put(contact.getFirstAiderPhone(), 3); // 急救人员1
}
if (StrUtil.isNotBlank(contact.getFirstAiderPhone2())) {
phoneRoleMap.put(contact.getFirstAiderPhone2(), 4); // 急救人员2
}
if (phoneRoleMap.isEmpty()) {
log.warn("关爱/急救人员手机号均为空, workNo={}", workNo);
return;
}
// 4. 手机号反查本系统用户ID
List<String> phones = new ArrayList<>(phoneRoleMap.keySet());
Result<List<SysUser>> userResult = sysBaseAPI.getUserByPhoneList(phones);
if (!userResult.isSuccess() || CollUtil.isEmpty(userResult.getResult())) {
log.warn("关爱/急救人员手机号均未匹配到本系统用户, phones={}", phones);
return;
}
List<SysUser> sysUsers = userResult.getResult();
log.info("关爱/急救人员手机号匹配到{}个系统用户, phones={}", sysUsers.size(), phones);
// 5. 拉入IM群聊
List<String> memberAccounts = sysUsers.stream().map(SysUser::getId).collect(Collectors.toList());
AddGroupMemberDTO addDto = new AddGroupMemberDTO();
addDto.setGroupId(groupId);
addDto.setSilence(1);
addDto.setMemberList(memberAccounts.stream().map(account -> {
AddGroupMemberDTO.MemberListDTO m = new AddGroupMemberDTO.MemberListDTO();
m.setMemberAccount(account);
return m;
}).collect(Collectors.toList()));
try {
iMHelloApi.addGroupMember(addDto);
log.info("关爱/急救人员拉入群聊成功, groupId={}, userIds={}", groupId, memberAccounts);
} catch (Exception e) {
log.error("关爱/急救人员拉入群聊失败, groupId={}, userIds={}", groupId, memberAccounts, e);
// IM拉人失败不影响建群流程,继续写入 order_operator
}
// 6. 写入 order_operator 表
Date now = new Date();
Map<String, Integer> userIdRoleMap = new HashMap<>();
for (SysUser sysUser : sysUsers) {
for (Map.Entry<String, Integer> entry : phoneRoleMap.entrySet()) {
if (entry.getKey().equals(sysUser.getPhone())) {
userIdRoleMap.put(sysUser.getId(), entry.getValue());
break;
}
}
}
for (Map.Entry<String, Integer> entry : userIdRoleMap.entrySet()) {
OrderOperator orderOperator = new OrderOperator();
orderOperator.setOrderId(orderId);
orderOperator.setOperatorType(entry.getValue());
orderOperator.setUserId(entry.getKey());
orderOperator.setJoinType(1);
orderOperator.setJoinDate(now);
orderOperatorMapper.insert(orderOperator);
log.info("写入order_operator, orderId={}, userId={}, operatorType={}", orderId, entry.getKey(), entry.getValue());
}
} catch (Exception e) {
log.error("拉入关爱/急救人员异常,不影响建群主流程, userId={}, groupId={}, orderId={}", userId, groupId, orderId, e);
}
}
public void saveOderOperationInfo(String orderId, String sessionId, String majorsId, String operatorsId, String majorsTwoId,
String centerId, List<LoginUserNew> accountList) {
Map<String, LoginUserNew> userMap = accountList.stream().collect(Collectors.toMap(LoginUserNew::getId, user -> user));
@@ -623,6 +623,7 @@ public class EmergencyOrderApiServiceImpl implements EmergencyOrderApiService {
order.getInitiatorUserId(),
order.getInitiatorUserName(),
order.getInitiatorUserAvatar(),
order.getInitiatorUserMobile(),
1, -1);
// 已加入应急单人员
@@ -662,19 +663,22 @@ public class EmergencyOrderApiServiceImpl implements EmergencyOrderApiService {
for (OrderOperator op : orderOperators) {
String userName = "";
String avatar = "";
String phone = "";
EmergencyProfessionUser profUser = profUserMap.get(op.getUserId());
if (profUser != null) {
userName = profUser.getRealname();
avatar = profUser.getAvatar();
phone = profUser.getPhone();
} else {
LoginUserNew sysUser = sysUserMap.get(op.getUserId());
if (sysUser != null) {
userName = sysUser.getRealname();
avatar = sysUser.getAvatar();
phone = sysUser.getPhone();
}
}
handleGroupMember(groupMemberVos, orderId,
op.getUserId(), userName, avatar, 1, op.getOperatorType());
op.getUserId(), userName, avatar, phone, 1, op.getOperatorType());
}
// 其他专业人员(排除已在应急单中的用户),memberType=1
@@ -685,22 +689,37 @@ public class EmergencyOrderApiServiceImpl implements EmergencyOrderApiService {
}
List<EmergencyProfessionUser> otherMajorMemberList = emergencyProfessionUserMapper.selectList(otherMajorQuery);
// 批量查手机号
Map<String, String> otherMajorPhoneMap = new HashMap<>();
if (!otherMajorMemberList.isEmpty()) {
List<String> otherMajorUserIds = otherMajorMemberList.stream()
.map(EmergencyProfessionUser::getUserId).collect(Collectors.toList());
Map<String, LoginUserNew> otherUserMap = emergencyCloudManager.listUser(otherMajorUserIds);
for (EmergencyProfessionUser m : otherMajorMemberList) {
LoginUserNew u = otherUserMap.get(m.getUserId());
if (u != null) {
otherMajorPhoneMap.put(m.getUserId(), u.getPhone());
}
}
}
otherMajorMemberList.forEach(otherMajorMember ->
handleGroupMember(groupMemberVos, orderId,
otherMajorMember.getUserId(),
otherMajorMember.getRealname(),
otherMajorMember.getAvatar(),
otherMajorPhoneMap.get(otherMajorMember.getUserId()),
0, 1)
);
return groupMemberVos.stream().filter(groupMemberVO -> !groupMemberVO.getUserId().equals(loginUser.getId())).collect(Collectors.toList());
}
public void handleGroupMember(List<GroupMemberVO> groupMemberVos, String orderId, String userId, String userName, String avatar, Integer isGroupMember, Integer memberType) {
public void handleGroupMember(List<GroupMemberVO> groupMemberVos, String orderId, String userId, String userName, String avatar, String phone, Integer isGroupMember, Integer memberType) {
GroupMemberVO groupMemberVO = new GroupMemberVO();
groupMemberVO.setOrderId(orderId);
groupMemberVO.setUserId(userId);
groupMemberVO.setUserName(userName);
groupMemberVO.setAvatar(avatar);
groupMemberVO.setPhone(phone);
groupMemberVO.setIsGroupMember(isGroupMember);
groupMemberVO.setMemberType(memberType);
groupMemberVos.add(groupMemberVO);
@@ -742,6 +761,9 @@ public class EmergencyOrderApiServiceImpl implements EmergencyOrderApiService {
if (sysUser != null && StrUtil.isNotBlank(sysUser.getOrgCode())) {
prof.setDeptName(emergencyCacheManager.getUserDepart(sysUser.getOrgCode()).getDepartName());
}
if (sysUser != null) {
prof.setPhone(sysUser.getPhone());
}
});
}
return result;