package com.talentcard.server.business.portal; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import com.alibaba.fastjson.JSON; import com.talentcard.server.business.DatabaseRows; 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 MedicalPackagePortalService { private final JdbcTemplate jdbc; public MedicalPackagePortalService(JdbcTemplate jdbc) { this.jdbc = jdbc; } @Transactional(readOnly = true) public List> packages(Long merchantId) { medicalMerchant(merchantId); return query(""" select p.id, p.product_name as package_name, p.cover_url as hero_image, case when coalesce(p.sale_price, 0) = 0 then 1 else 0 end as free_status, '' as applicable_cards, coalesce( nullif(json_unquote(json_extract(p.purchase_notice, '$.availableTime')), ''), nullif(json_unquote(json_extract(p.purchase_notice, '$.validPeriod')), ''), '' ) as open_time, p.stock, p.status, '' as unavailable_reason from merchant_product p where p.merchant_id = ? and p.deleted = 0 and p.audit_status = 2 and p.status = 1 order by p.sort_no desc, p.id desc """, merchantId).stream().map(this::packageListView).toList(); } @Transactional(readOnly = true) public Map detail(Long packageId) { Map row = packageRow(packageId); Map result = new LinkedHashMap<>(); result.put("packageId", row.get("packageId")); result.put("name", row.get("packageName")); result.put("heroImage", row.get("heroImage")); result.put("free", number(row.get("freeStatus")) == 1); result.put("freeLabel", number(row.get("freeStatus")) == 1 ? "免费" : "优惠"); result.put("applicableCards", row.get("applicableCards")); result.put("available", available(row)); result.put("unavailableReason", unavailableReason(row)); result.put("hospital", Map.of( "hospitalId", row.get("merchantId"), "name", row.get("hospitalName"), "address", text(row.get("hospitalAddress")), "mobile", text(row.get("hospitalMobile")), "latitude", row.get("hospitalLatitude"), "longitude", row.get("hospitalLongitude") )); return result; } @Transactional(readOnly = true) public List> items(Long packageId) { Map row = packageRow(packageId); String detailText = text(row.get("detailText")).trim(); if (detailText.isBlank()) return List.of(); try { List> records = JSON.parseObject(detailText, new com.alibaba.fastjson.TypeReference<>() {}); if (records == null) return List.of(); java.util.ArrayList> result = new java.util.ArrayList<>(); for (int index = 0; index < records.size(); index++) { Map item = records.get(index); List genders = JSON.parseArray(JSON.toJSONString(item.get("gender")), String.class); Map normalized = new LinkedHashMap<>(); normalized.put("id", index + 1); normalized.put("name", text(item.get("name"))); normalized.put("meaning", text(item.get("meaning"))); normalized.put("male", genders != null && genders.contains("男")); normalized.put("female", genders != null && genders.contains("女")); result.add(normalized); } return result; } catch (RuntimeException ignored) { return List.of(); } } private Map medicalMerchant(Long merchantId) { if (merchantId == null || merchantId <= 0) throw new BusinessException(ErrorCode.BAD_REQUEST, "体检医院信息不能为空"); List> rows = query(""" select m.id from merchant m where m.id = ? and m.merchant_type_code = 'medical' and m.deleted = 0 """, merchantId); if (rows.isEmpty()) throw new BusinessException(ErrorCode.NOT_FOUND, "体检医院不存在"); return rows.get(0); } private Map packageRow(Long packageId) { if (packageId == null || packageId <= 0) throw new BusinessException(ErrorCode.BAD_REQUEST, "体检套餐信息不能为空"); List> rows = query(""" select p.id as package_id, p.merchant_id, p.product_name as package_name, p.cover_url as hero_image, case when coalesce(p.sale_price, 0) = 0 then 1 else 0 end as free_status, '' as applicable_cards, coalesce( nullif(json_unquote(json_extract(p.purchase_notice, '$.availableTime')), ''), nullif(json_unquote(json_extract(p.purchase_notice, '$.validPeriod')), ''), '' ) as open_time, p.stock, p.status, '' as unavailable_reason, p.detail_text, m.merchant_name as hospital_name, m.address as hospital_address, m.contact_mobile as hospital_mobile, m.latitude as hospital_latitude, m.longitude as hospital_longitude from merchant_product p join merchant m on m.id = p.merchant_id and m.deleted = 0 and m.merchant_type_code = 'medical' where p.id = ? and p.deleted = 0 and p.audit_status = 2 """, packageId); if (rows.isEmpty()) throw new BusinessException(ErrorCode.NOT_FOUND, "体检套餐不存在"); return rows.get(0); } private Map packageListView(Map row) { Map result = new LinkedHashMap<>(); result.put("id", row.get("id")); result.put("name", row.get("packageName")); result.put("heroImage", row.get("heroImage")); result.put("openTime", row.get("openTime")); result.put("free", number(row.get("freeStatus")) == 1); result.put("applicableCards", row.get("applicableCards")); result.put("available", available(row)); result.put("unavailableReason", unavailableReason(row)); return result; } private boolean available(Map row) { return number(row.get("status")) == 1 && number(row.get("stock")) != 0; } private String unavailableReason(Map row) { if (number(row.get("status")) != 1) return text(row.get("unavailableReason"), "套餐已停用"); if (number(row.get("stock")) == 0) return text(row.get("unavailableReason"), "当前库存不足"); return ""; } private List> query(String sql, Object... args) { return DatabaseRows.normalize(jdbc.queryForList(sql, args)); } private int number(Object value) { return value instanceof Number number ? number.intValue() : Integer.parseInt(String.valueOf(value)); } private String text(Object value) { return value == null ? "" : String.valueOf(value); } private String text(Object value, String fallback) { String valueText = text(value).trim(); return valueText.isBlank() ? fallback : valueText; } }