From 8dc19f0bd6c4abdc9cec1f9b99b90c33b9da966f Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期一, 12 十月 2026 17:24:12 +0800
Subject: [PATCH] fix(settlement): 共用收款主体那格改写「共用收款主体(N 家)」,悬停列出名单

---
 talent-card-server/src/main/java/com/talentcard/server/service/DependentCardApplyAdminService.java |  168 +++++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 149 insertions(+), 19 deletions(-)

diff --git a/talent-card-server/src/main/java/com/talentcard/server/service/DependentCardApplyAdminService.java b/talent-card-server/src/main/java/com/talentcard/server/service/DependentCardApplyAdminService.java
index 7deb67e..795c5c3 100644
--- a/talent-card-server/src/main/java/com/talentcard/server/service/DependentCardApplyAdminService.java
+++ b/talent-card-server/src/main/java/com/talentcard/server/service/DependentCardApplyAdminService.java
@@ -1,20 +1,24 @@
 package com.talentcard.server.service;
 
+import java.time.LocalDate;
 import java.time.LocalDateTime;
+import java.util.LinkedHashMap;
 import java.util.List;
+import java.util.Map;
 
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.talentcard.server.business.PageResult;
-import com.talentcard.server.request.DependentCardAuditRequest;
 import com.talentcard.server.common.BusinessException;
 import com.talentcard.server.common.ErrorCode;
 import com.talentcard.server.entity.DependentCardApply;
 import com.talentcard.server.entity.DependentCardApplyStatusLog;
 import com.talentcard.server.mapper.DependentCardApplyMapper;
 import com.talentcard.server.mapper.DependentCardApplyStatusLogMapper;
+import com.talentcard.server.request.DependentCardAuditRequest;
 import com.talentcard.server.response.DependentCardApplyAdminResponse;
 import com.talentcard.server.response.DependentCardApplyStatusLogResponse;
+import org.springframework.jdbc.core.JdbcTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -26,46 +30,91 @@
 
 	private final DependentCardApplyMapper mapper;
 	private final DependentCardApplyStatusLogMapper statusLogMapper;
+	private final JdbcTemplate jdbcTemplate;
 
 	public DependentCardApplyAdminService(
 			DependentCardApplyMapper mapper,
-			DependentCardApplyStatusLogMapper statusLogMapper
+			DependentCardApplyStatusLogMapper statusLogMapper,
+			JdbcTemplate jdbcTemplate
 	) {
 		this.mapper = mapper;
 		this.statusLogMapper = statusLogMapper;
+		this.jdbcTemplate = jdbcTemplate;
 	}
 
 	@Transactional(readOnly = true)
 	public PageResult<DependentCardApplyAdminResponse> page(
 			Long talentCardId,
+			String familyKeyword,
+			String talentKeyword,
+			String cardLevel,
 			Integer status,
+			LocalDate applyStart,
+			LocalDate applyEnd,
+			boolean management,
 			int page,
 			int size
 	) {
 		validateStatusFilter(status);
+		if (management && status != null
+				&& status != DependentCardApply.STATUS_APPROVED
+				&& status != DependentCardApply.STATUS_CANCELLED) {
+			throw new BusinessException(ErrorCode.BAD_REQUEST, "家属卡管理状态只能为正常或已解绑");
+		}
+		if (applyStart != null && applyEnd != null && applyStart.isAfter(applyEnd)) {
+			throw new BusinessException(ErrorCode.BAD_REQUEST,
+					management ? "注册开始时间不能晚于结束时间" : "申请开始时间不能晚于结束时间");
+		}
 		int safePage = Math.max(page, 1);
 		int safeSize = Math.min(Math.max(size, 1), 100);
 		LambdaQueryWrapper<DependentCardApply> query = Wrappers.<DependentCardApply>lambdaQuery()
 				.eq(talentCardId != null, DependentCardApply::getTalentCardId, talentCardId)
-				.eq(status != null, DependentCardApply::getStatus, status);
+				.eq(status != null, DependentCardApply::getStatus, status)
+				.in(management && status == null, DependentCardApply::getStatus,
+						DependentCardApply.STATUS_APPROVED, DependentCardApply.STATUS_CANCELLED)
+				.eq(cardLevel != null && !cardLevel.isBlank(), DependentCardApply::getCardLevel, trimToNull(cardLevel))
+				.and(familyKeyword != null && !familyKeyword.isBlank(), wrapper -> wrapper
+						.like(DependentCardApply::getDependentName, familyKeyword.trim())
+						.or()
+						.like(DependentCardApply::getDependentMobile, familyKeyword.trim())
+						.or()
+						.like(DependentCardApply::getDependentIdCardNo, familyKeyword.trim()))
+				.and(talentKeyword != null && !talentKeyword.isBlank(), wrapper -> wrapper
+						.like(DependentCardApply::getTalentName, talentKeyword.trim())
+						.or()
+						.like(DependentCardApply::getTalentMobile, talentKeyword.trim())
+						.or()
+						.like(DependentCardApply::getTalentCardNo, talentKeyword.trim())
+						.or()
+						.apply("exists (select 1 from talent_user u where u.id = dependent_card_apply.talent_id and u.deleted = 0 and u.id_card_no like concat('%', {0}, '%'))", talentKeyword.trim()))
+				.ge(!management && applyStart != null, DependentCardApply::getApplyTime,
+						applyStart == null ? null : applyStart.atStartOfDay())
+				.lt(!management && applyEnd != null, DependentCardApply::getApplyTime,
+						applyEnd == null ? null : applyEnd.plusDays(1).atStartOfDay())
+				.ge(management && applyStart != null, DependentCardApply::getAuditTime,
+						applyStart == null ? null : applyStart.atStartOfDay())
+				.lt(management && applyEnd != null, DependentCardApply::getAuditTime,
+						applyEnd == null ? null : applyEnd.plusDays(1).atStartOfDay());
 		long total = mapper.selectCount(query);
 		if (total == 0) {
 			return new PageResult<>(List.of(), safePage, safeSize, 0);
 		}
 		long offset = (long) (safePage - 1) * safeSize;
 		List<DependentCardApplyAdminResponse> records = mapper.selectList(
-						query.orderByDesc(DependentCardApply::getApplyTime)
+						query.orderByDesc(management, DependentCardApply::getAuditTime)
+								.orderByDesc(!management, DependentCardApply::getApplyTime)
 								.orderByDesc(DependentCardApply::getId)
 								.last("limit " + safeSize + " offset " + offset)
 				).stream()
-				.map(DependentCardApplyAdminResponse::from)
+				.map(apply -> DependentCardApplyAdminResponse.from(apply, applicationExtra(apply)))
 				.toList();
 		return new PageResult<>(records, safePage, safeSize, total);
 	}
 
 	@Transactional(readOnly = true)
 	public DependentCardApplyAdminResponse detail(Long id) {
-		return DependentCardApplyAdminResponse.from(requireApplication(id));
+		DependentCardApply apply = requireApplication(id);
+		return DependentCardApplyAdminResponse.from(apply, applicationExtra(apply));
 	}
 
 	@Transactional(readOnly = true)
@@ -90,7 +139,7 @@
 		int targetStatus = request.status();
 		if (targetStatus != DependentCardApply.STATUS_APPROVED
 				&& targetStatus != DependentCardApply.STATUS_REJECTED) {
-			throw new BusinessException(ErrorCode.BAD_REQUEST, "审核状态只能为1通过或2驳回");
+			throw new BusinessException(ErrorCode.BAD_REQUEST, "审核状态只能为通过或驳回");
 		}
 		String auditRemark = trimToNull(request.auditRemark());
 		if (targetStatus == DependentCardApply.STATUS_REJECTED && auditRemark == null) {
@@ -98,10 +147,10 @@
 		}
 		DependentCardApply apply = mapper.selectByIdForUpdate(id);
 		if (apply == null) {
-			throw new BusinessException(ErrorCode.NOT_FOUND, "家属附属卡申请不存在");
+			throw new BusinessException(ErrorCode.NOT_FOUND, "家属卡申请不存在");
 		}
 		if (!Integer.valueOf(DependentCardApply.STATUS_PENDING_REVIEW).equals(apply.getStatus())) {
-			throw new BusinessException(ErrorCode.CONFLICT, "该申请已审核或已解绑");
+			throw new BusinessException(ErrorCode.CONFLICT, "该申请已审核或已取消");
 		}
 		if (targetStatus == DependentCardApply.STATUS_APPROVED) {
 			ensureApprovalSlot(apply);
@@ -131,7 +180,89 @@
 		)) != 1) {
 			throw new BusinessException(ErrorCode.INTERNAL_ERROR, "审核状态记录保存失败");
 		}
-		return DependentCardApplyAdminResponse.from(mapper.selectById(id));
+		DependentCardApply updatedApply = mapper.selectById(id);
+		return DependentCardApplyAdminResponse.from(updatedApply, applicationExtra(updatedApply));
+	}
+
+	@Transactional
+	public DependentCardApplyAdminResponse unbind(Long id, Long operatorId) {
+		DependentCardApply apply = mapper.selectByIdForUpdate(id);
+		if (apply == null) {
+			throw new BusinessException(ErrorCode.NOT_FOUND, "家属卡不存在");
+		}
+		if (!Integer.valueOf(DependentCardApply.STATUS_APPROVED).equals(apply.getStatus())) {
+			throw new BusinessException(ErrorCode.CONFLICT, "仅正常状态的家属卡可以解除绑定");
+		}
+		int updated = mapper.update(
+				null,
+				Wrappers.<DependentCardApply>lambdaUpdate()
+						.eq(DependentCardApply::getId, id)
+						.eq(DependentCardApply::getStatus, DependentCardApply.STATUS_APPROVED)
+						.set(DependentCardApply::getStatus, DependentCardApply.STATUS_CANCELLED)
+		);
+		if (updated != 1) {
+			throw new BusinessException(ErrorCode.CONFLICT, "家属卡状态已变化,请刷新后重试");
+		}
+		if (statusLogMapper.insert(new DependentCardApplyStatusLog(
+				apply.getId(),
+				apply.getApplyNo(),
+				DependentCardApply.STATUS_APPROVED,
+				DependentCardApply.STATUS_CANCELLED,
+				"ADMIN",
+				operatorId,
+				"管理后台解除绑定"
+		)) != 1) {
+			throw new BusinessException(ErrorCode.INTERNAL_ERROR, "解绑状态记录保存失败");
+		}
+		DependentCardApply updatedApply = mapper.selectById(id);
+		return DependentCardApplyAdminResponse.from(updatedApply, applicationExtra(updatedApply));
+	}
+
+	private Map<String, Object> applicationExtra(DependentCardApply apply) {
+		if (apply == null || apply.getTalentCardId() == null) {
+			return Map.of();
+		}
+		List<Map<String, Object>> rows = jdbcTemplate.queryForList("""
+				select u.id_card_no as talentIdCardNo,
+				       u.gender as gender,
+				       u.contact_address as contactAddress,
+				       u.work_address as workAddress,
+				       ao.display_name as auditorName,
+				       c.valid_start as validStart,
+				       c.valid_end as validEnd,
+				       c.issue_time as issueTime,
+				       c.card_level as cardLevel,
+				       (select count(*) from dependent_card_apply approved
+				          where approved.deleted = 0
+				            and approved.talent_card_id = a.talent_card_id
+				            and approved.status = 1) as currentFamilyCardCount
+				from dependent_card_apply a
+				left join talent_user u on u.id = a.talent_id and u.deleted = 0
+				left join talent_card c on c.id = a.talent_card_id and c.deleted = 0
+				left join admin_operator ao on ao.id = a.audit_by
+				where a.id = ? and a.deleted = 0
+				limit 1
+				""", apply.getId());
+		if (rows.isEmpty()) {
+			return Map.of();
+		}
+		Map<String, Object> extra = new LinkedHashMap<>(rows.get(0));
+		// 名额按真正生效的规则算:审核/申请侧也是这个口径。之前直接读 talent_card.family_card_limit,
+		// 那一列在库里基本是 0(按卡配置那条路没启用),页面就成了「1/0」。
+		extra.put("familyCardLimit", familyCardLimit(string(extra.get("cardLevel"))));
+		return extra;
+	}
+
+	/**
+	 * 家属附属卡名额:卓越人才卡 2 个({@link #MAX_FAMILY_CARD_COUNT}),其它等级没有名额。
+	 * 展示与限流都用这一个口径,避免「页面显示 0、实际能绑 2 个」。
+	 */
+	private int familyCardLimit(String cardLevel) {
+		return cardLevel != null && ELITE_CARD_LEVEL.equals(cardLevel.trim()) ? MAX_FAMILY_CARD_COUNT : 0;
+	}
+
+	private String string(Object value) {
+		return value == null ? "" : String.valueOf(value).trim();
 	}
 
 	private void ensureApprovalSlot(DependentCardApply apply) {
@@ -139,15 +270,14 @@
 		if (ownerCard == null) {
 			throw new BusinessException(ErrorCode.CONFLICT, "主卡不存在或已失效,不能审核通过");
 		}
-		if (!ELITE_CARD_LEVEL.equals(ownerCard.getCardLevel())
-				|| !Integer.valueOf(1).equals(ownerCard.getIsFamilyCardEnabled())) {
+		if (!ELITE_CARD_LEVEL.equals(ownerCard.getCardLevel())) {
 			throw new BusinessException(ErrorCode.CONFLICT, "该主卡当前无家属附属卡权限");
 		}
-		Integer configuredLimit = ownerCard.getFamilyCardLimit();
-		if (configuredLimit == null || configuredLimit <= 0) {
-			throw new BusinessException(ErrorCode.CONFLICT, "该主卡未配置家属附属卡名额");
-		}
-		int familyCardLimit = Math.min(configuredLimit, MAX_FAMILY_CARD_COUNT);
+//		Integer configuredLimit = ownerCard.getFamilyCardLimit();
+//		if (configuredLimit == null || configuredLimit <= 0) {
+//			throw new BusinessException(ErrorCode.CONFLICT, "该主卡未配置家属附属卡名额");
+//		}
+		int familyCardLimit = familyCardLimit(ownerCard.getCardLevel());
 		long approvedCount = mapper.selectCount(
 				Wrappers.<DependentCardApply>lambdaQuery()
 						.eq(DependentCardApply::getTalentCardId, apply.getTalentCardId())
@@ -164,7 +294,7 @@
 		}
 		DependentCardApply apply = mapper.selectById(id);
 		if (apply == null) {
-			throw new BusinessException(ErrorCode.NOT_FOUND, "家属附属卡申请不存在");
+			throw new BusinessException(ErrorCode.NOT_FOUND, "家属卡申请不存在");
 		}
 		return apply;
 	}
@@ -182,4 +312,4 @@
 		}
 		return value.trim();
 	}
-}
\ No newline at end of file
+}

--
Gitblit v1.7.1