shinnosuke
2026-08-21 bc2efbdb52190fe2e7be9920bbed6902d4ee28aa
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
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", 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<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,
                       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<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; }
}