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<Map<String, Object>> 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<String, Object> detail(Long packageId) {
|
Map<String, Object> row = packageRow(packageId);
|
Map<String, Object> result = new LinkedHashMap<>();
|
result.put("packageId", row.get("packageId"));
|
result.put("name", row.get("packageName"));
|
result.put("heroImage", detailHeroImage(row));
|
// 商户在「图文详情」里可以传多张:顶部大图取第一张,剩下的(含第一张)在这里整组返回,
|
// 详情页按顺序铺出来,否则商户传了几张也只能看到一张。
|
result.put("detailImages", imageUrls(row.get("productImages")).stream()
|
.filter(url -> !url.isBlank())
|
.toList());
|
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<Map<String, Object>> items(Long packageId) {
|
Map<String, Object> row = packageRow(packageId);
|
String detailText = text(row.get("detailText")).trim();
|
if (detailText.isBlank()) return List.of();
|
try {
|
List<Map<String, Object>> records = JSON.parseObject(detailText, new com.alibaba.fastjson.TypeReference<>() {});
|
if (records == null) return List.of();
|
java.util.ArrayList<Map<String, Object>> result = new java.util.ArrayList<>();
|
for (int index = 0; index < records.size(); index++) {
|
Map<String, Object> item = records.get(index);
|
List<String> genders = JSON.parseArray(JSON.toJSONString(item.get("gender")), String.class);
|
Map<String, Object> 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<String, Object> medicalMerchant(Long merchantId) {
|
if (merchantId == null || merchantId <= 0) throw new BusinessException(ErrorCode.BAD_REQUEST, "体检医院信息不能为空");
|
List<Map<String, Object>> 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<String, Object> packageRow(Long packageId) {
|
if (packageId == null || packageId <= 0) throw new BusinessException(ErrorCode.BAD_REQUEST, "体检套餐信息不能为空");
|
List<Map<String, Object>> rows = query("""
|
select p.id as package_id, p.merchant_id, p.product_name as package_name, p.cover_url as hero_image,
|
p.product_images as product_images,
|
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);
|
}
|
|
/**
|
* 详情页大图:优先用商户在「商品详情 → 图文详情」上传的第一张图({@code product_images}),
|
* 没配时才回落商品封面图。
|
*
|
* <p>图文详情才是商户为详情页配的图,封面图只是列表里的缩略图。详情页直接取封面,
|
* 商户改了图文详情也看不到任何变化,表现成"详情图片没生效"。
|
*/
|
private String detailHeroImage(Map<String, Object> row) {
|
for (String image : imageUrls(row.get("productImages"))) {
|
if (!image.isBlank()) return image;
|
}
|
return text(row.get("heroImage"));
|
}
|
|
/** {@code product_images} 是 json 数组列(驱动按字符串返回);内容不是数组或格式坏掉时按"没配"处理。 */
|
private List<String> imageUrls(Object value) {
|
String json = text(value).trim();
|
if (!json.startsWith("[")) return List.of();
|
try {
|
List<String> parsed = JSON.parseArray(json, String.class);
|
if (parsed == null) return List.of();
|
return parsed.stream().map(item -> item == null ? "" : item.trim()).toList();
|
} catch (RuntimeException ignored) {
|
return List.of();
|
}
|
}
|
|
private Map<String, Object> packageListView(Map<String, Object> row) {
|
Map<String, Object> 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<String, Object> row) {
|
return number(row.get("status")) == 1 && number(row.get("stock")) != 0;
|
}
|
|
private String unavailableReason(Map<String, Object> 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<Map<String, Object>> 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; }
|
}
|