package com.talentcard.server.business.portal; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; 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; import tools.jackson.core.type.TypeReference; import tools.jackson.databind.ObjectMapper; @Service public class AdminApprovalPortalService { private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final TypeReference>> APPROVAL_ITEMS = new TypeReference<>() {}; private final JdbcTemplate jdbc; public AdminApprovalPortalService(JdbcTemplate jdbc) { this.jdbc = jdbc; } @Transactional(readOnly = true) public Map detail(Long serviceId) { Map merchant = approvalService(serviceId); Map benefit = benefit(); Map result = new LinkedHashMap<>(); result.put("benefitId", benefit.get("id")); result.put("serviceId", serviceId); result.put("title", benefit.get("title")); result.put("freeLabel", benefit.get("freeLabel")); result.put("heroImage", text(merchant.get("coverUrl"), text(benefit.get("heroImage")))); result.put("available", number(benefit.get("enabled")) == 1 && number(merchant.get("status")) == 1); result.put("unavailableReason", text(benefit.get("unavailableReason"), "该权益暂未开放")); List> configuredOrganizations = configuredOrganizations(merchant); result.put("organizations", configuredOrganizations.isEmpty() ? organizations(((Number) benefit.get("id")).longValue()) : configuredOrganizations); return result; } private List> configuredOrganizations(Map merchant) { List> items = parseApprovalItems(merchant.get("usageInstructions")); if (items.isEmpty()) { String rightsText = text(merchant.get("rightsText")); if (rightsText.isBlank()) return List.of(); items = List.of(Map.of( "name", text(merchant.get("merchantName"), "行政审批服务"), "content", rightsText, "address", text(merchant.get("address")), "phone", text(merchant.get("contactMobile")))); } List> result = new java.util.ArrayList<>(); for (int index = 0; index < items.size(); index++) { Map item = items.get(index); Map organization = new LinkedHashMap<>(); organization.put("id", merchant.get("id") + "-" + index); organization.put("name", text(item.get("name"), text(merchant.get("merchantName"), "行政审批服务"))); organization.put("address", text(item.get("address"), text(merchant.get("address")))); organization.put("latitude", merchant.get("latitude")); organization.put("longitude", merchant.get("longitude")); organization.put("description", text(item.get("content"), text(merchant.get("rightsText"), "暂无权益说明"))); organization.put("phone", text(item.get("phone"), text(merchant.get("contactMobile")))); organization.put("officeHours", ""); String windowInfoUrl = text(item.get("windowInfoUrl"), text(item.get("pdfFile"), text(item.get("pdf_file")))); organization.put("windowInfoUrl", windowInfoUrl); organization.put("windows", List.of()); organization.put("hasWindowInfo", !windowInfoUrl.isBlank()); result.add(organization); } return result; } private List> parseApprovalItems(Object value) { if (value == null || String.valueOf(value).isBlank()) return List.of(); try { return OBJECT_MAPPER.readValue(String.valueOf(value), APPROVAL_ITEMS); } catch (Exception ignored) { return List.of(); } } private List> organizations(Long benefitId) { return query(""" select id, organization_name, address, latitude, longitude, rights_text, consultation_phone, office_hours, pdf_file, is_pdf_visible from admin_approval_organization where benefit_id = ? and enabled = 1 and deleted = 0 order by sort_no asc, id asc """, benefitId).stream().map(row -> { Long organizationId = ((Number) row.get("id")).longValue(); List> windows = query(""" select id, window_name, location_text from admin_approval_window where organization_id = ? and enabled = 1 and deleted = 0 order by sort_no asc, id asc """, organizationId).stream().map(window -> Map.of( "id", window.get("id"), "name", window.get("windowName"), "location", text(window.get("locationText")) )).toList(); Map organization = new LinkedHashMap<>(); organization.put("id", organizationId); organization.put("name", row.get("organizationName")); organization.put("address", text(row.get("address"))); organization.put("latitude", row.get("latitude")); organization.put("longitude", row.get("longitude")); organization.put("description", row.get("rightsText")); organization.put("phone", text(row.get("consultationPhone"))); organization.put("officeHours", text(row.get("officeHours"))); String windowInfoUrl = number(row.get("isPdfVisible")) == 1 ? text(row.get("pdfFile")) : ""; organization.put("windowInfoUrl", windowInfoUrl); organization.put("windows", windows); organization.put("hasWindowInfo", !windowInfoUrl.isBlank()); return organization; }).toList(); } private Map approvalService(Long serviceId) { if (serviceId == null || serviceId <= 0) throw new BusinessException(ErrorCode.BAD_REQUEST, "行政审批服务信息不能为空"); List> rows = query(""" select m.id, m.merchant_name, m.address, m.contact_mobile, m.cover_url, m.latitude, m.longitude, m.status, m.usage_instructions, m.rights_text from merchant m where m.id = ? and m.deleted = 0 and m.merchant_type_code = 'approval' """, serviceId); if (rows.isEmpty()) throw new BusinessException(ErrorCode.NOT_FOUND, "行政审批服务不存在"); return rows.get(0); } private Map benefit() { List> rows = query(""" select id, title, free_label, hero_image, enabled, unavailable_reason from admin_approval_benefit where deleted = 0 order by id asc limit 1 """); if (rows.isEmpty()) throw new BusinessException(ErrorCode.NOT_FOUND, "行政审批权益暂未配置"); return rows.get(0); } 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; } }