diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/common/handler/GlobalResponseHandler.java b/hz-pm-api/src/main/java/com/hz/pm/api/common/handler/GlobalResponseHandler.java index 0226f44..54967fa 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/common/handler/GlobalResponseHandler.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/common/handler/GlobalResponseHandler.java @@ -41,7 +41,8 @@ import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; "com.hz.pm.api.dashboard.controller", "com.hz.pm.api.wps.controller", "com.hz.pm.api.external.controller", - "com.hz.pm.api.gov.controller" + "com.hz.pm.api.gov.controller", + "com.hz.pm.api.open.controller" }) public class GlobalResponseHandler implements ResponseBodyAdvice { diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/expert/model/dto/DictionaryVO.java b/hz-pm-api/src/main/java/com/hz/pm/api/expert/model/dto/DictionaryVO.java index 6cafcd5..e1d92fb 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/expert/model/dto/DictionaryVO.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/expert/model/dto/DictionaryVO.java @@ -2,6 +2,7 @@ package com.hz.pm.api.expert.model.dto; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; /** *

@@ -13,6 +14,7 @@ import lombok.Data; */ @Data @AllArgsConstructor +@NoArgsConstructor public class DictionaryVO { private String dictionaryCode; diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/entity/domain/MeetingExpert.java b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/entity/domain/MeetingExpert.java index 3088f10..de74edb 100644 --- a/hz-pm-api/src/main/java/com/hz/pm/api/meeting/entity/domain/MeetingExpert.java +++ b/hz-pm-api/src/main/java/com/hz/pm/api/meeting/entity/domain/MeetingExpert.java @@ -80,4 +80,16 @@ public class MeetingExpert implements Serializable { @TableField(fill = FieldFill.INSERT_UPDATE) private LocalDateTime updateOn; + /** + * 20240511 新增字段 + */ + @ApiModelProperty("专家签到状态") + private Boolean signStatus; + + @ApiModelProperty("专家签到时间") + private LocalDateTime signTime; + + @ApiModelProperty("专家签到地点") + private String signAddress; + } diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/open/controller/OpenApiMeetingExpertInfoController.java b/hz-pm-api/src/main/java/com/hz/pm/api/open/controller/OpenApiMeetingExpertInfoController.java new file mode 100644 index 0000000..8f3603c --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/open/controller/OpenApiMeetingExpertInfoController.java @@ -0,0 +1,45 @@ +package com.hz.pm.api.open.controller; + + +import java.util.List; + +import javax.validation.Valid; + +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.hz.pm.api.open.manage.OpenApiMeetingExpertInfoManage; +import com.hz.pm.api.open.model.po.ReqMeetingExpertInfoPO; +import com.hz.pm.api.open.model.vo.MeetingExpertInfoVO; +import com.ningdatech.log.annotation.WebLog; + +import io.swagger.annotations.ApiOperation; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + *

+ * 会议专家信息外部调用接口 + *

+ * @author CMM + * + */ +@Slf4j +@Validated +@RestController +@RequestMapping("/open/api/meeting-expert-info") +@RequiredArgsConstructor +public class OpenApiMeetingExpertInfoController { + + private final OpenApiMeetingExpertInfoManage openMeetingExpertInfoManage; + + @PostMapping("/list") + @ApiOperation("获取已抽取完成会议确定参加会议且未签到的专家信息列表") + @WebLog + public List infoList(@Valid @RequestBody ReqMeetingExpertInfoPO po) { + return openMeetingExpertInfoManage.infoList(po); + } +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/open/manage/OpenApiMeetingExpertInfoManage.java b/hz-pm-api/src/main/java/com/hz/pm/api/open/manage/OpenApiMeetingExpertInfoManage.java new file mode 100644 index 0000000..4a9972e --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/open/manage/OpenApiMeetingExpertInfoManage.java @@ -0,0 +1,184 @@ +package com.hz.pm.api.open.manage; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.*; +import java.util.stream.Collectors; + +import com.hz.pm.api.expert.model.dto.DictionaryVO; +import com.hz.pm.api.meta.model.dto.DictionaryDTO; +import com.hz.pm.api.user.model.entity.UserInfo; +import com.hz.pm.api.user.security.model.UserFullInfoDTO; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import com.hz.pm.api.common.helper.UserInfoHelper; +import com.hz.pm.api.meeting.entity.domain.Meeting; +import com.hz.pm.api.meeting.entity.domain.MeetingExpert; +import com.hz.pm.api.meeting.service.IMeetingExpertService; +import com.hz.pm.api.meeting.service.IMeetingService; +import com.hz.pm.api.meta.constant.ExpertDictTypeEnum; +import com.hz.pm.api.meta.helper.DictionaryCache; +import com.hz.pm.api.meta.model.entity.ExpertDictionary; +import com.hz.pm.api.meta.service.IExpertDictionaryService; +import com.hz.pm.api.open.model.po.ReqMeetingExpertInfoPO; +import com.hz.pm.api.open.model.vo.ExpertInfoVO; +import com.hz.pm.api.open.model.vo.MeetingExpertInfoVO; +import com.hz.pm.api.user.service.IUserInfoService; +import com.ningdatech.basic.exception.BizException; +import com.ningdatech.basic.util.CollUtils; + +import cn.hutool.core.collection.CollUtil; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + * @author CMM + * @since 2024/05/10 17:18 + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class OpenApiMeetingExpertInfoManage { + + @Value("${hz-pm.interfaceKey}") + private String meetingExpertInfoKey; + + private final IMeetingService meetingService; + private final IMeetingExpertService meetingExpertService; + private final UserInfoHelper userInfoHelper; + private final IUserInfoService userInfoService; + private final IExpertDictionaryService iExpertDictionaryService; + private final DictionaryCache dictionaryCache; + public List infoList(ReqMeetingExpertInfoPO po) { + String key = po.getKey(); + // 校验传入的签名 + if (!checkSign(key)){ + throw new BizException("签名错误!"); + } + // 获取已经抽取完成 确认参加会议并且未签到的专家信息 + List meetingList = meetingService.list(Wrappers.lambdaQuery(Meeting.class) + .eq(Meeting::getConfirmedRoster, true)); + if(CollUtil.isEmpty(meetingList)){ + return Collections.emptyList(); + } + List result = Lists.newArrayList(); + // 获取已抽取完成会议中未签到的专家信息 + List meetingIds = meetingList.stream().map(Meeting::getId).collect(Collectors.toList()); + Map> meetingExpertMap = meetingExpertService.list(Wrappers.lambdaQuery(MeetingExpert.class) + .in(MeetingExpert::getMeetingId, meetingIds) + .eq(MeetingExpert::getSignStatus, Boolean.FALSE)) + .stream().collect(Collectors.groupingBy(MeetingExpert::getMeetingId)); + Map meetingMap = CollUtils.listToMap(meetingList, Meeting::getId); + List experts = Lists.newArrayList(); + for (Map.Entry> listEntry : meetingExpertMap.entrySet()) { + List value = listEntry.getValue(); + experts.addAll(value); + } + + // 获取未签到的专家信息 + List expertList = experts.stream().filter(m -> Boolean.FALSE.equals(m.getSignStatus())).collect(Collectors.toList()); + Set userIds = expertList.stream().map(MeetingExpert::getExpertId).collect(Collectors.toSet()); + List userInfoList = userInfoService.list(Wrappers.lambdaQuery(UserInfo.class).in(UserInfo::getId, userIds)); + Set employeeSet = Sets.newHashSet(); + userInfoList = userInfoList.stream().filter(u -> employeeSet.add(u.getEmployeeCode())).collect(Collectors.toList()); + List userFullInfos = userInfoHelper.getUserFullInfos(userInfoList); + Set userIdSet = Sets.newHashSet(); + userFullInfos = userFullInfos.stream().filter(u -> userIdSet.add(u.getUserId())).collect(Collectors.toList()); + Map infoMap = userFullInfos.stream().collect(Collectors.toMap(UserFullInfoDTO::getUserId, v -> v)); + + List expertDictionaryList = iExpertDictionaryService.list(Wrappers.lambdaQuery(ExpertDictionary.class) + .eq(ExpertDictionary::getExpertInfoField, ExpertDictTypeEnum.EXPERT_TYPE.getKey()) + .in(ExpertDictionary::getUserId, userIdSet)); + Map> dictMap = expertDictionaryList.stream().collect(Collectors.groupingBy(ExpertDictionary::getUserId)); + + for (Map.Entry> entry : meetingExpertMap.entrySet()) { + MeetingExpertInfoVO vo = new MeetingExpertInfoVO(); + Long meetingId = entry.getKey(); + Meeting meeting = meetingMap.get(meetingId); + if (Objects.nonNull(meeting)){ + vo.setMeetingId(meeting.getId()); + vo.setStartTime(meeting.getStartTime()); + vo.setEndTime(meeting.getEndTime()); + vo.setMeetingName(meeting.getName()); + vo.setMeetingAddress(meeting.getMeetingAddress()); + vo.setConnecter(meeting.getConnecter()); + vo.setContact(meeting.getContact()); + } + List meetingExperts = entry.getValue(); + if (CollUtil.isNotEmpty(meetingExperts)){ + // 获取未签到的专家信息 + List expertUserList = meetingExperts.stream().filter(m -> Boolean.FALSE.equals(m.getSignStatus())).collect(Collectors.toList()); + if (CollUtil.isNotEmpty(expertUserList)){ + List expertInfoVOList = expertUserList.stream().map(m -> { + ExpertInfoVO expertInfoVO = new ExpertInfoVO(); + expertInfoVO.setUserId(m.getExpertId()); + expertInfoVO.setName(m.getExpertName()); + expertInfoVO.setPhoneNo(m.getMobile()); + UserFullInfoDTO userFullInfoDTO = infoMap.get(m.getExpertId()); + if (Objects.nonNull(userFullInfoDTO)){ + expertInfoVO.setMhUnitId(userFullInfoDTO.getMhUnitId()); + expertInfoVO.setMhUnitName(userFullInfoDTO.getMhUnitName()); + } + List dictionaries = dictMap.get(m.getExpertId()); + if (CollUtil.isNotEmpty(dictionaries)){ + List dictionaryFieldInfos = dictionaries.stream().map(r -> { + DictionaryVO dictionary = new DictionaryVO(); + dictionary.setDictionaryType(r.getExpertInfoField()); + dictionary.setDictionaryCode(r.getDictionaryCode()); + Optional dictionaryDTO = dictionaryCache.getByCode(r.getDictionaryCode()); + dictionaryDTO.ifPresent(dictDTO -> dictionary.setDictionaryName(dictDTO.getName())); + return dictionary; + }).collect(Collectors.toList()); + expertInfoVO.setExpertType(dictionaryFieldInfos); + } + return expertInfoVO; + }).collect(Collectors.toList()); + vo.setExpertInfoList(expertInfoVOList); + } + } + result.add(vo); + } + return result; + } + + private Boolean checkSign(String key) { + StringBuilder sb = new StringBuilder(); + sb.append("key=").append(meetingExpertInfoKey); + // 使用SHA-256进行加密 + String sign = null; + try { + MessageDigest md = MessageDigest.getInstance("SHA-256"); + byte[] bytes = md.digest(sb.toString().getBytes()); + sign = bytesToHexString(bytes); + } catch (NoSuchAlgorithmException e) { + e.printStackTrace(); + } + if (sign == null || sign.length() <= 8) { + return false; + } + String signStr = sign.substring(sign.length() - 8); + return signStr.equals(key); + } + + /** + * 将字节数组转换为十六进制字符串 + * + * @param bytes 字节数组 + * @return 十六进制字符串 + */ + private static String bytesToHexString(byte[] bytes) { + StringBuilder sb = new StringBuilder(); + for (byte b : bytes) { + String hex = Integer.toHexString(b & 0xFF); + if (hex.length() == 1) { + sb.append("0"); + } + sb.append(hex); + } + return sb.toString(); + } +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/open/model/po/ReqMeetingExpertInfoPO.java b/hz-pm-api/src/main/java/com/hz/pm/api/open/model/po/ReqMeetingExpertInfoPO.java new file mode 100644 index 0000000..c7288b1 --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/open/model/po/ReqMeetingExpertInfoPO.java @@ -0,0 +1,26 @@ +package com.hz.pm.api.open.model.po; + +import javax.validation.constraints.NotBlank; + +import com.ningdatech.basic.model.PagePo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * @author CMM + * @since 2024-05-10 + */ +@ApiModel(description = "会议专家信息列表请求入参") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ReqMeetingExpertInfoPO extends PagePo { + + @ApiModelProperty("校验公钥") + @NotBlank(message = "秘钥不能为空") + private String key; +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/open/model/vo/ExpertInfoVO.java b/hz-pm-api/src/main/java/com/hz/pm/api/open/model/vo/ExpertInfoVO.java new file mode 100644 index 0000000..e351da8 --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/open/model/vo/ExpertInfoVO.java @@ -0,0 +1,36 @@ +package com.hz.pm.api.open.model.vo; + +import java.util.List; + +import com.hz.pm.api.expert.model.dto.DictionaryVO; +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * @author CMM + * @date 2024/5/11 + */ +@Data +@ApiModel("专家信息") +public class ExpertInfoVO { + + @ApiModelProperty("专家用户id") + private Long userId; + + @ApiModelProperty(value = "专家类型") + private List expertType; + + @ApiModelProperty(value = "专家姓名") + private String name; + + @ApiModelProperty(value = "手机号") + private String phoneNo; + + @ApiModelProperty(value = "单位名称") + private String mhUnitName; + + @ApiModelProperty(value = "单位id") + private Long mhUnitId; + +} diff --git a/hz-pm-api/src/main/java/com/hz/pm/api/open/model/vo/MeetingExpertInfoVO.java b/hz-pm-api/src/main/java/com/hz/pm/api/open/model/vo/MeetingExpertInfoVO.java new file mode 100644 index 0000000..a1bc59d --- /dev/null +++ b/hz-pm-api/src/main/java/com/hz/pm/api/open/model/vo/MeetingExpertInfoVO.java @@ -0,0 +1,49 @@ +package com.hz.pm.api.open.model.vo; + +import java.time.LocalDateTime; +import java.util.List; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 外部调用接口会议专家信息实体 + * + * @author CMM + * @since 2024/05/10 17:16 + */ +@Data +public class MeetingExpertInfoVO { + + + /** + * 会议信息 + */ + + @ApiModelProperty("会议ID") + private Long meetingId; + + @ApiModelProperty("会议开始时间") + private LocalDateTime startTime; + + @ApiModelProperty("会议结束时间") + private LocalDateTime endTime; + + @ApiModelProperty("会议名称") + private String meetingName; + + @ApiModelProperty("会议地址") + private String meetingAddress; + + @ApiModelProperty("联系人") + private String connecter; + + @ApiModelProperty("联系方式") + private String contact; + + /** + * 专家信息列表 + */ + @ApiModelProperty("专家信息列表") + private List expertInfoList; +} diff --git a/hz-pm-api/src/main/resources/application-dev.yml b/hz-pm-api/src/main/resources/application-dev.yml index 3eefcd7..388cbb4 100644 --- a/hz-pm-api/src/main/resources/application-dev.yml +++ b/hz-pm-api/src/main/resources/application-dev.yml @@ -204,4 +204,6 @@ mobile-call: reminder-task: declared-record: open: false - cron: 0 30 8 * * ? \ No newline at end of file + cron: 0 30 8 * * ? +hz-pm: + interfaceKey: hz_meeting_expert_info \ No newline at end of file diff --git a/hz-pm-api/src/main/resources/application-prod.yml b/hz-pm-api/src/main/resources/application-prod.yml index 6bfc12e..8fa3bc3 100644 --- a/hz-pm-api/src/main/resources/application-prod.yml +++ b/hz-pm-api/src/main/resources/application-prod.yml @@ -220,4 +220,7 @@ reminder-task: cron: 0 30 8 * * ? # 电话服务调用地址 mobile-call: - host: http://10.54.21.185:18085/blue_esl_api \ No newline at end of file + host: http://10.54.21.185:18085/blue_esl_api +hz-pm: + interfaceKey: hz_meeting_expert_info + diff --git a/hz-pm-api/src/main/resources/application-test.yml b/hz-pm-api/src/main/resources/application-test.yml index 22e1d56..f47f850 100644 --- a/hz-pm-api/src/main/resources/application-test.yml +++ b/hz-pm-api/src/main/resources/application-test.yml @@ -264,4 +264,6 @@ auth-code: secret-key: uqrvd2bani4fercnisua1cqxjwk1neym agent-login: proxy: - secret-key: tqkwiqojg5j4eiypr3rb8w7nb4noa8b2 \ No newline at end of file + secret-key: tqkwiqojg5j4eiypr3rb8w7nb4noa8b2 +hz-pm: + interfaceKey: hz_meeting_expert_info \ No newline at end of file diff --git a/hz-pm-api/src/main/resources/security/auth-dev.yml b/hz-pm-api/src/main/resources/security/auth-dev.yml index 8716753..770e59e 100644 --- a/hz-pm-api/src/main/resources/security/auth-dev.yml +++ b/hz-pm-api/src/main/resources/security/auth-dev.yml @@ -73,6 +73,7 @@ security: - /api/v1/wps-convert/** - /api/v1/belong-org/business-strip/list - /expert/ephemeral/*/registration + - /open/api/** role-map: "engineer": "project_manager": diff --git a/hz-pm-api/src/main/resources/security/auth-prod.yml b/hz-pm-api/src/main/resources/security/auth-prod.yml index 8716753..770e59e 100644 --- a/hz-pm-api/src/main/resources/security/auth-prod.yml +++ b/hz-pm-api/src/main/resources/security/auth-prod.yml @@ -73,6 +73,7 @@ security: - /api/v1/wps-convert/** - /api/v1/belong-org/business-strip/list - /expert/ephemeral/*/registration + - /open/api/** role-map: "engineer": "project_manager": diff --git a/hz-pm-api/src/main/resources/security/auth-test.yml b/hz-pm-api/src/main/resources/security/auth-test.yml index 2a672b5..2c433ab 100644 --- a/hz-pm-api/src/main/resources/security/auth-test.yml +++ b/hz-pm-api/src/main/resources/security/auth-test.yml @@ -72,6 +72,7 @@ security: - /api/v1/wps-convert/** - /api/v1/belong-org/business-strip/list - /expert/ephemeral/*/registration + - /open/api/** role-map: "engineer": "project_manager":