package com.talentcard.server.service;
|
|
import java.time.LocalDate;
|
import java.time.LocalDateTime;
|
import java.util.LinkedHashMap;
|
import java.util.List;
|
import java.util.Locale;
|
import java.util.Map;
|
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.talentcard.server.business.PageResult;
|
import com.talentcard.server.business.merchant.MerchantTypeCatalog;
|
import com.talentcard.server.common.BusinessException;
|
import com.talentcard.server.common.ErrorCode;
|
import com.talentcard.server.entity.BenefitFamilyCardApplication;
|
import com.talentcard.server.entity.BenefitTalentCard;
|
import com.talentcard.server.entity.MerchantDisplayPolicyEvent;
|
import com.talentcard.server.mapper.BenefitFamilyCardApplicationMapper;
|
import com.talentcard.server.mapper.BenefitTalentCardMapper;
|
import com.talentcard.server.mapper.MerchantDisplayPolicyEventMapper;
|
import com.talentcard.server.mapper.MerchantDisplayPolicyMerchantMapper;
|
import com.talentcard.server.request.MerchantDisplayPolicyEventRequest;
|
import org.springframework.stereotype.Service;
|
import org.springframework.transaction.annotation.Transactional;
|
|
@Service
|
public class MerchantDisplayPolicyPortalService {
|
private final MerchantDisplayPolicyMerchantMapper merchantMapper;
|
private final MerchantDisplayPolicyEventMapper eventMapper;
|
private final BenefitTalentCardMapper cardMapper;
|
private final BenefitFamilyCardApplicationMapper familyMapper;
|
|
public MerchantDisplayPolicyPortalService(
|
MerchantDisplayPolicyMerchantMapper merchantMapper,
|
MerchantDisplayPolicyEventMapper eventMapper,
|
BenefitTalentCardMapper cardMapper,
|
BenefitFamilyCardApplicationMapper familyMapper
|
) {
|
this.merchantMapper = merchantMapper;
|
this.eventMapper = eventMapper;
|
this.cardMapper = cardMapper;
|
this.familyMapper = familyMapper;
|
}
|
|
@Transactional(readOnly = true)
|
public PageResult<Map<String, Object>> page(Long talentId, Long cardId, String merchantType, String keyword, int page, int size) {
|
CardContext card = cardContext(talentId, cardId);
|
int safePage = Math.max(page, 1);
|
int safeSize = Math.min(Math.max(size, 1), 100);
|
List<String> aliases = cardLevelAliases(card.cardLevel());
|
List<Map<String, Object>> records = merchantMapper.selectPortalPage(
|
aliases,
|
merchantTypeCode(merchantType),
|
clean(keyword),
|
safeSize,
|
(safePage - 1) * safeSize
|
).stream().map(row -> portalView(row, card)).toList();
|
Long total = merchantMapper.countPortalPage(aliases, merchantTypeCode(merchantType), clean(keyword));
|
return new PageResult<>(records, safePage, safeSize, total == null ? 0 : total);
|
}
|
|
@Transactional(readOnly = true)
|
public Map<String, Object> detail(Long merchantId, Long talentId, Long cardId) {
|
Long id = requireMerchantId(merchantId);
|
CardContext card = cardContext(talentId, cardId);
|
Map<String, Object> row = merchantMapper.selectPortalDetail(id, cardLevelAliases(card.cardLevel()));
|
if (row == null || row.isEmpty()) {
|
throw new BusinessException(ErrorCode.NOT_FOUND, "商户优惠政策不存在或当前人才卡类型不可用");
|
}
|
return portalView(row, card);
|
}
|
|
private String merchantTypeCode(String value) {
|
String cleaned = clean(value);
|
return cleaned.isBlank() ? "" : MerchantTypeCatalog.canonicalCode(cleaned);
|
}
|
|
@Transactional
|
public Map<String, Object> recordEvent(Long merchantId, MerchantDisplayPolicyEventRequest request) {
|
Long id = requireMerchantId(merchantId);
|
String eventType = normalizeEventType(request.eventType());
|
CardContext card = cardContext(request.talentId(), request.cardId());
|
Map<String, Object> row = merchantMapper.selectPortalDetail(id, cardLevelAliases(card.cardLevel()));
|
if (row == null || row.isEmpty()) {
|
throw new BusinessException(ErrorCode.NOT_FOUND, "商户优惠政策不存在或当前人才卡类型不可用");
|
}
|
MerchantDisplayPolicyEvent event = new MerchantDisplayPolicyEvent();
|
event.setMerchantId(id);
|
event.setTalentId(card.sourceTalentId());
|
event.setCardId(card.sourceCardId());
|
event.setPrimaryCardId(card.primaryCardId());
|
event.setCardLevel(card.cardLevel());
|
event.setEventType(eventType);
|
event.setRemark(trimToLength(request.remark(), 255));
|
event.setCreatedAt(LocalDateTime.now());
|
event.setDeleted(0);
|
if (eventMapper.insert(event) != 1) {
|
throw new BusinessException(ErrorCode.INTERNAL_ERROR, "浏览/咨询记录保存失败");
|
}
|
Map<String, Object> result = new LinkedHashMap<>();
|
result.put("id", event.getId());
|
result.put("merchantId", event.getMerchantId());
|
result.put("eventType", event.getEventType());
|
result.put("eventTypeName", "CONSULT".equals(event.getEventType()) ? "咨询" : "浏览");
|
result.put("talentId", event.getTalentId());
|
result.put("cardId", event.getCardId());
|
result.put("primaryCardId", event.getPrimaryCardId());
|
result.put("cardLevel", event.getCardLevel());
|
result.put("createdAt", event.getCreatedAt());
|
return result;
|
}
|
|
private Map<String, Object> portalView(Map<String, Object> row, CardContext card) {
|
Map<String, Object> result = new LinkedHashMap<>();
|
result.put("merchantId", value(row, "merchantId"));
|
result.put("merchantName", text(row, "merchantName"));
|
result.put("merchantType", text(row, "merchantType"));
|
result.put("contactName", text(row, "contactName"));
|
result.put("contactMobile", text(row, "contactMobile"));
|
result.put("logoUrl", text(row, "logoUrl"));
|
result.put("coverUrl", text(row, "coverUrl"));
|
result.put("address", text(row, "address"));
|
result.put("longitude", value(row, "longitude"));
|
result.put("latitude", value(row, "latitude"));
|
result.put("policyId", value(row, "benefitId"));
|
result.put("policyText", text(row, "policyText"));
|
result.put("benefitText", text(row, "policyText"));
|
result.put("rightsText", text(row, "rightsText"));
|
result.put("handlingInstructions", text(row, "handlingInstructions"));
|
result.put("serviceLabel", text(row, "serviceLabel"));
|
result.put("cardLevel", text(row, "cardLevel"));
|
result.put("cardName", text(row, "cardName"));
|
result.put("theme", text(row, "theme"));
|
result.put("available", intValue(value(row, "status")) == 1);
|
result.put("sourceTalentId", card.sourceTalentId());
|
result.put("sourceCardId", card.sourceCardId());
|
result.put("sourceCardType", card.sourceCardType());
|
result.put("primaryCardId", card.primaryCardId());
|
result.put("primaryCardLevel", card.cardLevel());
|
result.put("familyCard", card.familyCard());
|
result.put("policyUpdatedAt", value(row, "policyUpdatedAt"));
|
return result;
|
}
|
|
private CardContext cardContext(Long talentId, Long cardId) {
|
if ((talentId == null || talentId <= 0) && (cardId == null || cardId <= 0)) {
|
throw new BusinessException(ErrorCode.BAD_REQUEST, "人才用户ID或人才卡ID不能为空");
|
}
|
BenefitTalentCard source = cardId == null ? activeCardForTalent(talentId) : activeCard(cardId, talentId);
|
String sourceType = clean(source.getCardType()).toUpperCase(Locale.ROOT);
|
if (sourceType.isBlank() || "PERSONAL".equals(sourceType)) {
|
return new CardContext(source.getTalentId(), source.getId(), sourceType, source.getId(), text(source.getCardLevel()), false);
|
}
|
BenefitFamilyCardApplication owner = familyMapper.selectOne(new QueryWrapper<BenefitFamilyCardApplication>()
|
.eq("deleted", 0)
|
.eq("family_card_id", source.getId())
|
.isNotNull("talent_id")
|
.orderByDesc("id")
|
.last("limit 1"));
|
if (owner == null) {
|
throw new BusinessException(ErrorCode.CONFLICT, "亲属附属卡未找到绑定的主卡");
|
}
|
BenefitTalentCard primary = activePersonalCard(owner.getTalentId());
|
return new CardContext(source.getTalentId(), source.getId(), sourceType, primary.getId(), text(primary.getCardLevel()), true);
|
}
|
|
private BenefitTalentCard activeCard(Long cardId, Long talentId) {
|
QueryWrapper<BenefitTalentCard> query = activeCardQuery()
|
.eq("id", cardId);
|
if (talentId != null && talentId > 0) {
|
query.eq("talent_id", talentId);
|
}
|
BenefitTalentCard card = cardMapper.selectOne(query.last("limit 1"));
|
if (card == null) {
|
throw new BusinessException(ErrorCode.NOT_FOUND, "有效人才卡不存在");
|
}
|
return card;
|
}
|
|
private BenefitTalentCard activeCardForTalent(Long talentId) {
|
BenefitTalentCard card = cardMapper.selectOne(activeCardQuery()
|
.eq("talent_id", talentId)
|
.last("order by case when card_type = 'PERSONAL' then 0 else 1 end, id desc limit 1"));
|
if (card == null) {
|
throw new BusinessException(ErrorCode.NOT_FOUND, "当前用户未持有有效人才卡");
|
}
|
return card;
|
}
|
|
private BenefitTalentCard activePersonalCard(Long talentId) {
|
BenefitTalentCard card = cardMapper.selectOne(activeCardQuery()
|
.eq("talent_id", talentId)
|
.eq("card_type", "PERSONAL")
|
.orderByDesc("id")
|
.last("limit 1"));
|
if (card == null) {
|
throw new BusinessException(ErrorCode.CONFLICT, "未找到有效主卡");
|
}
|
return card;
|
}
|
|
private QueryWrapper<BenefitTalentCard> activeCardQuery() {
|
LocalDate today = LocalDate.now();
|
LocalDateTime now = LocalDateTime.now();
|
return new QueryWrapper<BenefitTalentCard>()
|
.eq("deleted", 0)
|
.eq("status", 1)
|
.and(wrapper -> wrapper.isNull("valid_start").or().le("valid_start", today))
|
.and(wrapper -> wrapper.isNull("valid_end").or().ge("valid_end", today))
|
.and(wrapper -> wrapper.isNull("expire_time").or().gt("expire_time", now));
|
}
|
|
private List<String> cardLevelAliases(String cardLevel) {
|
String normalized = normalizeCardLevel(cardLevel);
|
if ("EXCELLENT".equals(normalized)) {
|
return List.of("EXCELLENT", "卓越人才卡", "卓越");
|
}
|
if ("OUTSTANDING".equals(normalized)) {
|
return List.of("OUTSTANDING", "杰出人才卡", "杰出");
|
}
|
if ("ELITE".equals(normalized)) {
|
return List.of("ELITE", "菁英人才卡", "菁英");
|
}
|
return List.of(normalized);
|
}
|
|
private String normalizeCardLevel(String value) {
|
String raw = clean(value);
|
String upper = raw.toUpperCase(Locale.ROOT);
|
if (upper.contains("EXCELLENT") || raw.contains("卓越")) {
|
return "EXCELLENT";
|
}
|
if (upper.contains("OUTSTANDING") || raw.contains("杰出")) {
|
return "OUTSTANDING";
|
}
|
if (upper.contains("ELITE") || raw.contains("菁英")) {
|
return "ELITE";
|
}
|
return upper;
|
}
|
|
private String normalizeEventType(String value) {
|
String raw = clean(value);
|
String upper = raw.toUpperCase(Locale.ROOT);
|
if ("VIEW".equals(upper) || "浏览".equals(raw)) {
|
return MerchantDisplayPolicyEvent.EVENT_VIEW;
|
}
|
if ("CONSULT".equals(upper) || "咨询".equals(raw)) {
|
return MerchantDisplayPolicyEvent.EVENT_CONSULT;
|
}
|
throw new BusinessException(ErrorCode.BAD_REQUEST, "事件类型只能为VIEW浏览或CONSULT咨询");
|
}
|
|
private Long requireMerchantId(Long merchantId) {
|
if (merchantId == null || merchantId <= 0) {
|
throw new BusinessException(ErrorCode.BAD_REQUEST, "商户ID不能为空");
|
}
|
return merchantId;
|
}
|
|
private String trimToLength(String value, int maxLength) {
|
String text = clean(value);
|
return text.length() <= maxLength ? text : text.substring(0, maxLength);
|
}
|
|
private Object value(Map<String, Object> row, String key) {
|
return row == null ? null : row.get(key);
|
}
|
|
private String text(Map<String, Object> row, String key) {
|
return text(value(row, key));
|
}
|
|
private String text(Object value) {
|
return value == null ? "" : String.valueOf(value).trim();
|
}
|
|
private int intValue(Object value) {
|
return value instanceof Number number ? number.intValue() : 0;
|
}
|
|
private String clean(String value) {
|
return value == null ? "" : value.trim();
|
}
|
|
private record CardContext(
|
Long sourceTalentId,
|
Long sourceCardId,
|
String sourceCardType,
|
Long primaryCardId,
|
String cardLevel,
|
boolean familyCard
|
) {
|
}
|
}
|