shinnosuke
2026-09-21 7844f5af457ff3b7d3f8eeac8799e8d281e635a1
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package com.talentcard.server.business.admin;
 
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
 
import com.talentcard.server.business.DatabaseRows;
import com.talentcard.server.business.PageResult;
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 ChildInformationAdminService {
 
    private static final List<String> AUDIT_STATUSES = List.of("待审核", "已通过", "审核不通过");
 
    private final JdbcTemplate jdbc;
 
    public ChildInformationAdminService(JdbcTemplate jdbc) {
        this.jdbc = jdbc;
    }
 
    @Transactional(readOnly = true)
    public PageResult<Map<String, Object>> page(
            String childKeyword,
            String talentKeyword,
            String cardLevel,
            String status,
            LocalDate applyStart,
            LocalDate applyEnd,
            int page,
            int size
    ) {
        validateFilter(status, applyStart, applyEnd);
        int safePage = Math.max(page, 1);
        int safeSize = Math.min(Math.max(size, 1), 100);
        List<Object> parameters = new ArrayList<>();
        String where = where(childKeyword, talentKeyword, cardLevel, status, applyStart, applyEnd, parameters);
        Long total = jdbc.queryForObject("select count(*) " + baseFrom() + where, Long.class, parameters.toArray());
        if (total == null || total == 0) {
            return new PageResult<>(List.of(), safePage, safeSize, 0);
        }
 
        List<Object> pageParameters = new ArrayList<>(parameters);
        pageParameters.add(safeSize);
        pageParameters.add((safePage - 1) * safeSize);
        List<Map<String, Object>> records = DatabaseRows.normalize(jdbc.queryForList(
                selectFields() + baseFrom() + where + " order by ci.submitted_at desc, ci.id desc limit ? offset ?",
                pageParameters.toArray()
        )).stream().map(this::view).toList();
        return new PageResult<>(records, safePage, safeSize, total);
    }
 
    @Transactional(readOnly = true)
    public Map<String, Object> detail(Long id) {
        List<Map<String, Object>> rows = DatabaseRows.normalize(jdbc.queryForList(
                selectFields() + baseFrom() + " where ci.id = ? and ci.deleted = 0",
                id
        ));
        if (rows.isEmpty()) {
            throw new BusinessException(ErrorCode.NOT_FOUND, "子女信息不存在");
        }
        return view(rows.get(0));
    }
 
    @Transactional
    public Map<String, Object> audit(Long id, String status, String auditRemark) {
        String targetStatus = clean(status);
        if (!List.of("已通过", "审核不通过").contains(targetStatus)) {
            throw new BusinessException(ErrorCode.BAD_REQUEST, "审核状态只能为已通过或审核不通过");
        }
        String remark = clean(auditRemark);
        if ("审核不通过".equals(targetStatus) && remark.isBlank()) {
            throw new BusinessException(ErrorCode.BAD_REQUEST, "审核不通过时必须填写原因");
        }
        int updated = jdbc.update("""
                update child_information
                set audit_status = ?, reject_reason = ?, updated_at = now()
                where id = ? and deleted = 0 and audit_status = '待审核'
                """, targetStatus, "审核不通过".equals(targetStatus) ? remark : null, id);
        if (updated != 1) {
            Integer exists = jdbc.queryForObject(
                    "select count(*) from child_information where id = ? and deleted = 0",
                    Integer.class,
                    id
            );
            if (exists == null || exists == 0) {
                throw new BusinessException(ErrorCode.NOT_FOUND, "子女信息不存在");
            }
            throw new BusinessException(ErrorCode.CONFLICT, "该子女信息已审核,请刷新后重试");
        }
        return detail(id);
    }
 
    private String selectFields() {
        return """
                select ci.id,
                       ci.child_name,
                       ci.gender,
                       ci.birth_date,
                       ci.proof_file_ids,
                       ci.audit_status as status,
                       ci.reject_reason,
                       ci.submitted_at as apply_time,
                       ci.submitted_at as register_time,
                       u.name as talent_name,
                       u.id_card_no as talent_id_card_no,
                       u.gender as talent_gender,
                       u.mobile as talent_mobile,
                       u.contact_address,
                       u.work_address,
                       coalesce(tc.card_no, u.talent_card_no) as talent_card_no,
                       coalesce(tc.card_level, u.talent_level) as card_level
                """;
    }
 
    private String baseFrom() {
        return """
                 from child_information ci
                 join talent_user u on u.id = ci.talent_id and u.deleted = 0
                 left join talent_card tc on tc.id = (
                     select max(card.id)
                     from talent_card card
                     where card.talent_id = ci.talent_id
                       and card.card_type = 'PERSONAL'
                       and card.deleted = 0
                 )
                """;
    }
 
    private String where(
            String childKeyword,
            String talentKeyword,
            String cardLevel,
            String status,
            LocalDate applyStart,
            LocalDate applyEnd,
            List<Object> parameters
    ) {
        StringBuilder sql = new StringBuilder(" where ci.deleted = 0");
        String child = clean(childKeyword);
        if (!child.isBlank()) {
            sql.append(" and ci.child_name like ?");
            parameters.add("%" + child + "%");
        }
        String talent = clean(talentKeyword);
        if (!talent.isBlank()) {
            sql.append(" and (u.name like ? or u.mobile like ? or u.talent_card_no like ? or tc.card_no like ?)");
            String like = "%" + talent + "%";
            parameters.addAll(List.of(like, like, like, like));
        }
        String level = clean(cardLevel);
        if (!level.isBlank()) {
            sql.append(" and coalesce(tc.card_level, u.talent_level) = ?");
            parameters.add(level);
        }
        String auditStatus = clean(status);
        if (!auditStatus.isBlank()) {
            sql.append(" and ci.audit_status = ?");
            parameters.add(auditStatus);
        }
        if (applyStart != null) {
            sql.append(" and ci.submitted_at >= ?");
            parameters.add(applyStart.atStartOfDay());
        }
        if (applyEnd != null) {
            sql.append(" and ci.submitted_at < ?");
            parameters.add(applyEnd.plusDays(1).atStartOfDay());
        }
        return sql.toString();
    }
 
    private Map<String, Object> view(Map<String, Object> row) {
        Map<String, Object> result = new LinkedHashMap<>(row);
        String proofFileIds = clean(row.get("proofFileIds"));
        result.remove("proofFileIds");
        result.put("images", proofFileIds.isBlank()
                ? List.of()
                : Arrays.stream(proofFileIds.split(","))
                        .map(String::trim)
                        .filter(value -> !value.isBlank())
                        .toList());
        return result;
    }
 
    private void validateFilter(String status, LocalDate applyStart, LocalDate applyEnd) {
        String value = clean(status);
        if (!value.isBlank() && !AUDIT_STATUSES.contains(value)) {
            throw new BusinessException(ErrorCode.BAD_REQUEST, "子女信息状态不正确");
        }
        if (applyStart != null && applyEnd != null && applyStart.isAfter(applyEnd)) {
            throw new BusinessException(ErrorCode.BAD_REQUEST, "申请开始时间不能晚于结束时间");
        }
    }
 
    private String clean(Object value) {
        return value == null ? "" : String.valueOf(value).trim();
    }
}