| | |
| | | import com.talentcard.server.common.SensitiveDataCrypto; |
| | | import com.talentcard.server.entity.ChildInformation; |
| | | import com.talentcard.server.entity.ServiceApplication; |
| | | import com.talentcard.server.entity.ServiceApplicationOperationLog; |
| | | import com.talentcard.server.mapper.ChildInformationMapper; |
| | | import com.talentcard.server.mapper.ServiceApplicationMapper; |
| | | import com.talentcard.server.mapper.ServiceApplicationOperationLogMapper; |
| | | import org.apache.poi.ss.usermodel.CellStyle; |
| | | import org.apache.poi.ss.usermodel.FillPatternType; |
| | | import org.apache.poi.ss.usermodel.IndexedColors; |
| | |
| | | |
| | | private final ServiceApplicationMapper applicationMapper; |
| | | private final ChildInformationMapper childInformationMapper; |
| | | private final ServiceApplicationOperationLogMapper operationLogMapper; |
| | | private final SensitiveDataCrypto sensitiveDataCrypto; |
| | | |
| | | public ServiceAppointmentAdminService( |
| | | ServiceApplicationMapper applicationMapper, |
| | | ChildInformationMapper childInformationMapper, |
| | | ServiceApplicationOperationLogMapper operationLogMapper, |
| | | SensitiveDataCrypto sensitiveDataCrypto |
| | | ) { |
| | | this.applicationMapper = applicationMapper; |
| | | this.childInformationMapper = childInformationMapper; |
| | | this.operationLogMapper = operationLogMapper; |
| | | this.sensitiveDataCrypto = sensitiveDataCrypto; |
| | | } |
| | | |
| | | @Transactional(readOnly = true) |
| | | public PageResult<Map<String, Object>> page(String applicationType, String keyword, String serviceKeyword, |
| | | String cardLevel, String status, String submittedStart, |
| | | public PageResult<Map<String, Object>> page(String applicationType, String keyword, String applicantName, |
| | | String applicantMobile, String serviceKeyword, |
| | | String cardLevel, String subsidy, String submissionType, String status, String submittedStart, |
| | | String submittedEnd, int page, int size) { |
| | | int safePage = Math.max(page, 1); |
| | | int safeSize = Math.min(Math.max(size, 1), 100); |
| | | QueryWrapper<ServiceApplication> countWrapper = filter(applicationType, keyword, serviceKeyword, cardLevel, status, submittedStart, submittedEnd); |
| | | QueryWrapper<ServiceApplication> countWrapper = filter(applicationType, keyword, applicantName, applicantMobile, serviceKeyword, cardLevel, subsidy, submissionType, status, submittedStart, submittedEnd); |
| | | Long total = applicationMapper.selectCount(countWrapper); |
| | | QueryWrapper<ServiceApplication> pageWrapper = filter(applicationType, keyword, serviceKeyword, cardLevel, status, submittedStart, submittedEnd) |
| | | QueryWrapper<ServiceApplication> pageWrapper = filter(applicationType, keyword, applicantName, applicantMobile, serviceKeyword, cardLevel, subsidy, submissionType, status, submittedStart, submittedEnd) |
| | | .orderByDesc("submitted_at", "id") |
| | | .last("limit " + safeSize + " offset " + ((safePage - 1) * safeSize)); |
| | | List<Map<String, Object>> records = applicationMapper.selectList(pageWrapper).stream() |
| | |
| | | return detailView(application); |
| | | } |
| | | |
| | | @Transactional(readOnly = true) |
| | | public List<Map<String, Object>> operationLogs(Long id) { |
| | | application(id); |
| | | return operationLogMapper.selectList(new QueryWrapper<ServiceApplicationOperationLog>() |
| | | .eq("application_id", id) |
| | | .orderByAsc("created_at", "id")) |
| | | .stream().map(this::operationLogView).toList(); |
| | | } |
| | | |
| | | |
| | | @Transactional |
| | | public Map<String, Object> updateStatus(Long id, ServiceAppointmentStatusUpdateRequest request, Long operatorId) { |
| | | ServiceApplication current = applicationMapper.selectByIdForUpdate(id); |
| | |
| | | if (REJECTED_STATUSES.contains(nextStatus) && remark.isBlank()) { |
| | | throw new BusinessException(ErrorCode.BAD_REQUEST, "拒绝或驳回时必须填写处理备注"); |
| | | } |
| | | String handlerName = first(request.handlerName(), operatorId == null ? "" : "管理员" + operatorId, "管理员"); |
| | | String handlerName = first(operatorName(operatorId), request.handlerName(), "管理员"); |
| | | LocalDateTime handledAt = PENDING_STATUSES.contains(nextStatus) ? null : LocalDateTime.now(); |
| | | UpdateWrapper<ServiceApplication> update = new UpdateWrapper<ServiceApplication>() |
| | | .set("status", nextStatus) |
| | |
| | | if (updated != 1) { |
| | | throw new BusinessException(ErrorCode.CONFLICT, "预约申请状态已变化,请刷新后重试"); |
| | | } |
| | | ServiceApplicationOperationLog log = new ServiceApplicationOperationLog(); |
| | | log.setApplicationId(current.getId()); |
| | | log.setApplicationNo(current.getApplicationNo()); |
| | | log.setOperationType("STATUS_UPDATE"); |
| | | log.setBeforeStatus(current.getStatus()); |
| | | log.setAfterStatus(nextStatus); |
| | | log.setOperatorType("ADMIN"); |
| | | log.setOperatorId(operatorId); |
| | | log.setOperatorName(handlerName); |
| | | log.setRemark(remark.isBlank() ? null : remark); |
| | | log.setCreatedAt(LocalDateTime.now()); |
| | | if (operationLogMapper.insert(log) != 1) { |
| | | throw new BusinessException(ErrorCode.INTERNAL_ERROR, "预约申请操作日志保存失败"); |
| | | } |
| | | syncChildAudit(current, nextStatus, remark); |
| | | return detail(id); |
| | | } |
| | | |
| | | @Transactional(readOnly = true) |
| | | public byte[] exportExcel(String applicationType, String keyword, String serviceKeyword, |
| | | String cardLevel, String status, String submittedStart, String submittedEnd) { |
| | | QueryWrapper<ServiceApplication> wrapper = filter(applicationType, keyword, serviceKeyword, cardLevel, status, submittedStart, submittedEnd) |
| | | public byte[] exportExcel(String applicationType, String keyword, String applicantName, |
| | | String applicantMobile, String serviceKeyword, |
| | | String cardLevel, String subsidy, String submissionType, String status, String submittedStart, String submittedEnd) { |
| | | QueryWrapper<ServiceApplication> wrapper = filter(applicationType, keyword, applicantName, applicantMobile, serviceKeyword, cardLevel, subsidy, submissionType, status, submittedStart, submittedEnd) |
| | | .orderByDesc("submitted_at", "id"); |
| | | List<ServiceApplication> applications = applicationMapper.selectList(wrapper); |
| | | String[] headers = { |
| | |
| | | } |
| | | } |
| | | |
| | | private QueryWrapper<ServiceApplication> filter(String applicationType, String keyword, String serviceKeyword, |
| | | String cardLevel, String status, String submittedStart, String submittedEnd) { |
| | | private QueryWrapper<ServiceApplication> filter(String applicationType, String keyword, String applicantName, |
| | | String applicantMobile, String serviceKeyword, |
| | | String cardLevel, String subsidy, String submissionType, String status, String submittedStart, String submittedEnd) { |
| | | QueryWrapper<ServiceApplication> wrapper = new QueryWrapper<ServiceApplication>().eq("deleted", 0); |
| | | String type = normalizeFilterType(applicationType); |
| | | if (!type.isBlank()) { |
| | |
| | | .or().like("spouse_name", like) |
| | | .or().like("child_name", like)); |
| | | } |
| | | String name = clean(applicantName); |
| | | if (!name.isBlank()) { |
| | | wrapper.and(query -> query.like("talent_name", name) |
| | | .or().like("applicant_name", name) |
| | | .or().like("spouse_name", name) |
| | | .or().like("child_name", name)); |
| | | } |
| | | String mobile = clean(applicantMobile); |
| | | if (!mobile.isBlank()) { |
| | | wrapper.and(query -> query.like("talent_mobile", mobile) |
| | | .or().like("applicant_mobile", mobile) |
| | | .or().like("spouse_mobile", mobile) |
| | | .or().like("contact_mobile", mobile)); |
| | | } |
| | | String service = clean(serviceKeyword); |
| | | if (!service.isBlank()) { |
| | | String like = "%" + service + "%"; |
| | |
| | | } |
| | | if (!clean(cardLevel).isBlank()) { |
| | | wrapper.eq("talent_level", clean(cardLevel)); |
| | | } |
| | | String subsidyValue = clean(subsidy); |
| | | if ("是".equals(subsidyValue)) { |
| | | wrapper.eq("subsidy", "是"); |
| | | } else if ("否".equals(subsidyValue)) { |
| | | wrapper.and(query -> query.eq("subsidy", "否").or().isNull("subsidy").or().eq("subsidy", "")); |
| | | } else if (!subsidyValue.isBlank()) { |
| | | wrapper.eq("subsidy", subsidyValue); |
| | | } |
| | | if (!clean(submissionType).isBlank()) { |
| | | wrapper.eq("submission_type", clean(submissionType)); |
| | | } |
| | | if (!clean(status).isBlank()) { |
| | | wrapper.eq("status", clean(status)); |
| | |
| | | result.put("talentMobile", application.getTalentMobile()); |
| | | result.put("talentLevel", application.getTalentLevel()); |
| | | result.put("cardNo", application.getCardNo()); |
| | | result.put("merchantName", application.getMerchantName()); |
| | | result.put("hospitalName", application.getHospitalName()); |
| | | result.put("contactMobile", application.getContactMobile()); |
| | | result.put("packageName", application.getPackageName()); |
| | | result.put("productName", application.getProductName()); |
| | | result.put("expectedTime", application.getExpectedTime()); |
| | | result.put("submissionType", application.getSubmissionType()); |
| | | result.put("plateNo", application.getPlateNo()); |
| | | result.put("parkingLot", application.getParkingLot()); |
| | | result.put("alipayMobile", application.getAlipayMobile()); |
| | | result.put("serviceName", serviceName(application)); |
| | | result.put("appointmentContent", appointmentContent(application)); |
| | | result.put("appointmentTime", first(application.getExpectedTime(), "以专员通知为准")); |
| | | result.put("applicantName", participantName(application)); |
| | | result.put("applicantMobile", sensitiveDataCrypto.decrypt(first(application.getApplicantMobile(), application.getSpouseMobile(), application.getTalentMobile()))); |
| | | result.put("guestCount", guestCount(application)); |
| | | if (ServiceAppointmentApplicationService.TYPE_SPOUSE.equals(application.getApplicationType())) { |
| | | result.put("spouseName", first(application.getSpouseName(), application.getApplicantName())); |
| | | result.put("spouseMobile", sensitiveDataCrypto.decrypt(first(application.getSpouseMobile(), application.getApplicantMobile()))); |
| | | } |
| | | result.put("submittedAt", application.getSubmittedAt()); |
| | | result.put("handledAt", application.getHandledAt()); |
| | | result.put("handlerName", application.getHandlerName()); |
| | |
| | | |
| | | private Map<String, Object> detailView(ServiceApplication application) { |
| | | Map<String, Object> result = listView(application); |
| | | result.put("handlerName", handlerName(application)); |
| | | result.put("talentId", application.getTalentId()); |
| | | result.put("cardId", application.getCardId()); |
| | | result.put("merchantId", application.getMerchantId()); |
| | |
| | | result.put("applicantIdCardNo", sensitiveDataCrypto.decrypt(application.getApplicantIdCardNo())); |
| | | result.put("checkInDate", application.getCheckInDate()); |
| | | result.put("checkOutDate", application.getCheckOutDate()); |
| | | result.put("guestCount", application.getGuestCount()); |
| | | result.put("guestCount", guestCount(application)); |
| | | result.put("subsidy", application.getSubsidy()); |
| | | result.put("remark", application.getRejectReason()); |
| | | result.put("estimatedProcessingTime", application.getEstimatedProcessingTime()); |
| | |
| | | spouse.put("inInstitution", first(application.getSpouseInInstitution())); |
| | | spouse.put("workExperience", first(application.getSpouseWorkExperience())); |
| | | result.put("spouse", spouse); |
| | | result.put("spouseIdCardNo", spouse.get("idCardNo")); |
| | | result.put("spousePoliticalStatus", spouse.get("politicalStatus")); |
| | | result.put("spouseEducation", spouse.get("education")); |
| | | result.put("spouseSchoolMajor", spouse.get("schoolMajor")); |
| | | result.put("spouseWorkUnit", spouse.get("workUnit")); |
| | | result.put("spouseInInstitution", spouse.get("inInstitution")); |
| | | result.put("spouseWorkExperience", spouse.get("workExperience")); |
| | | |
| | | Map<String, Object> child = new LinkedHashMap<>(); |
| | | child.put("id", application.getChildId()); |
| | |
| | | result.put("child", child); |
| | | return result; |
| | | } |
| | | |
| | | private Map<String, Object> operationLogView(ServiceApplicationOperationLog log) { |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("id", log.getId()); |
| | | result.put("applicationId", log.getApplicationId()); |
| | | result.put("applicationNo", log.getApplicationNo()); |
| | | result.put("operationType", log.getOperationType()); |
| | | result.put("operationTypeText", "SUBMIT".equals(log.getOperationType()) ? "提交申请" : "状态更新"); |
| | | result.put("beforeStatus", log.getBeforeStatus()); |
| | | result.put("afterStatus", log.getAfterStatus()); |
| | | result.put("operatorType", log.getOperatorType()); |
| | | result.put("operatorId", log.getOperatorId()); |
| | | result.put("operatorName", log.getOperatorName()); |
| | | result.put("remark", log.getRemark()); |
| | | result.put("createdAt", log.getCreatedAt()); |
| | | return result; |
| | | } |
| | | |
| | | |
| | | private List<String> exportValues(int index, ServiceApplication application) { |
| | | return List.of( |
| | |
| | | return first(application.getApplicantName(), application.getSpouseName(), application.getChildName(), application.getTalentName()); |
| | | } |
| | | |
| | | private Integer guestCount(ServiceApplication application) { |
| | | if (application.getGuestCount() != null) { |
| | | return application.getGuestCount(); |
| | | } |
| | | return ServiceAppointmentApplicationService.TYPE_HOTEL.equals(application.getApplicationType()) ? 1 : null; |
| | | } |
| | | |
| | | private String handlerName(ServiceApplication application) { |
| | | if (application.getHandledAt() == null) { |
| | | return first(application.getHandlerName()); |
| | | } |
| | | ServiceApplicationOperationLog log = operationLogMapper.selectOne( |
| | | new QueryWrapper<ServiceApplicationOperationLog>() |
| | | .eq("application_id", application.getId()) |
| | | .eq("operation_type", "STATUS_UPDATE") |
| | | .eq("after_status", application.getStatus()) |
| | | .orderByDesc("created_at", "id") |
| | | .last("limit 1") |
| | | ); |
| | | return first( |
| | | log == null ? "" : operatorName(log.getOperatorId()), |
| | | log == null ? "" : log.getOperatorName(), |
| | | application.getHandlerName() |
| | | ); |
| | | } |
| | | |
| | | private String operatorName(Long operatorId) { |
| | | return operatorId == null ? "" : first(operationLogMapper.selectOperatorName(operatorId)); |
| | | } |
| | | |
| | | private List<String> proofFileIds(String value) { |
| | | if (value == null || value.isBlank()) { |
| | | return List.of(); |
| | |
| | | private String clean(String value) { |
| | | return value == null ? "" : value.trim(); |
| | | } |
| | | } |
| | | } |