package com.talentcard.server.business.admin; import java.time.LocalDate; import java.util.ArrayList; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import com.talentcard.server.business.DatabaseRows; import com.talentcard.server.business.PageResult; import com.talentcard.server.common.BusinessException; import com.talentcard.server.common.ErrorCode; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class ChildInformationAdminService { private static final List AUDIT_STATUSES = List.of("待审核", "已通过", "审核不通过"); private final JdbcTemplate jdbc; public ChildInformationAdminService(JdbcTemplate jdbc) { this.jdbc = jdbc; } @Transactional(readOnly = true) public PageResult> page( String childKeyword, String talentKeyword, String cardLevel, String status, LocalDate applyStart, LocalDate applyEnd, int page, int size ) { validateFilter(status, applyStart, applyEnd); int safePage = Math.max(page, 1); int safeSize = Math.min(Math.max(size, 1), 100); List parameters = new ArrayList<>(); String where = where(childKeyword, talentKeyword, cardLevel, status, applyStart, applyEnd, parameters); Long total = jdbc.queryForObject("select count(*) " + baseFrom() + where, Long.class, parameters.toArray()); if (total == null || total == 0) { return new PageResult<>(List.of(), safePage, safeSize, 0); } List pageParameters = new ArrayList<>(parameters); pageParameters.add(safeSize); pageParameters.add((safePage - 1) * safeSize); List> records = DatabaseRows.normalize(jdbc.queryForList( selectFields() + baseFrom() + where + " order by ci.submitted_at desc, ci.id desc limit ? offset ?", pageParameters.toArray() )).stream().map(this::view).toList(); return new PageResult<>(records, safePage, safeSize, total); } @Transactional(readOnly = true) public Map detail(Long id) { List> rows = DatabaseRows.normalize(jdbc.queryForList( selectFields() + baseFrom() + " where ci.id = ? and ci.deleted = 0", id )); if (rows.isEmpty()) { throw new BusinessException(ErrorCode.NOT_FOUND, "子女信息不存在"); } return view(rows.get(0)); } @Transactional public Map audit(Long id, String status, String auditRemark) { String targetStatus = clean(status); if (!List.of("已通过", "审核不通过").contains(targetStatus)) { throw new BusinessException(ErrorCode.BAD_REQUEST, "审核状态只能为已通过或审核不通过"); } String remark = clean(auditRemark); if ("审核不通过".equals(targetStatus) && remark.isBlank()) { throw new BusinessException(ErrorCode.BAD_REQUEST, "审核不通过时必须填写原因"); } int updated = jdbc.update(""" update child_information set audit_status = ?, reject_reason = ?, updated_at = now() where id = ? and deleted = 0 and audit_status = '待审核' """, targetStatus, "审核不通过".equals(targetStatus) ? remark : null, id); if (updated != 1) { Integer exists = jdbc.queryForObject( "select count(*) from child_information where id = ? and deleted = 0", Integer.class, id ); if (exists == null || exists == 0) { throw new BusinessException(ErrorCode.NOT_FOUND, "子女信息不存在"); } throw new BusinessException(ErrorCode.CONFLICT, "该子女信息已审核,请刷新后重试"); } return detail(id); } private String selectFields() { return """ select ci.id, ci.child_name, ci.gender, ci.birth_date, ci.proof_file_ids, ci.audit_status as status, ci.reject_reason, ci.submitted_at as apply_time, ci.submitted_at as register_time, u.name as talent_name, u.id_card_no as talent_id_card_no, u.gender as talent_gender, u.mobile as talent_mobile, u.contact_address, u.work_address, coalesce(tc.card_no, u.talent_card_no) as talent_card_no, coalesce(tc.card_level, u.talent_level) as card_level """; } private String baseFrom() { return """ from child_information ci join talent_user u on u.id = ci.talent_id and u.deleted = 0 left join talent_card tc on tc.id = ( select max(card.id) from talent_card card where card.talent_id = ci.talent_id and card.card_type = 'PERSONAL' and card.deleted = 0 ) """; } private String where( String childKeyword, String talentKeyword, String cardLevel, String status, LocalDate applyStart, LocalDate applyEnd, List parameters ) { StringBuilder sql = new StringBuilder(" where ci.deleted = 0"); String child = clean(childKeyword); if (!child.isBlank()) { sql.append(" and ci.child_name like ?"); parameters.add("%" + child + "%"); } String talent = clean(talentKeyword); if (!talent.isBlank()) { sql.append(" and (u.name like ? or u.mobile like ? or u.talent_card_no like ? or tc.card_no like ?)"); String like = "%" + talent + "%"; parameters.addAll(List.of(like, like, like, like)); } String level = clean(cardLevel); if (!level.isBlank()) { sql.append(" and coalesce(tc.card_level, u.talent_level) = ?"); parameters.add(level); } String auditStatus = clean(status); if (!auditStatus.isBlank()) { sql.append(" and ci.audit_status = ?"); parameters.add(auditStatus); } if (applyStart != null) { sql.append(" and ci.submitted_at >= ?"); parameters.add(applyStart.atStartOfDay()); } if (applyEnd != null) { sql.append(" and ci.submitted_at < ?"); parameters.add(applyEnd.plusDays(1).atStartOfDay()); } return sql.toString(); } private Map view(Map row) { Map result = new LinkedHashMap<>(row); String proofFileIds = clean(row.get("proofFileIds")); result.remove("proofFileIds"); result.put("images", proofFileIds.isBlank() ? List.of() : Arrays.stream(proofFileIds.split(",")) .map(String::trim) .filter(value -> !value.isBlank()) .toList()); return result; } private void validateFilter(String status, LocalDate applyStart, LocalDate applyEnd) { String value = clean(status); if (!value.isBlank() && !AUDIT_STATUSES.contains(value)) { throw new BusinessException(ErrorCode.BAD_REQUEST, "子女信息状态不正确"); } if (applyStart != null && applyEnd != null && applyStart.isAfter(applyEnd)) { throw new BusinessException(ErrorCode.BAD_REQUEST, "申请开始时间不能晚于结束时间"); } } private String clean(Object value) { return value == null ? "" : String.valueOf(value).trim(); } }