wenminjie
2026-08-04 93414e8ee25852aca467213402585d0e65c08a4a
talent-card-server/src/main/java/com/talentcard/server/service/ServiceAppointmentAdminService.java
@@ -20,8 +20,10 @@
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;
@@ -39,27 +41,31 @@
    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,
    public PageResult<Map<String, Object>> page(String applicationType, String keyword, String applicantName,
                                                String applicantMobile, String serviceKeyword,
                                                String cardLevel, 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, 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, status, submittedStart, submittedEnd)
                .orderByDesc("submitted_at", "id")
                .last("limit " + safeSize + " offset " + ((safePage - 1) * safeSize));
        List<Map<String, Object>> records = applicationMapper.selectList(pageWrapper).stream()
@@ -73,6 +79,16 @@
        ServiceApplication application = application(id);
        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) {
@@ -98,14 +114,29 @@
        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,
    public byte[] exportExcel(String applicationType, String keyword, String applicantName,
                              String applicantMobile, String serviceKeyword,
                              String cardLevel, String status, String submittedStart, String submittedEnd) {
        QueryWrapper<ServiceApplication> wrapper = filter(applicationType, keyword, serviceKeyword, cardLevel, status, submittedStart, submittedEnd)
        QueryWrapper<ServiceApplication> wrapper = filter(applicationType, keyword, applicantName, applicantMobile, serviceKeyword, cardLevel, status, submittedStart, submittedEnd)
                .orderByDesc("submitted_at", "id");
        List<ServiceApplication> applications = applicationMapper.selectList(wrapper);
        String[] headers = {
@@ -142,7 +173,8 @@
        }
    }
    private QueryWrapper<ServiceApplication> filter(String applicationType, String keyword, String serviceKeyword,
    private QueryWrapper<ServiceApplication> filter(String applicationType, String keyword, String applicantName,
                                                    String applicantMobile, String serviceKeyword,
                                                    String cardLevel, String status, String submittedStart, String submittedEnd) {
        QueryWrapper<ServiceApplication> wrapper = new QueryWrapper<ServiceApplication>().eq("deleted", 0);
        String type = normalizeFilterType(applicationType);
@@ -160,6 +192,20 @@
                    .or().like("applicant_mobile", like)
                    .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()) {
@@ -201,6 +247,7 @@
        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("submittedAt", application.getSubmittedAt());
        result.put("handledAt", application.getHandledAt());
        result.put("handlerName", application.getHandlerName());
@@ -248,6 +295,24 @@
        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(
                String.valueOf(index),