fix: centralize merchant type integration
| | |
| | | <view class="input-box"><input v-model="form.name" maxlength="20" placeholder="请输入姓名" placeholder-class="input-placeholder" /></view> |
| | | |
| | | <text class="field-label">身份证号</text> |
| | | <view class="input-box"><input :value="form.idCard" maxlength="18" placeholder="请输入身份证号" placeholder-class="input-placeholder" @input="handleIdCardInput" /></view> |
| | | <view class="input-box"><input :value="form.idCard" type="idcard" maxlength="18" placeholder="请输入身份证号" placeholder-class="input-placeholder" @input="handleIdCardInput" /></view> |
| | | |
| | | <text class="field-label">手机号</text> |
| | | <view class="input-box"><input v-model="form.phone" type="number" maxlength="11" placeholder="请输入手机号" placeholder-class="input-placeholder" /></view> |
| | |
| | | const talentCardId = ref('') |
| | | const applicationId = ref('') |
| | | const auditRemark = ref('') |
| | | const normalizeIdCard = (value: string): string => { |
| | | const raw = value.toUpperCase().replace(/[^0-9X]/g, '') |
| | | const xIndex = raw.indexOf('X') |
| | | if (xIndex >= 17) { |
| | | return raw.substring(0, xIndex).replace(/[^0-9]/g, '').slice(0, 17) + 'X' |
| | | } |
| | | return raw.replace(/[^0-9]/g, '').slice(0, 18) |
| | | } |
| | | |
| | | onLoad((options: any) => { |
| | | // #ifdef MP-WEIXIN |
| | |
| | | if (data == null) return |
| | | const next = form.value |
| | | next.name = data.dependentName != null ? data.dependentName : '' |
| | | next.idCard = data.dependentIdCardNo != null ? data.dependentIdCardNo : '' |
| | | next.idCard = data.dependentIdCardNo != null ? normalizeIdCard('' + data.dependentIdCardNo) : '' |
| | | next.phone = data.dependentMobile != null ? data.dependentMobile : '' |
| | | next.relation = data.relation != null ? data.relation : '' |
| | | form.value = next |
| | |
| | | form.value = next |
| | | } |
| | | const handleIdCardInput = (event: any) => { |
| | | const raw = (event.detail.value as string).toUpperCase() |
| | | let normalized = raw.replace(/[^0-9X]/g, '').slice(0, 18) |
| | | if (normalized.indexOf('X') >= 0 && (normalized.length != 18 || !normalized.endsWith('X'))) { |
| | | normalized = normalized.replace(/X/g, '') |
| | | } |
| | | const normalized = normalizeIdCard(event.detail.value as string) |
| | | const next = form.value |
| | | next.idCard = normalized |
| | | form.value = next |
| | |
| | | const supportsMerchantAppLogin = computed(() => ( |
| | | isServiceMerchant.value |
| | | || isApartmentMerchant.value |
| | | || isHotelMerchant.value |
| | | || isCarWashMerchant.value |
| | | || isHealthCheckMerchant.value |
| | | || isParkingMerchant.value |
| | |
| | | import com.alibaba.fastjson.JSON; |
| | | import com.talentcard.server.business.DatabaseRows; |
| | | import com.talentcard.server.business.PageResult; |
| | | import com.talentcard.server.business.merchant.MerchantTypeCatalog; |
| | | import com.talentcard.server.business.merchant.MerchantTypeCatalog.MerchantType; |
| | | import com.talentcard.server.business.merchant.auth.MerchantPasswordHasher; |
| | | import com.talentcard.server.common.BusinessException; |
| | | import com.talentcard.server.common.ErrorCode; |
| | |
| | | |
| | | @Service |
| | | public class AdminBusinessService { |
| | | |
| | | private static final Set<String> MERCHANT_APP_LOGIN_TYPES = Set.of( |
| | | "餐饮服务", "景区服务", "免费体检", "配偶推荐就业", "金融服务", "行政审批", "子女托管", |
| | | "酒店服务", "公寓租赁", "加油服务", "停车服务", "自助洗车", "购房优惠", "购车服务", |
| | | "家电服务", "数码服务", "文化服务", "电信服务", "口腔治疗保健" |
| | | ); |
| | | |
| | | private final JdbcTemplate jdbcTemplate; |
| | | |
| | |
| | | args.add("%" + contactMobile.trim() + "%"); |
| | | } |
| | | if (type != null && !type.isBlank()) { |
| | | sql.append(" and m.merchant_type = ?"); |
| | | countSql.append(" and m.merchant_type = ?"); |
| | | args.add(type); |
| | | sql.append(" and m.merchant_type_code = ?"); |
| | | countSql.append(" and m.merchant_type_code = ?"); |
| | | args.add(MerchantTypeCatalog.canonicalCode(type)); |
| | | } |
| | | if (status != null) { |
| | | sql.append(" and m.status = ?"); |
| | |
| | | |
| | | @Transactional |
| | | public Map<String, Object> createMerchant(MerchantCreateRequest request) { |
| | | validateMerchantAddress(request.merchantType(), request.address()); |
| | | MerchantType merchantType = requireMerchantType(request.merchantType()); |
| | | validateMerchantAddress(merchantType.code(), request.address()); |
| | | validateRequiredMerchantCreateFields(request); |
| | | jdbcTemplate.update(""" |
| | | insert into merchant(merchant_name, merchant_type, contact_name, contact_mobile, address, |
| | | insert into merchant(merchant_name, merchant_type, merchant_type_code, contact_name, contact_mobile, address, |
| | | logo_url, cover_url, business_status, longitude, latitude, can_login_merchant_app, group_buying_enabled, |
| | | usage_instructions, rights_text, merchant_rights_config, service_label, is_recommend, status, sort_no) |
| | | values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?) |
| | | values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, ?) |
| | | """, |
| | | request.merchantName(), |
| | | request.merchantType(), |
| | | merchantType.displayName(), |
| | | merchantType.code(), |
| | | request.contactName(), |
| | | request.contactMobile(), |
| | | request.address(), |
| | |
| | | request.rightsText(), |
| | | toJson(request.merchantRightsConfig()), |
| | | request.serviceLabel(), |
| | | request.recommend() == null ? defaultRecommend(request.merchantType()) : request.recommend(), |
| | | request.recommend() == null ? defaultRecommend(merchantType.code()) : request.recommend(), |
| | | request.sortNo() == null ? 0 : request.sortNo()); |
| | | Long id = jdbcTemplate.queryForObject("select last_insert_id()", Long.class); |
| | | ensureParkingBenefit(id, request.merchantType()); |
| | | ensureFuelBenefit(id, request.merchantType(), request.serviceLabel()); |
| | | ensureParkingBenefit(id, merchantType.code()); |
| | | ensureFuelBenefit(id, merchantType.code(), request.serviceLabel()); |
| | | syncPremiumTopicMerchant(id, merchantType, request.sortNo()); |
| | | Long storeId = createDefaultStore(id, request.merchantName(), request.address()); |
| | | syncMerchantBusinessHours(id, storeId, request.businessHours()); |
| | | if (hasMerchantAppAccess(request)) { |
| | |
| | | } |
| | | |
| | | void validateMerchantAddress(String merchantType, String address) { |
| | | if (!"公寓租赁".equals(merchantType) && (address == null || address.isBlank())) { |
| | | if (!"apartment".equals(MerchantTypeCatalog.canonicalCode(merchantType)) && (address == null || address.isBlank())) { |
| | | throw new BusinessException(ErrorCode.BAD_REQUEST, "请输入商家地址"); |
| | | } |
| | | } |
| | |
| | | } |
| | | |
| | | boolean supportsMerchantAppLogin(String merchantType) { |
| | | return MERCHANT_APP_LOGIN_TYPES.contains(merchantType); |
| | | return MerchantTypeCatalog.find(merchantType) |
| | | .map(MerchantType::supportsMerchantAppLogin) |
| | | .orElse(false); |
| | | } |
| | | |
| | | private void validateRequiredMerchantCreateFields(MerchantCreateRequest request) { |
| | | if (!Set.of("景区服务", "餐饮服务").contains(request.merchantType())) { |
| | | if (!Set.of("scenic", "food").contains(MerchantTypeCatalog.canonicalCode(request.merchantType()))) { |
| | | return; |
| | | } |
| | | if (request.loginAccount() == null || !request.loginAccount().trim().matches("^[A-Za-z0-9]{1,32}$")) { |
| | |
| | | } |
| | | |
| | | private byte defaultRecommend(String merchantType) { |
| | | return (byte) (("餐饮服务".equals(merchantType) || "景区服务".equals(merchantType)) ? 1 : 0); |
| | | return (byte) (("food".equals(merchantType) || "scenic".equals(merchantType)) ? 1 : 0); |
| | | } |
| | | |
| | | @Transactional |
| | | public Map<String, Object> updateMerchant(Long id, MerchantUpdateRequest request) { |
| | | MerchantType merchantType = resolveMerchantTypeForUpdate(id, request.merchantType()); |
| | | validateMerchantAddress(merchantType.code(), request.address()); |
| | | int updated = jdbcTemplate.update(""" |
| | | update merchant |
| | | set merchant_name = ?, merchant_type = ?, contact_name = ?, contact_mobile = ?, |
| | | set merchant_name = ?, merchant_type = ?, merchant_type_code = ?, contact_name = ?, contact_mobile = ?, |
| | | address = ?, logo_url = ?, cover_url = ?, business_status = ?, longitude = ?, latitude = ?, can_login_merchant_app = ?, |
| | | group_buying_enabled = ?, usage_instructions = ?, rights_text = ?, merchant_rights_config = ?, service_label = ?, |
| | | is_recommend = ?, sort_no = ?, status = ? |
| | | where deleted = 0 and id = ? |
| | | """, |
| | | request.merchantName(), |
| | | request.merchantType(), |
| | | merchantType.displayName(), |
| | | merchantType.code(), |
| | | request.contactName(), |
| | | request.contactMobile(), |
| | | request.address(), |
| | |
| | | request.status() == null ? 1 : request.status(), |
| | | id); |
| | | ensureUpdated(updated); |
| | | ensureParkingBenefit(id, request.merchantType()); |
| | | ensureFuelBenefit(id, request.merchantType(), request.serviceLabel()); |
| | | ensureParkingBenefit(id, merchantType.code()); |
| | | ensureFuelBenefit(id, merchantType.code(), request.serviceLabel()); |
| | | syncPremiumTopicMerchant(id, merchantType, request.sortNo()); |
| | | Long storeId = findOrCreateDefaultStore(id, request.merchantName(), request.address()); |
| | | syncMerchantBusinessHours(id, storeId, request.businessHours()); |
| | | if (request.loginPassword() != null && !request.loginPassword().isBlank()) { |
| | |
| | | } |
| | | |
| | | private void ensureParkingBenefit(Long merchantId, String merchantType) { |
| | | if (!"停车服务".equals(merchantType) && !"parking".equalsIgnoreCase(merchantType)) return; |
| | | if (!"parking".equals(merchantType)) return; |
| | | jdbcTemplate.update(""" |
| | | insert into transport_parking_benefit(merchant_id, talent_levels, benefit_text, action_mode, plate_form_mode, usage_instructions, rule_text, status, deleted) |
| | | select ?, '', '停车权益', 'plate', 'withParking', |
| | |
| | | } |
| | | |
| | | private void ensureFuelBenefit(Long merchantId, String merchantType, String serviceLabel) { |
| | | if (!"加油服务".equals(merchantType) && !"fuel".equalsIgnoreCase(merchantType)) return; |
| | | if (!"fuel".equals(merchantType)) return; |
| | | String discountText = serviceLabel == null || serviceLabel.isBlank() ? "请咨询商户" : serviceLabel.trim(); |
| | | jdbcTemplate.update(""" |
| | | insert into transport_fuel_benefit( |
| | |
| | | '具体适用油品、优惠额度、可用时段和活动规则以加油站说明为准。', 1, 0 |
| | | where not exists (select 1 from transport_fuel_benefit where merchant_id = ? and deleted = 0) |
| | | """, merchantId, discountText, merchantId); |
| | | } |
| | | |
| | | private void syncPremiumTopicMerchant(Long merchantId, MerchantType merchantType, Integer sortNo) { |
| | | if (merchantType.premiumCategoryKey() == null || merchantType.premiumBusinessType() == null) { |
| | | jdbcTemplate.update("update premium_topic_merchant set deleted = 1 where merchant_id = ? and deleted = 0", merchantId); |
| | | return; |
| | | } |
| | | jdbcTemplate.update(""" |
| | | insert into premium_topic_merchant(merchant_id, category_key, business_type, sort_no, deleted) |
| | | values (?, ?, ?, ?, 0) |
| | | on duplicate key update category_key = values(category_key), business_type = values(business_type), |
| | | sort_no = values(sort_no), deleted = 0 |
| | | """, merchantId, merchantType.premiumCategoryKey(), merchantType.premiumBusinessType(), sortNo == null ? 0 : sortNo); |
| | | } |
| | | |
| | | private MerchantType requireMerchantType(String value) { |
| | | return MerchantTypeCatalog.find(value) |
| | | .orElseThrow(() -> new BusinessException(ErrorCode.BAD_REQUEST, "不支持的商家类型")); |
| | | } |
| | | |
| | | private MerchantType resolveMerchantTypeForUpdate(Long merchantId, String value) { |
| | | MerchantType requested = requireMerchantType(value); |
| | | if (!"shopping".equals(requested.code())) return requested; |
| | | List<String> existingCodes = jdbcTemplate.queryForList( |
| | | "select merchant_type_code from merchant where id = ? and deleted = 0 limit 1", |
| | | String.class, |
| | | merchantId); |
| | | if (existingCodes.isEmpty()) return requested; |
| | | return MerchantTypeCatalog.find(existingCodes.get(0)).orElse(requested); |
| | | } |
| | | |
| | | @Transactional |
| | |
| | | args.add(like); |
| | | } |
| | | if (type != null && !type.isBlank()) { |
| | | sql.append(" and m.merchant_type = ?"); |
| | | countSql.append(" and m.merchant_type = ?"); |
| | | args.add(type); |
| | | sql.append(" and m.merchant_type_code = ?"); |
| | | countSql.append(" and m.merchant_type_code = ?"); |
| | | args.add(MerchantTypeCatalog.canonicalCode(type)); |
| | | } |
| | | if (status != null) { |
| | | sql.append(" and p.audit_status = ?"); |
| New file |
| | |
| | | package com.talentcard.server.business.merchant; |
| | | |
| | | import java.util.Collection; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.LinkedHashSet; |
| | | import java.util.List; |
| | | import java.util.Locale; |
| | | import java.util.Map; |
| | | import java.util.Optional; |
| | | import java.util.Set; |
| | | |
| | | public final class MerchantTypeCatalog { |
| | | |
| | | private static final Map<String, MerchantType> BY_ALIAS; |
| | | private static final List<MerchantType> TYPES; |
| | | |
| | | static { |
| | | List<MerchantType> types = List.of( |
| | | type("food", "餐饮服务", "暖心食享", "MERCHANT", null, null, null, true, |
| | | "餐饮", "餐饮商家"), |
| | | type("scenic", "景区服务", "悦览花城", "MERCHANT", null, null, null, true, |
| | | "景区", "景点服务"), |
| | | type("hotel", "酒店服务", "舒心安居", "MERCHANT", null, null, null, true, |
| | | "酒店", "住宿服务"), |
| | | type("apartment", "公寓租赁", "舒心安居", "MERCHANT", null, null, null, true, |
| | | "公寓租赁服务", "公寓服务"), |
| | | type("cinema", "观影服务", "怡乐生活", "CINEMA", null, null, null, true, |
| | | "theatre", "电影院", "影院", "影城", "剧院"), |
| | | type("fuel", "加油服务", "畅行无忧", "FUEL", null, null, null, true, |
| | | "加油", "加油站"), |
| | | type("parking", "停车服务", "畅行无忧", "PARKING", null, null, null, true, |
| | | "停车", "停车场"), |
| | | type("carWash", "自助洗车", "畅行无忧", "CAR_WASH", null, null, null, true, |
| | | "自主洗车", "自助洗车服务", "洗车服务"), |
| | | type("medical", "免费体检", "臻享礼遇", "MEDICAL", "medical", "MEDICAL", null, true, |
| | | "healthCheck", "体检服务", "医疗体检"), |
| | | type("employment", "配偶推荐就业", "臻享礼遇", "SPOUSE_EMPLOYMENT", "employment", "SPOUSE_EMPLOYMENT", null, true, |
| | | "就业服务", "配偶就业"), |
| | | type("finance", "金融服务", "臻享礼遇", "FINANCE", "finance", "FINANCE", null, true, |
| | | "金融"), |
| | | type("approval", "行政审批", "臻享礼遇", "ADMIN_APPROVAL", "approval", "ADMIN_APPROVAL", null, true, |
| | | "行政审批服务", "政务服务"), |
| | | type("children", "子女托管", "臻享礼遇", "CHILD_ENTRUSTMENT", "children", "CHILD_ENTRUSTMENT", null, true, |
| | | "学生托付", "学生托管", "子女托管服务"), |
| | | type("housing", "购房优惠", "惠享好物", "SHOPPING", null, null, "housing", true, |
| | | "购房服务", "住房服务"), |
| | | type("car", "购车服务", "惠享好物", "SHOPPING", null, null, "car", true, |
| | | "购车优惠", "汽车服务"), |
| | | type("appliance", "家电服务", "惠享好物", "SHOPPING", null, null, "appliance", true, |
| | | "家用电器服务"), |
| | | type("digital", "数码服务", "惠享好物", "SHOPPING", null, null, "digital", true, |
| | | "数码产品服务"), |
| | | type("culture", "文化服务", "惠享好物", "SHOPPING", null, null, "book", true, |
| | | "图书服务", "文化图书服务"), |
| | | type("telecom", "电信服务", "惠享好物", "SHOPPING", null, null, "digital", true, |
| | | "通信服务"), |
| | | type("dental", "口腔治疗保健", "臻享礼遇", "MERCHANT", null, null, null, true, |
| | | "口腔服务", "口腔保健"), |
| | | type("shopping", "购物服务", "惠享好物", "SHOPPING", null, null, null, true, |
| | | "惠享好物") |
| | | ); |
| | | Map<String, MerchantType> aliases = new LinkedHashMap<>(); |
| | | for (MerchantType type : types) { |
| | | register(aliases, type.code(), type); |
| | | register(aliases, type.displayName(), type); |
| | | for (String alias : type.aliases()) register(aliases, alias, type); |
| | | } |
| | | TYPES = List.copyOf(types); |
| | | BY_ALIAS = Map.copyOf(aliases); |
| | | } |
| | | |
| | | private MerchantTypeCatalog() { |
| | | } |
| | | |
| | | public static Optional<MerchantType> find(String value) { |
| | | return Optional.ofNullable(BY_ALIAS.get(normalize(value))); |
| | | } |
| | | |
| | | public static String canonicalCode(String value) { |
| | | return find(value).map(MerchantType::code).orElse("other"); |
| | | } |
| | | |
| | | public static String displayName(String value) { |
| | | return find(value).map(MerchantType::displayName).orElseGet(() -> value == null ? "" : value.trim()); |
| | | } |
| | | |
| | | public static List<String> codesForTopic(String topicName) { |
| | | return TYPES.stream() |
| | | .filter(type -> type.topicName().equals(topicName)) |
| | | .map(MerchantType::code) |
| | | .toList(); |
| | | } |
| | | |
| | | public static List<String> codesForRequestedValue(String value) { |
| | | List<String> topicCodes = codesForTopic(value == null ? "" : value.trim()); |
| | | if (!topicCodes.isEmpty()) return topicCodes; |
| | | return find(value).map(type -> List.of(type.code())).orElseGet(List::of); |
| | | } |
| | | |
| | | public static List<String> codesForShoppingCategory(String categoryKey) { |
| | | return TYPES.stream() |
| | | .filter(type -> categoryKey != null && categoryKey.equals(type.shoppingCategoryKey())) |
| | | .map(MerchantType::code) |
| | | .toList(); |
| | | } |
| | | |
| | | public static Optional<MerchantType> forPremiumCategory(String categoryKey) { |
| | | return TYPES.stream() |
| | | .filter(type -> categoryKey != null && categoryKey.equals(type.premiumCategoryKey())) |
| | | .findFirst(); |
| | | } |
| | | |
| | | public static Collection<MerchantType> all() { |
| | | return TYPES; |
| | | } |
| | | |
| | | private static MerchantType type( |
| | | String code, |
| | | String displayName, |
| | | String topicName, |
| | | String detailType, |
| | | String premiumCategoryKey, |
| | | String premiumBusinessType, |
| | | String shoppingCategoryKey, |
| | | boolean supportsMerchantAppLogin, |
| | | String... aliases |
| | | ) { |
| | | Set<String> values = new LinkedHashSet<>(); |
| | | if (aliases != null) values.addAll(List.of(aliases)); |
| | | return new MerchantType(code, displayName, topicName, detailType, premiumCategoryKey, |
| | | premiumBusinessType, shoppingCategoryKey, supportsMerchantAppLogin, Set.copyOf(values)); |
| | | } |
| | | |
| | | private static void register(Map<String, MerchantType> aliases, String alias, MerchantType type) { |
| | | String key = normalize(alias); |
| | | if (!key.isBlank()) aliases.put(key, type); |
| | | } |
| | | |
| | | private static String normalize(String value) { |
| | | return value == null ? "" : value.trim().toLowerCase(Locale.ROOT); |
| | | } |
| | | |
| | | public record MerchantType( |
| | | String code, |
| | | String displayName, |
| | | String topicName, |
| | | String detailType, |
| | | String premiumCategoryKey, |
| | | String premiumBusinessType, |
| | | String shoppingCategoryKey, |
| | | boolean supportsMerchantAppLogin, |
| | | Set<String> aliases |
| | | ) { |
| | | } |
| | | } |
| | |
| | | if (serviceId == null || serviceId <= 0) throw new BusinessException(ErrorCode.BAD_REQUEST, "行政审批服务信息不能为空"); |
| | | List<Map<String, Object>> rows = query(""" |
| | | select m.id |
| | | from merchant m left join premium_topic_merchant mapping on mapping.merchant_id = m.id and mapping.deleted = 0 |
| | | where m.id = ? and m.deleted = 0 |
| | | and ((mapping.category_key = 'approval') or (mapping.id is null and m.merchant_type = '行政审批')) |
| | | 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); |
| | |
| | | Map<String, Object> merchant = one(""" |
| | | select id, merchant_name, contact_mobile, address, cover_url, service_label, rights_text, merchant_rights_config, status |
| | | from merchant |
| | | where id = ? and merchant_type in ('carWash', '自主洗车', '自助洗车', '自助洗车服务') and deleted = 0 |
| | | where id = ? and merchant_type_code = 'carWash' and deleted = 0 |
| | | """, merchantId); |
| | | List<Map<String, Object>> serviceRows = query(""" |
| | | select id, talent_levels, service_point_text, tip_text, external_link_type, |
| | |
| | | if (serviceId == null || serviceId <= 0) throw new BusinessException(ErrorCode.BAD_REQUEST, "学生托付服务信息不能为空"); |
| | | List<Map<String, Object>> rows = query(""" |
| | | select m.id, m.merchant_rights_config, m.usage_instructions |
| | | from merchant m join premium_topic_merchant mapping on mapping.merchant_id = m.id |
| | | where m.id = ? and m.deleted = 0 and mapping.deleted = 0 and mapping.category_key = 'children' |
| | | from merchant m |
| | | where m.id = ? and m.deleted = 0 and m.merchant_type_code = 'children' |
| | | """, serviceId); |
| | | if (rows.isEmpty()) throw new BusinessException(ErrorCode.NOT_FOUND, "学生托付服务不存在"); |
| | | return rows.get(0); |
| | |
| | | |
| | | private Map<String, Object> cinema(Long merchantId, boolean requireAvailable) { |
| | | List<Map<String, Object>> merchants = query(""" |
| | | select id, merchant_name, merchant_type, contact_mobile, address, cover_url, service_label, rights_text, |
| | | select id, merchant_name, merchant_type_code as merchant_type, contact_mobile, address, cover_url, service_label, rights_text, |
| | | merchant_rights_config, latitude, longitude, status |
| | | from merchant where id = ? and deleted = 0 and merchant_type in ('cinema', 'theatre', '观影服务', '电影院', '影院', '影城', '剧院') |
| | | from merchant where id = ? and deleted = 0 and merchant_type_code = 'cinema' |
| | | """, merchantId); |
| | | if (merchants.isEmpty()) throw new BusinessException(ErrorCode.NOT_FOUND, "影城不存在"); |
| | | Map<String, Object> merchant = merchants.get(0); |
| | |
| | | detail_config.advisor_department, detail_config.advisor_mobile, detail_config.advisor_service_time, |
| | | detail_config.advisor_available, detail_config.unavailable_reason, detail_config.status as detail_status |
| | | from merchant m |
| | | join premium_topic_merchant mapping on mapping.merchant_id = m.id and mapping.category_key = 'finance' and mapping.deleted = 0 |
| | | left join finance_service_detail detail_config on detail_config.merchant_id = m.id and detail_config.deleted = 0 |
| | | where m.id = ? and m.merchant_type in ('finance', '金融服务') and m.deleted = 0 |
| | | where m.id = ? and m.merchant_type_code = 'finance' and m.deleted = 0 |
| | | order by detail_config.id asc limit 1 |
| | | """, merchantId); |
| | | boolean available = number(row.get("merchantStatus")) == 1 && (row.get("detailStatus") == null || number(row.get("detailStatus")) == 1); |
| | |
| | | |
| | | private Map<String, Object> fuelMerchant(Long merchantId) { |
| | | return one(""" |
| | | select id, merchant_name, merchant_type, contact_mobile, address, cover_url, service_label, status, latitude, longitude |
| | | select id, merchant_name, merchant_type_code as merchant_type, contact_mobile, address, cover_url, service_label, status, latitude, longitude |
| | | from merchant |
| | | where id = ? and merchant_type in ('fuel', '加油服务') and deleted = 0 |
| | | where id = ? and merchant_type_code = 'fuel' and deleted = 0 |
| | | """, merchantId); |
| | | } |
| | | |
| | |
| | | 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 join premium_topic_merchant mapping on mapping.merchant_id = m.id |
| | | where m.id = ? and m.merchant_type in ('medical', '免费体检', 'healthCheck') and m.deleted = 0 |
| | | and mapping.category_key = 'medical' and mapping.deleted = 0 |
| | | 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); |
| | |
| | | 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 |
| | | join premium_topic_merchant mapping on mapping.merchant_id = m.id and mapping.category_key = 'medical' and mapping.deleted = 0 |
| | | 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, "体检套餐不存在"); |
| | |
| | | select id, merchant_name, contact_mobile, address, cover_url, service_label, usage_instructions, |
| | | rights_text, merchant_rights_config, status, latitude, longitude |
| | | from merchant |
| | | where id = ? and merchant_type in ('parking', '停车服务') and deleted = 0 |
| | | where id = ? and merchant_type_code = 'parking' and deleted = 0 |
| | | """, merchantId); |
| | | } |
| | | |
| | |
| | | import java.time.LocalDateTime; |
| | | import java.time.temporal.TemporalAdjusters; |
| | | import java.util.ArrayList; |
| | | import java.util.Collections; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.Collections; |
| | | import java.util.Locale; |
| | | |
| | | import com.talentcard.server.business.DatabaseRows; |
| | | import com.talentcard.server.business.PageResult; |
| | | import com.talentcard.server.business.merchant.MerchantTypeCatalog; |
| | | import com.talentcard.server.business.merchant.MerchantTypeCatalog.MerchantType; |
| | | import com.talentcard.server.common.BusinessException; |
| | | import com.talentcard.server.common.ErrorCode; |
| | | import org.jsoup.Jsoup; |
| | |
| | | limit 8 |
| | | """)); |
| | | result.put("recommendedMerchants", query(""" |
| | | select id, merchant_name, merchant_type, logo_url, cover_url, address |
| | | select id, merchant_name, merchant_type_code as merchant_type, logo_url, cover_url, address |
| | | from merchant |
| | | where deleted = 0 and status = 1 and is_recommend = 1 |
| | | order by sort_no desc, id desc |
| | |
| | | } |
| | | } |
| | | PageResult<Map<String, Object>> result = page(""" |
| | | select id, merchant_name, merchant_type, contact_name, contact_mobile, logo_url, cover_url, address, |
| | | select id, merchant_name, merchant_type_code as merchant_type, contact_name, contact_mobile, logo_url, cover_url, address, |
| | | is_recommend, sort_no, longitude, latitude, business_status, |
| | | (select min(p.sale_price) from merchant_product p where p.merchant_id = merchant.id and p.deleted = 0 and p.status = 1 and p.audit_status = 2) as sale_price, |
| | | (select min(p.original_price) from merchant_product p where p.merchant_id = merchant.id and p.deleted = 0 and p.status = 1 and p.audit_status = 2) as original_price |
| | |
| | | |
| | | @Transactional(readOnly = true) |
| | | public PageResult<Map<String, Object>> foodMerchants(int page, int size, Long talentId) { |
| | | String merchantWhere = " where deleted = 0 and merchant_type in ('food', '餐饮服务')"; |
| | | String merchantWhere = " where deleted = 0 and merchant_type_code = 'food'"; |
| | | PageResult<Map<String, Object>> result = page(""" |
| | | select id, merchant_name, merchant_type, contact_name, contact_mobile, logo_url, cover_url, address, |
| | | select id, merchant_name, merchant_type_code as merchant_type, contact_name, contact_mobile, logo_url, cover_url, address, |
| | | status, sort_no, longitude, latitude, business_status, |
| | | (select min(p.sale_price) from merchant_product p where p.merchant_id = merchant.id and p.deleted = 0 and p.status = 1 and p.audit_status = 2) as sale_price, |
| | | (select min(p.original_price) from merchant_product p where p.merchant_id = merchant.id and p.deleted = 0 and p.status = 1 and p.audit_status = 2) as original_price |
| | |
| | | |
| | | @Transactional(readOnly = true) |
| | | public PageResult<Map<String, Object>> travelMerchants(int page, int size) { |
| | | String merchantWhere = " where deleted = 0 and merchant_type in ('scenic', '景区服务')"; |
| | | String merchantWhere = " where deleted = 0 and merchant_type_code = 'scenic'"; |
| | | PageResult<Map<String, Object>> result = page(""" |
| | | select id, merchant_name, merchant_type, contact_name, contact_mobile, logo_url, cover_url, address, |
| | | select id, merchant_name, merchant_type_code as merchant_type, contact_name, contact_mobile, logo_url, cover_url, address, |
| | | status, sort_no, longitude, latitude, business_status, |
| | | (select min(p.sale_price) from merchant_product p where p.merchant_id = merchant.id and p.deleted = 0 and p.status = 1 and p.audit_status = 2) as sale_price, |
| | | (select min(p.original_price) from merchant_product p where p.merchant_id = merchant.id and p.deleted = 0 and p.status = 1 and p.audit_status = 2) as original_price |
| | |
| | | |
| | | @Transactional(readOnly = true) |
| | | public PageResult<Map<String, Object>> stayMerchants(String type, int page, int size, Long talentId) { |
| | | String requestedType = type == null ? "" : type.trim(); |
| | | String requestedType = MerchantTypeCatalog.canonicalCode(type); |
| | | boolean apartment = switch (requestedType) { |
| | | case "hotel", "酒店服务" -> false; |
| | | case "apartment", "公寓租赁", "公寓租赁服务" -> true; |
| | | case "hotel" -> false; |
| | | case "apartment" -> true; |
| | | default -> throw new BusinessException(ErrorCode.BAD_REQUEST, "不支持的住宿服务类型"); |
| | | }; |
| | | String responseMerchantType = apartment ? "apartment" : "hotel"; |
| | | String merchantWhere = apartment |
| | | ? " where deleted = 0 and merchant_type in ('apartment', '公寓租赁', '公寓租赁服务')" |
| | | : " where deleted = 0 and merchant_type in ('hotel', '酒店服务')"; |
| | | String merchantWhere = " where deleted = 0 and merchant_type_code = '" + responseMerchantType + "'"; |
| | | PageResult<Map<String, Object>> result = page(""" |
| | | select id, merchant_name, merchant_type, contact_name, contact_mobile, logo_url, cover_url, address, |
| | | select id, merchant_name, merchant_type_code as merchant_type, contact_name, contact_mobile, logo_url, cover_url, address, |
| | | status, sort_no, longitude, latitude, business_status, group_buying_enabled, merchant_rights_config, |
| | | (select min(p.sale_price) from merchant_product p where p.merchant_id = merchant.id and p.deleted = 0 and p.status = 1 and p.audit_status = 2) as sale_price, |
| | | (select min(p.original_price) from merchant_product p where p.merchant_id = merchant.id and p.deleted = 0 and p.status = 1 and p.audit_status = 2) as original_price |
| | |
| | | |
| | | @Transactional(readOnly = true) |
| | | public PageResult<Map<String, Object>> entertainmentMerchants(int page, int size) { |
| | | String merchantWhere = " where deleted = 0 and merchant_type in ('cinema', 'theatre', '观影服务', '电影院', '影院', '影城', '剧院')"; |
| | | String merchantWhere = " where deleted = 0 and merchant_type_code = 'cinema'"; |
| | | PageResult<Map<String, Object>> result = page(""" |
| | | select id, merchant_name, merchant_type, contact_name, contact_mobile, logo_url, cover_url, address, |
| | | select id, merchant_name, merchant_type_code as merchant_type, contact_name, contact_mobile, logo_url, cover_url, address, |
| | | service_label, status, sort_no, longitude, latitude, business_status, |
| | | (select min(p.sale_price) from merchant_product p where p.merchant_id = merchant.id and p.deleted = 0 and p.status = 1 and p.audit_status = 2) as sale_price, |
| | | (select min(p.original_price) from merchant_product p where p.merchant_id = merchant.id and p.deleted = 0 and p.status = 1 and p.audit_status = 2) as original_price |
| | |
| | | |
| | | @Transactional(readOnly = true) |
| | | public PageResult<Map<String, Object>> transportMerchants(String type, int page, int size) { |
| | | if (!List.of("fuel", "parking", "carWash").contains(type)) { |
| | | String requestedType = MerchantTypeCatalog.canonicalCode(type); |
| | | if (!List.of("fuel", "parking", "carWash").contains(requestedType)) { |
| | | throw new BusinessException(ErrorCode.BAD_REQUEST, "不支持的行服务类型"); |
| | | } |
| | | String requestedType = type; |
| | | String merchantWhere = switch (type) { |
| | | case "fuel" -> " where deleted = 0 and merchant_type in ('fuel', '加油服务')"; |
| | | case "parking" -> " where deleted = 0 and merchant_type in ('parking', '停车服务')"; |
| | | case "carWash" -> " where deleted = 0 and merchant_type in ('carWash', '自助洗车', '自助洗车服务')"; |
| | | default -> throw new BusinessException(ErrorCode.BAD_REQUEST, "不支持的行服务类型"); |
| | | }; |
| | | String merchantWhere = " where deleted = 0 and merchant_type_code = '" + requestedType + "'"; |
| | | PageResult<Map<String, Object>> result = page(""" |
| | | select id, merchant_name, merchant_type, contact_name, contact_mobile, logo_url, cover_url, address, |
| | | select id, merchant_name, merchant_type_code as merchant_type, contact_name, contact_mobile, logo_url, cover_url, address, |
| | | service_label, status, sort_no, longitude, latitude, business_status, merchant_rights_config, |
| | | (select min(p.sale_price) from merchant_product p where p.merchant_id = merchant.id and p.deleted = 0 and p.status = 1 and p.audit_status = 2) as sale_price, |
| | | (select min(p.original_price) from merchant_product p where p.merchant_id = merchant.id and p.deleted = 0 and p.status = 1 and p.audit_status = 2) as original_price |
| | |
| | | where category_key = ? and enabled = 1 and deleted = 0 |
| | | """, Integer.class, categoryId); |
| | | if (categoryCount == 0) throw new BusinessException(ErrorCode.BAD_REQUEST, "不支持的购物服务分类"); |
| | | String merchantTypeFilter = switch (categoryId) { |
| | | case "housing" -> "('购房优惠')"; |
| | | case "car" -> "('购车服务')"; |
| | | case "appliance" -> "('家电服务')"; |
| | | case "digital" -> "('数码服务')"; |
| | | case "book" -> "('文化服务', '图书服务')"; |
| | | default -> "('')"; |
| | | }; |
| | | List<String> merchantTypeCodes = MerchantTypeCatalog.codesForShoppingCategory(categoryId); |
| | | if (merchantTypeCodes.isEmpty()) throw new BusinessException(ErrorCode.BAD_REQUEST, "该购物分类未配置商家类型"); |
| | | String placeholders = String.join(",", java.util.Collections.nCopies(merchantTypeCodes.size(), "?")); |
| | | String merchantWhere = """ |
| | | where m.deleted = 0 |
| | | and (m.merchant_type in %s or (m.merchant_type = 'shopping' and mapping.deleted = 0 and mapping.category_key = ?)) |
| | | """.formatted(merchantTypeFilter); |
| | | and m.merchant_type_code in (%s) |
| | | """.formatted(placeholders); |
| | | List<Object> args = new ArrayList<>(); |
| | | args.add(categoryId); |
| | | args.addAll(merchantTypeCodes); |
| | | PageResult<Map<String, Object>> result = page(""" |
| | | select m.id, m.merchant_name, m.merchant_type, m.contact_name, m.contact_mobile, m.logo_url, m.cover_url, m.address, |
| | | select m.id, m.merchant_name, m.merchant_type_code as merchant_type, m.contact_name, m.contact_mobile, m.logo_url, m.cover_url, m.address, |
| | | m.service_label, m.status, m.sort_no, m.longitude, m.latitude, m.business_status |
| | | from merchant m |
| | | left join shopping_topic_merchant mapping on mapping.merchant_id = m.id and mapping.deleted = 0 and mapping.category_key = ? |
| | | """ + merchantWhere + " order by mapping.sort_no asc, m.sort_no asc, m.id asc", |
| | | "select count(*) from merchant m left join shopping_topic_merchant mapping on mapping.merchant_id = m.id and mapping.deleted = 0 and mapping.category_key = ? " + merchantWhere, |
| | | page, size, categoryId, categoryId); |
| | | page, size, args.toArray()); |
| | | List<Map<String, Object>> records = result.records().stream().map(row -> { |
| | | Map<String, Object> item = enrichMerchantSearchRow(row); |
| | | item.put("discountText", textOrDefault(item.get("serviceLabel"), "人才专享")); |
| | |
| | | where category_key = ? and enabled = 1 and deleted = 0 |
| | | """, Integer.class, categoryId); |
| | | if (categoryCount == null || categoryCount == 0) throw new BusinessException(ErrorCode.BAD_REQUEST, "不支持的臻享服务分类"); |
| | | String fallbackMerchantType = premiumFallbackMerchantType(categoryId); |
| | | String serviceWhere = """ |
| | | where m.deleted = 0 and ( |
| | | (mapping.deleted = 0 and mapping.category_key = ?) |
| | | or (mapping.id is null and m.merchant_type = ?) |
| | | ) |
| | | """; |
| | | MerchantType merchantType = MerchantTypeCatalog.forPremiumCategory(categoryId) |
| | | .orElseThrow(() -> new BusinessException(ErrorCode.BAD_REQUEST, "该臻享分类未配置商家类型")); |
| | | String serviceWhere = " where m.deleted = 0 and m.merchant_type_code = ?"; |
| | | PageResult<Map<String, Object>> result = page(""" |
| | | select m.id, m.merchant_name, m.merchant_type, m.contact_mobile, m.cover_url, m.address, |
| | | select m.id, m.merchant_name, m.merchant_type_code as merchant_type, m.contact_mobile, m.cover_url, m.address, |
| | | m.service_label, m.status, m.sort_no, m.longitude, m.latitude, |
| | | coalesce(mapping.category_key, case m.merchant_type |
| | | when '免费体检' then 'medical' when '配偶推荐就业' then 'employment' |
| | | when '金融服务' then 'finance' when '行政审批' then 'approval' |
| | | when '学生托付' then 'children' when '子女托管' then 'children' end) as category_key, |
| | | coalesce(mapping.business_type, case m.merchant_type |
| | | when '免费体检' then 'MEDICAL' when '配偶推荐就业' then 'SPOUSE_EMPLOYMENT' |
| | | when '金融服务' then 'FINANCE' when '行政审批' then 'ADMIN_APPROVAL' |
| | | when '学生托付' then 'CHILD_ENTRUSTMENT' when '子女托管' then 'CHILD_ENTRUSTMENT' end) as business_type |
| | | mapping.sort_no as topic_sort_no |
| | | from merchant m |
| | | left join premium_topic_merchant mapping on mapping.merchant_id = m.id and mapping.deleted = 0 |
| | | left join premium_topic_merchant mapping on mapping.merchant_id = m.id and mapping.deleted = 0 and mapping.category_key = ? |
| | | """ + serviceWhere + " order by mapping.sort_no asc, m.sort_no asc, m.id asc", |
| | | "select count(*) from merchant m left join premium_topic_merchant mapping on mapping.merchant_id = m.id and mapping.deleted = 0 " + serviceWhere, |
| | | page, size, categoryId, fallbackMerchantType); |
| | | "select count(*) from merchant m left join premium_topic_merchant mapping on mapping.merchant_id = m.id and mapping.deleted = 0 and mapping.category_key = ? " + serviceWhere, |
| | | page, size, categoryId, merchantType.code()); |
| | | List<Map<String, Object>> records = result.records().stream().map(row -> { |
| | | Map<String, Object> item = enrichMerchantSearchRow(row); |
| | | item.put("businessType", row.get("businessType")); |
| | | item.put("targetAction", row.get("businessType")); |
| | | item.put("businessType", merchantType.premiumBusinessType()); |
| | | item.put("targetAction", merchantType.premiumBusinessType()); |
| | | item.put("benefitText", textOrDefault(row.get("serviceLabel"), "人才专享")); |
| | | item.put("discountText", textOrDefault(row.get("serviceLabel"), "人才专享")); |
| | | return item; |
| | | }).toList(); |
| | | return new PageResult<>(records, result.page(), result.size(), result.total()); |
| | | } |
| | | |
| | | private String premiumFallbackMerchantType(String categoryId) { |
| | | return switch (categoryId) { |
| | | case "medical" -> "免费体检"; |
| | | case "employment" -> "配偶推荐就业"; |
| | | case "finance" -> "金融服务"; |
| | | case "approval" -> "行政审批"; |
| | | case "children" -> "子女托管"; |
| | | default -> ""; |
| | | }; |
| | | } |
| | | |
| | | private Map<String, Object> enrichMerchantSearchRow(Map<String, Object> row) { |
| | |
| | | } |
| | | |
| | | private String categoryName(String merchantType) { |
| | | return switch (merchantType) { |
| | | case "food", "餐饮服务" -> "暖心食享"; |
| | | case "hotel", "apartment" -> "舒心安居"; |
| | | case "cinema", "theatre" -> "怡乐生活"; |
| | | case "scenic", "景区服务" -> "悦览花城"; |
| | | case "fuel", "parking", "carWash" -> "畅行无忧"; |
| | | case "shopping" -> "惠享好物"; |
| | | case "medical", "finance" -> "臻享礼遇"; |
| | | default -> "其他权益"; |
| | | }; |
| | | return MerchantTypeCatalog.find(merchantType).map(MerchantType::topicName).orElse("其他权益"); |
| | | } |
| | | |
| | | private String appendMerchantTypeFilter(String merchantWhere, List<String> merchantTypes, List<Object> args) { |
| | | if (merchantTypes.size() == 1) { |
| | | args.add(merchantTypes.get(0)); |
| | | return merchantWhere + " and merchant_type = ?"; |
| | | return merchantWhere + " and merchant_type_code = ?"; |
| | | } |
| | | args.addAll(merchantTypes); |
| | | String placeholders = String.join(",", Collections.nCopies(merchantTypes.size(), "?")); |
| | | return merchantWhere + " and merchant_type in (" + placeholders + ")"; |
| | | return merchantWhere + " and merchant_type_code in (" + placeholders + ")"; |
| | | } |
| | | |
| | | private List<String> merchantTypesForRequestedValue(String value) { |
| | | List<String> categoryTypes = merchantTypesForCategory(value); |
| | | return categoryTypes.isEmpty() ? List.of(value) : categoryTypes; |
| | | List<String> types = MerchantTypeCatalog.codesForRequestedValue(value); |
| | | return types.isEmpty() ? List.of("other") : types; |
| | | } |
| | | |
| | | private List<String> merchantTypesForCategory(String value) { |
| | | return switch (value) { |
| | | case "暖心食享", "餐饮服务", "food" -> List.of("food", "餐饮服务"); |
| | | case "舒心安居", "hotel" -> List.of("hotel"); |
| | | case "怡乐生活", "cinema", "theatre" -> List.of("cinema", "theatre"); |
| | | case "悦览花城", "scenic", "景区服务" -> List.of("scenic", "景区服务"); |
| | | case "畅行无忧" -> List.of("fuel", "parking", "carWash"); |
| | | case "fuel" -> List.of("fuel"); |
| | | case "parking" -> List.of("parking"); |
| | | case "carWash" -> List.of("carWash"); |
| | | case "惠享好物", "shopping" -> List.of("shopping"); |
| | | case "臻享礼遇" -> List.of("medical", "finance"); |
| | | case "medical" -> List.of("medical"); |
| | | case "finance" -> List.of("finance"); |
| | | default -> List.of(); |
| | | }; |
| | | return MerchantTypeCatalog.codesForRequestedValue(value); |
| | | } |
| | | |
| | | private String detailType(String merchantType) { |
| | | return switch (merchantType) { |
| | | case "cinema" -> "CINEMA"; |
| | | case "theatre" -> "THEATRE"; |
| | | case "shopping" -> "SHOPPING"; |
| | | case "medical" -> "MEDICAL"; |
| | | case "finance" -> "FINANCE"; |
| | | case "carWash" -> "CAR_WASH"; |
| | | case "fuel" -> "FUEL"; |
| | | case "parking" -> "PARKING"; |
| | | default -> "MERCHANT"; |
| | | }; |
| | | return MerchantTypeCatalog.find(merchantType).map(MerchantType::detailType).orElse("MERCHANT"); |
| | | } |
| | | |
| | | private String targetAction(String merchantType) { |
| | |
| | | @Transactional(readOnly = true) |
| | | public Map<String, Object> merchantDetail(Long id, Long talentId) { |
| | | Map<String, Object> merchant = one(""" |
| | | select id, merchant_name, merchant_type, contact_name, contact_mobile, logo_url, cover_url, address, |
| | | select id, merchant_name, merchant_type_code as merchant_type, contact_name, contact_mobile, logo_url, cover_url, address, |
| | | business_status, longitude, latitude, group_buying_enabled, usage_instructions, rights_text, merchant_rights_config, |
| | | service_label, status, is_recommend, sort_no |
| | | from merchant |
| | |
| | | Map<String, Object> row = one(""" |
| | | select m.id, m.merchant_name, m.cover_url, m.service_label, m.address, m.contact_mobile, m.contact_name, m.rights_text, |
| | | m.longitude, m.latitude, m.status as merchant_status, |
| | | coalesce(mapping.category_key, case m.merchant_type |
| | | when '购房优惠' then 'housing' when '购车服务' then 'car' |
| | | when '家电服务' then 'appliance' when '数码服务' then 'digital' |
| | | when '文化服务' then 'book' when '图书服务' then 'book' end) as category_key, |
| | | case m.merchant_type_code |
| | | when 'housing' then 'housing' when 'car' then 'car' |
| | | when 'appliance' then 'appliance' when 'digital' then 'digital' |
| | | when 'culture' then 'book' when 'telecom' then 'digital' end as category_key, |
| | | detail_config.talent_levels, detail_config.benefit_detail, detail_config.advisor_id, |
| | | detail_config.advisor_name, detail_config.advisor_department, detail_config.advisor_mobile, |
| | | detail_config.advisor_service_time, detail_config.advisor_available, |
| | | detail_config.unavailable_reason, detail_config.status as detail_status |
| | | from merchant m |
| | | left join shopping_topic_merchant mapping on mapping.merchant_id = m.id and mapping.deleted = 0 |
| | | left join shopping_service_detail detail_config on detail_config.merchant_id = m.id and detail_config.deleted = 0 |
| | | where m.id = ? and m.merchant_type in ('shopping', '购房优惠', '购车服务', '家电服务', '数码服务', '文化服务', '图书服务') and m.deleted = 0 |
| | | where m.id = ? and m.merchant_type_code in ('shopping', 'housing', 'car', 'appliance', 'digital', 'culture', 'telecom') and m.deleted = 0 |
| | | order by detail_config.id asc limit 1 |
| | | """, merchantId); |
| | | boolean available = number(row.get("merchantStatus")) == 1 && (row.get("detailStatus") == null || number(row.get("detailStatus")) == 1); |
| | |
| | | m.merchant_rights_config |
| | | from merchant m |
| | | left join spouse_employment_benefit b on b.merchant_id = m.id and b.deleted = 0 |
| | | join premium_topic_merchant mapping on mapping.merchant_id = m.id and mapping.category_key = 'employment' and mapping.deleted = 0 |
| | | where m.id = ? and m.deleted = 0 |
| | | where m.id = ? and m.deleted = 0 and m.merchant_type_code = 'employment' |
| | | """, merchantId); |
| | | if (rows.isEmpty()) throw new BusinessException(ErrorCode.NOT_FOUND, "配偶推荐就业权益不存在"); |
| | | return rows.get(0); |
| | |
| | | o.id as target_detail_id, |
| | | o.merchant_id, |
| | | coalesce(nullif(m.merchant_name, ''), '人才权益服务') as merchant_name, |
| | | coalesce(nullif(m.merchant_type, ''), 'service') as merchant_type, |
| | | coalesce(nullif(m.merchant_type_code, ''), 'service') as merchant_type, |
| | | coalesce(nullif(b.benefit_name, ''), o.order_type, '人才权益订单') as detail_name, |
| | | o.created_at as occurred_at, |
| | | case |
| | |
| | | <script> |
| | | select m.id as merchantId, |
| | | m.merchant_name as merchantName, |
| | | case |
| | | when m.merchant_type = 'parking' then 'parking' |
| | | when m.merchant_type = 'medical' then 'medical' |
| | | when m.merchant_type = 'apartment' then 'apartment' |
| | | when exists (select 1 from shopping_topic_merchant s1 where s1.merchant_id = m.id and s1.deleted = 0 and s1.category_key = 'housing') then 'housing' |
| | | when exists (select 1 from shopping_topic_merchant s2 where s2.merchant_id = m.id and s2.deleted = 0 and s2.category_key = 'car') then 'car' |
| | | when exists (select 1 from shopping_topic_merchant s3 where s3.merchant_id = m.id and s3.deleted = 0 and s3.category_key = 'appliance') then 'appliance' |
| | | else m.merchant_type |
| | | end as category, |
| | | m.merchant_type_code as category, |
| | | m.merchant_type as merchantType, |
| | | m.contact_name as contactName, |
| | | m.contact_mobile as contactMobile, |
| | |
| | | m.updated_at as updatedAt |
| | | from merchant m |
| | | where m.deleted = 0 |
| | | and ( |
| | | m.merchant_type in ('parking', 'medical', 'apartment') |
| | | or exists ( |
| | | select 1 |
| | | from shopping_topic_merchant sm |
| | | where sm.merchant_id = m.id |
| | | and sm.deleted = 0 |
| | | and sm.category_key in ('housing', 'car', 'appliance') |
| | | ) |
| | | ) |
| | | and m.merchant_type_code in ('parking', 'medical', 'apartment', 'housing', 'car', 'appliance') |
| | | <if test="category != null and category != ''"> |
| | | <choose> |
| | | <when test="category == 'housing' or category == 'car' or category == 'appliance'"> |
| | | and m.merchant_type = 'shopping' |
| | | and exists ( |
| | | select 1 |
| | | from shopping_topic_merchant sm |
| | | where sm.merchant_id = m.id |
| | | and sm.deleted = 0 |
| | | and sm.category_key = #{category} |
| | | ) |
| | | </when> |
| | | <otherwise> |
| | | and m.merchant_type = #{category} |
| | | </otherwise> |
| | | </choose> |
| | | and m.merchant_type_code = #{category} |
| | | </if> |
| | | <if test="status != null">and m.status = #{status}</if> |
| | | <if test="keyword != null and keyword != ''"> |
| | |
| | | select count(*) |
| | | from merchant m |
| | | where m.deleted = 0 |
| | | and ( |
| | | m.merchant_type in ('parking', 'medical', 'apartment') |
| | | or exists ( |
| | | select 1 |
| | | from shopping_topic_merchant sm |
| | | where sm.merchant_id = m.id |
| | | and sm.deleted = 0 |
| | | and sm.category_key in ('housing', 'car', 'appliance') |
| | | ) |
| | | ) |
| | | and m.merchant_type_code in ('parking', 'medical', 'apartment', 'housing', 'car', 'appliance') |
| | | <if test="category != null and category != ''"> |
| | | <choose> |
| | | <when test="category == 'housing' or category == 'car' or category == 'appliance'"> |
| | | and m.merchant_type = 'shopping' |
| | | and exists ( |
| | | select 1 |
| | | from shopping_topic_merchant sm |
| | | where sm.merchant_id = m.id |
| | | and sm.deleted = 0 |
| | | and sm.category_key = #{category} |
| | | ) |
| | | </when> |
| | | <otherwise> |
| | | and m.merchant_type = #{category} |
| | | </otherwise> |
| | | </choose> |
| | | and m.merchant_type_code = #{category} |
| | | </if> |
| | | <if test="status != null">and m.status = #{status}</if> |
| | | <if test="keyword != null and keyword != ''"> |
| | |
| | | <script> |
| | | select m.id as merchantId, |
| | | m.merchant_name as merchantName, |
| | | case |
| | | when m.merchant_type = 'parking' then 'parking' |
| | | when m.merchant_type = 'medical' then 'medical' |
| | | when m.merchant_type = 'apartment' then 'apartment' |
| | | when exists (select 1 from shopping_topic_merchant s1 where s1.merchant_id = m.id and s1.deleted = 0 and s1.category_key = 'housing') then 'housing' |
| | | when exists (select 1 from shopping_topic_merchant s2 where s2.merchant_id = m.id and s2.deleted = 0 and s2.category_key = 'car') then 'car' |
| | | when exists (select 1 from shopping_topic_merchant s3 where s3.merchant_id = m.id and s3.deleted = 0 and s3.category_key = 'appliance') then 'appliance' |
| | | else m.merchant_type |
| | | end as category, |
| | | m.merchant_type_code as category, |
| | | m.merchant_type as merchantType, |
| | | m.contact_name as contactName, |
| | | m.contact_mobile as contactMobile, |
| | |
| | | from merchant m |
| | | where m.deleted = 0 |
| | | and m.id = #{serviceId} |
| | | and ( |
| | | m.merchant_type in ('parking', 'medical', 'apartment') |
| | | or exists ( |
| | | select 1 |
| | | from shopping_topic_merchant sm |
| | | where sm.merchant_id = m.id |
| | | and sm.deleted = 0 |
| | | and sm.category_key in ('housing', 'car', 'appliance') |
| | | ) |
| | | ) |
| | | and m.merchant_type_code in ('parking', 'medical', 'apartment', 'housing', 'car', 'appliance') |
| | | limit 1 |
| | | </script> |
| | | """) |
| | | Map<String, Object> selectAdminDetail(@Param("serviceId") Long serviceId); |
| | | } |
| | | } |
| | |
| | | from merchant m |
| | | where m.deleted = 0 |
| | | <if test="merchantId != null">and m.id = #{merchantId}</if> |
| | | <if test="merchantType != null and merchantType != ''">and m.merchant_type = #{merchantType}</if> |
| | | <if test="merchantType != null and merchantType != ''">and m.merchant_type_code = #{merchantType}</if> |
| | | <if test="status != null">and m.status = #{status}</if> |
| | | <if test="keyword != null and keyword != ''"> |
| | | and (m.merchant_name like concat('%', #{keyword}, '%') |
| | |
| | | from merchant m |
| | | where m.deleted = 0 |
| | | <if test="merchantId != null">and m.id = #{merchantId}</if> |
| | | <if test="merchantType != null and merchantType != ''">and m.merchant_type = #{merchantType}</if> |
| | | <if test="merchantType != null and merchantType != ''">and m.merchant_type_code = #{merchantType}</if> |
| | | <if test="status != null">and m.status = #{status}</if> |
| | | <if test="keyword != null and keyword != ''"> |
| | | and (m.merchant_name like concat('%', #{keyword}, '%') |
| | |
| | | ) |
| | | where m.deleted = 0 |
| | | and m.status = 1 |
| | | <if test="merchantType != null and merchantType != ''">and m.merchant_type = #{merchantType}</if> |
| | | <if test="merchantType != null and merchantType != ''">and m.merchant_type_code = #{merchantType}</if> |
| | | <if test="keyword != null and keyword != ''"> |
| | | and (m.merchant_name like concat('%', #{keyword}, '%') |
| | | or m.contact_mobile like concat('%', #{keyword}, '%') |
| | |
| | | from merchant m |
| | | where m.deleted = 0 |
| | | and m.status = 1 |
| | | <if test="merchantType != null and merchantType != ''">and m.merchant_type = #{merchantType}</if> |
| | | <if test="merchantType != null and merchantType != ''">and m.merchant_type_code = #{merchantType}</if> |
| | | <if test="keyword != null and keyword != ''"> |
| | | and (m.merchant_name like concat('%', #{keyword}, '%') |
| | | or m.contact_mobile like concat('%', #{keyword}, '%') |
| | |
| | | and o.order_status = 1 |
| | | and o.verify_status = 1 |
| | | <if test="merchantId != null">and o.merchant_id = #{merchantId}</if> |
| | | <if test="merchantType != null and merchantType != ''">and m.merchant_type = #{merchantType}</if> |
| | | <if test="merchantType != null and merchantType != ''">and m.merchant_type_code = #{merchantType}</if> |
| | | <if test="startTime != null">and coalesce(v.verify_time, o.updated_at, o.created_at) >= #{startTime}</if> |
| | | <if test="endTime != null">and coalesce(v.verify_time, o.updated_at, o.created_at) < #{endTime}</if> |
| | | ) base |
| | |
| | | and o.order_status = 1 |
| | | and o.verify_status = 1 |
| | | <if test="merchantId != null">and o.merchant_id = #{merchantId}</if> |
| | | <if test="merchantType != null and merchantType != ''">and m.merchant_type = #{merchantType}</if> |
| | | <if test="merchantType != null and merchantType != ''">and m.merchant_type_code = #{merchantType}</if> |
| | | <if test="startTime != null">and coalesce(v.verify_time, o.updated_at, o.created_at) >= #{startTime}</if> |
| | | <if test="endTime != null">and coalesce(v.verify_time, o.updated_at, o.created_at) < #{endTime}</if> |
| | | group by period_key, o.merchant_id |
| | |
| | | and o.order_status = 1 |
| | | and o.verify_status = 1 |
| | | <if test="merchantId != null">and o.merchant_id = #{merchantId}</if> |
| | | <if test="merchantType != null and merchantType != ''">and m.merchant_type = #{merchantType}</if> |
| | | <if test="merchantType != null and merchantType != ''">and m.merchant_type_code = #{merchantType}</if> |
| | | <if test="startTime != null">and coalesce(v.verify_time, o.updated_at, o.created_at) >= #{startTime}</if> |
| | | <if test="endTime != null">and coalesce(v.verify_time, o.updated_at, o.created_at) < #{endTime}</if> |
| | | ) base |
| | |
| | | where mapping.merchant_id = m.id |
| | | and mapping.category_key = #{categoryKey} |
| | | and mapping.deleted = 0) |
| | | or (#{categoryKey} = 'medical' and m.merchant_type in ('medical', '免费体检', 'healthCheck'))) |
| | | or (#{categoryKey} = 'medical' and m.merchant_type_code = 'medical')) |
| | | limit 1 |
| | | """) |
| | | Map<String, Object> selectPremiumMerchantSnapshot(@Param("merchantId") Long merchantId, @Param("categoryKey") String categoryKey); |
| | |
| | | """) |
| | | Map<String, Object> selectPrimaryCard(@Param("familyCardId") Long familyCardId); |
| | | |
| | | @Select("select count(*) from merchant where id = #{merchantId} and merchant_type = 'carWash' and deleted = 0 and status = 1") |
| | | @Select("select count(*) from merchant where id = #{merchantId} and merchant_type_code = 'carWash' and deleted = 0 and status = 1") |
| | | Long countActiveMerchant(@Param("merchantId") Long merchantId); |
| | | |
| | | @Select(""" |
| | |
| | | |
| | | @Select("select * from transport_car_wash_credential where id = #{id} and deleted = 0 limit 1 for update") |
| | | TransportCarWashCredential selectByIdForUpdate(@Param("id") Long id); |
| | | } |
| | | } |
| | |
| | | from merchant m |
| | | left join transport_car_wash_service s on s.merchant_id = m.id and s.deleted = 0 |
| | | where m.deleted = 0 |
| | | and m.merchant_type = '自助洗车' |
| | | and m.merchant_type_code = 'carWash' |
| | | and (#{keyword} is null or #{keyword} = '' or m.merchant_name like concat('%', #{keyword}, '%') |
| | | or m.address like concat('%', #{keyword}, '%') or m.contact_mobile like concat('%', #{keyword}, '%')) |
| | | and (#{merchantId} is null or m.id = #{merchantId}) |
| | |
| | | select count(*) |
| | | from merchant m |
| | | where m.deleted = 0 |
| | | and m.merchant_type = '自助洗车' |
| | | and m.merchant_type_code = 'carWash' |
| | | and (#{keyword} is null or #{keyword} = '' or m.merchant_name like concat('%', #{keyword}, '%') |
| | | or m.address like concat('%', #{keyword}, '%') or m.contact_mobile like concat('%', #{keyword}, '%')) |
| | | and (#{merchantId} is null or m.id = #{merchantId}) |
| | |
| | | from merchant m |
| | | left join transport_car_wash_service s on s.merchant_id = m.id and s.deleted = 0 |
| | | where m.deleted = 0 |
| | | and m.merchant_type = '自助洗车' |
| | | and m.merchant_type_code = 'carWash' |
| | | and m.id = #{merchantId} |
| | | limit 1 |
| | | """) |
| | |
| | | from merchant m |
| | | left join transport_car_wash_service s on s.merchant_id = m.id and s.deleted = 0 |
| | | where m.deleted = 0 |
| | | and m.merchant_type = '自助洗车' |
| | | and m.merchant_type_code = 'carWash' |
| | | and m.id = #{merchantId} |
| | | limit 1 |
| | | """) |
| | |
| | | @Select(""" |
| | | select count(*) |
| | | from merchant |
| | | where deleted = 0 and merchant_type = '自助洗车' and id = #{merchantId} |
| | | where deleted = 0 and merchant_type_code = 'carWash' and id = #{merchantId} |
| | | """) |
| | | Long countCarWashMerchant(@Param("merchantId") Long merchantId); |
| | | } |
| | | } |
| | |
| | | b.action_mode as actionMode, |
| | | b.status as benefitStatus |
| | | from transport_parking_card_level_right r |
| | | join merchant m on m.id = r.merchant_id and m.deleted = 0 and m.merchant_type in ('parking', '停车服务') |
| | | join merchant m on m.id = r.merchant_id and m.deleted = 0 and m.merchant_type_code = 'parking' |
| | | left join transport_parking_benefit b on b.merchant_id = m.id and b.deleted = 0 |
| | | where r.deleted = 0 |
| | | and (#{merchantId} is null or r.merchant_id = #{merchantId}) |
| | |
| | | @Select(""" |
| | | select count(*) |
| | | from transport_parking_card_level_right r |
| | | join merchant m on m.id = r.merchant_id and m.deleted = 0 and m.merchant_type in ('parking', '停车服务') |
| | | join merchant m on m.id = r.merchant_id and m.deleted = 0 and m.merchant_type_code = 'parking' |
| | | where r.deleted = 0 |
| | | and (#{merchantId} is null or r.merchant_id = #{merchantId}) |
| | | and (#{cardLevel} is null or #{cardLevel} = '' or r.card_level = #{cardLevel}) |
| | |
| | | b.action_mode as actionMode, |
| | | b.status as benefitStatus |
| | | from transport_parking_card_level_right r |
| | | join merchant m on m.id = r.merchant_id and m.deleted = 0 and m.merchant_type in ('parking', '停车服务') |
| | | join merchant m on m.id = r.merchant_id and m.deleted = 0 and m.merchant_type_code = 'parking' |
| | | left join transport_parking_benefit b on b.merchant_id = m.id and b.deleted = 0 |
| | | where r.deleted = 0 |
| | | and r.merchant_id = #{merchantId} |
| | |
| | | b.action_mode as actionMode, |
| | | b.status as benefitStatus |
| | | from transport_parking_card_level_right r |
| | | join merchant m on m.id = r.merchant_id and m.deleted = 0 and m.merchant_type in ('parking', '停车服务') and m.status = 1 |
| | | join merchant m on m.id = r.merchant_id and m.deleted = 0 and m.merchant_type_code = 'parking' and m.status = 1 |
| | | join transport_parking_benefit b on b.merchant_id = m.id and b.deleted = 0 and b.status = 1 |
| | | where r.deleted = 0 |
| | | and r.enabled = 1 |
| | |
| | | order by m.sort_no desc, m.id desc |
| | | """) |
| | | List<Map<String, Object>> selectAvailableForCard(@Param("cardLevel") String cardLevel, @Param("merchantId") Long merchantId); |
| | | } |
| | | } |
| | |
| | | left join transport_parking_benefit b on b.merchant_id = m.id and b.deleted = 0 |
| | | where m.id = #{merchantId} |
| | | and m.deleted = 0 |
| | | and m.merchant_type in ('parking', '停车服务') |
| | | and m.merchant_type_code = 'parking' |
| | | limit 1 |
| | | """) |
| | | Map<String, Object> selectParkingMerchant(@Param("merchantId") Long merchantId); |
| | |
| | | .or() |
| | | .apply("exists (select 1 from benefit_order o " |
| | | + "join merchant m on m.id = o.merchant_id and m.deleted = 0 " |
| | | + "and m.merchant_type in ('hotel', '酒店服务') " |
| | | + "and m.merchant_type_code = 'hotel' " |
| | | + "where o.id = verify_record.order_id and o.deleted = 0)")); |
| | | if (merchantId != null) { |
| | | recordQuery.and(wrapper -> wrapper.eq("merchant_id", merchantId) |
| | |
| | | .eq("verify_status", 1) |
| | | .ge("updated_at", windowStart) |
| | | .apply("exists (select 1 from merchant m where m.id = benefit_order.merchant_id " |
| | | + "and m.deleted = 0 and m.merchant_type in ('hotel', '酒店服务'))") |
| | | + "and m.deleted = 0 and m.merchant_type_code = 'hotel')") |
| | | .apply("not exists (select 1 from verify_record r where r.order_id = benefit_order.id and r.status = 1)"); |
| | | if (merchantId != null) { |
| | | orderQuery.eq("merchant_id", merchantId); |
| | |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; |
| | | import com.talentcard.server.business.PageResult; |
| | | import com.talentcard.server.business.merchant.MerchantTypeCatalog; |
| | | import com.talentcard.server.common.BusinessException; |
| | | import com.talentcard.server.common.ErrorCode; |
| | | import com.talentcard.server.entity.MerchantCardBenefit; |
| | |
| | | List<Map<String, Object>> records = merchantMapper.selectAdminPage( |
| | | merchantId, |
| | | clean(keyword), |
| | | clean(merchantType), |
| | | merchantTypeCode(merchantType), |
| | | aliases, |
| | | status, |
| | | safeSize, |
| | |
| | | List<Map<String, Object>> views = records.stream() |
| | | .map(row -> adminView(row, benefits.getOrDefault(longValue(row, "merchantId"), List.of()))) |
| | | .toList(); |
| | | Long total = merchantMapper.countAdminPage(merchantId, clean(keyword), clean(merchantType), aliases, status); |
| | | Long total = merchantMapper.countAdminPage(merchantId, clean(keyword), merchantTypeCode(merchantType), aliases, status); |
| | | return new PageResult<>(views, safePage, safeSize, total == null ? 0 : total); |
| | | } |
| | | |
| | |
| | | } |
| | | } |
| | | |
| | | private String merchantTypeCode(String value) { |
| | | String cleaned = clean(value); |
| | | return cleaned.isBlank() ? "" : MerchantTypeCatalog.canonicalCode(cleaned); |
| | | } |
| | | |
| | | private void upsertPolicies(Long merchantId, List<MerchantDisplayPolicySaveRequest.CardPolicy> policies) { |
| | | if (policies == null) { |
| | | return; |
| | |
| | | |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.talentcard.server.business.PageResult; |
| | | import com.talentcard.server.business.merchant.MerchantTypeCatalog; |
| | | import com.talentcard.server.common.BusinessException; |
| | | import com.talentcard.server.common.ErrorCode; |
| | | import com.talentcard.server.entity.BenefitFamilyCardApplication; |
| | |
| | | List<String> aliases = cardLevelAliases(card.cardLevel()); |
| | | List<Map<String, Object>> records = merchantMapper.selectPortalPage( |
| | | aliases, |
| | | clean(merchantType), |
| | | merchantTypeCode(merchantType), |
| | | clean(keyword), |
| | | safeSize, |
| | | (safePage - 1) * safeSize |
| | | ).stream().map(row -> portalView(row, card)).toList(); |
| | | Long total = merchantMapper.countPortalPage(aliases, clean(merchantType), clean(keyword)); |
| | | Long total = merchantMapper.countPortalPage(aliases, merchantTypeCode(merchantType), clean(keyword)); |
| | | return new PageResult<>(records, safePage, safeSize, total == null ? 0 : total); |
| | | } |
| | | |
| | |
| | | return portalView(row, card); |
| | | } |
| | | |
| | | private String merchantTypeCode(String value) { |
| | | String cleaned = clean(value); |
| | | return cleaned.isBlank() ? "" : MerchantTypeCatalog.canonicalCode(cleaned); |
| | | } |
| | | |
| | | @Transactional |
| | | public Map<String, Object> recordEvent(Long merchantId, MerchantDisplayPolicyEventRequest request) { |
| | | Long id = requireMerchantId(merchantId); |
| | |
| | | import java.util.Map; |
| | | |
| | | import com.talentcard.server.business.PageResult; |
| | | import com.talentcard.server.business.merchant.MerchantTypeCatalog; |
| | | import com.talentcard.server.common.BusinessException; |
| | | import com.talentcard.server.common.ErrorCode; |
| | | import com.talentcard.server.mapper.MerchantSettlementStatisticsMapper; |
| | |
| | | int safeSize = Math.min(Math.max(size, 1), 100); |
| | | List<Map<String, Object>> records = mapper.selectPeriodSummaries( |
| | | merchantId, |
| | | clean(merchantType), |
| | | merchantTypeCode(merchantType), |
| | | normalizedPeriod, |
| | | range.startTime(), |
| | | range.endTime(), |
| | |
| | | .toList(); |
| | | Long total = mapper.countPeriodSummaries( |
| | | merchantId, |
| | | clean(merchantType), |
| | | merchantTypeCode(merchantType), |
| | | normalizedPeriod, |
| | | range.startTime(), |
| | | range.endTime() |
| | |
| | | public Map<String, Object> overview(Long merchantId, String merchantType, String startDate, String endDate) { |
| | | requireMerchantIdWhenSpecified(merchantId); |
| | | DateRange range = range("DAY", startDate, endDate); |
| | | List<Map<String, Object>> rows = mapper.selectMerchantOverviews(merchantId, clean(merchantType), range.startTime(), range.endTime()); |
| | | List<Map<String, Object>> rows = mapper.selectMerchantOverviews(merchantId, merchantTypeCode(merchantType), range.startTime(), range.endTime()); |
| | | Map<String, Object> result = new LinkedHashMap<>(); |
| | | result.put("startDate", range.startDate() == null ? "" : range.startDate().toString()); |
| | | result.put("endDate", range.endDateInclusive() == null ? "" : range.endDateInclusive().toString()); |
| | |
| | | return overview(merchantId, null, currentMonth.atDay(1).toString(), currentMonth.atEndOfMonth().toString()); |
| | | } |
| | | |
| | | private String merchantTypeCode(String value) { |
| | | String cleaned = clean(value); |
| | | return cleaned.isBlank() ? "" : MerchantTypeCatalog.canonicalCode(cleaned); |
| | | } |
| | | |
| | | @Transactional(readOnly = true) |
| | | public Map<String, Object> dashboardCards(Long merchantId) { |
| | | requireMerchantId(merchantId); |
| New file |
| | |
| | | insert into premium_topic_merchant(merchant_id, category_key, business_type, sort_no, deleted) |
| | | select m.id, |
| | | case m.merchant_type |
| | | when '免费体检' then 'medical' |
| | | when '配偶推荐就业' then 'employment' |
| | | when '金融服务' then 'finance' |
| | | when '行政审批' then 'approval' |
| | | when '学生托付' then 'children' |
| | | when '子女托管' then 'children' |
| | | end, |
| | | case m.merchant_type |
| | | when '免费体检' then 'MEDICAL' |
| | | when '配偶推荐就业' then 'SPOUSE_EMPLOYMENT' |
| | | when '金融服务' then 'FINANCE' |
| | | when '行政审批' then 'ADMIN_APPROVAL' |
| | | when '学生托付' then 'CHILD_ENTRUSTMENT' |
| | | when '子女托管' then 'CHILD_ENTRUSTMENT' |
| | | end, |
| | | m.sort_no, |
| | | 0 |
| | | from merchant m |
| | | where m.deleted = 0 |
| | | and m.merchant_type in ('免费体检', '配偶推荐就业', '金融服务', '行政审批', '学生托付', '子女托管') |
| | | on duplicate key update category_key = values(category_key), |
| | | business_type = values(business_type), |
| | | sort_no = values(sort_no), |
| | | deleted = 0; |
| New file |
| | |
| | | alter table merchant |
| | | add column merchant_type_code varchar(32) not null default 'other' after merchant_type; |
| | | |
| | | update merchant |
| | | set merchant_type_code = case |
| | | when merchant_type in ('food', '餐饮服务', '餐饮', '餐饮商家') then 'food' |
| | | when merchant_type in ('scenic', '景区服务', '景区', '景点服务') then 'scenic' |
| | | when merchant_type in ('hotel', '酒店服务', '酒店', '住宿服务') then 'hotel' |
| | | when merchant_type in ('apartment', '公寓租赁', '公寓租赁服务', '公寓服务') then 'apartment' |
| | | when merchant_type in ('cinema', 'theatre', '观影服务', '电影院', '影院', '影城', '剧院') then 'cinema' |
| | | when merchant_type in ('fuel', '加油服务', '加油', '加油站') then 'fuel' |
| | | when merchant_type in ('parking', '停车服务', '停车', '停车场') then 'parking' |
| | | when merchant_type in ('carWash', 'carwash', '自主洗车', '自助洗车', '自助洗车服务', '洗车服务') then 'carWash' |
| | | when merchant_type in ('medical', '免费体检', 'healthCheck', 'healthcheck', '体检服务', '医疗体检') then 'medical' |
| | | when merchant_type in ('employment', '配偶推荐就业', '就业服务', '配偶就业') then 'employment' |
| | | when merchant_type in ('finance', '金融服务', '金融') then 'finance' |
| | | when merchant_type in ('approval', '行政审批', '行政审批服务', '政务服务') then 'approval' |
| | | when merchant_type in ('children', '学生托付', '学生托管', '子女托管', '子女托管服务') then 'children' |
| | | when merchant_type in ('housing', '购房优惠', '购房服务', '住房服务') then 'housing' |
| | | when merchant_type in ('car', '购车服务', '购车优惠', '汽车服务') then 'car' |
| | | when merchant_type in ('appliance', '家电服务', '家用电器服务') then 'appliance' |
| | | when merchant_type in ('digital', '数码服务', '数码产品服务') then 'digital' |
| | | when merchant_type in ('culture', '文化服务', '图书服务', '文化图书服务') then 'culture' |
| | | when merchant_type in ('telecom', '电信服务', '通信服务') then 'telecom' |
| | | when merchant_type in ('dental', '口腔治疗保健', '口腔服务', '口腔保健') then 'dental' |
| | | when merchant_type in ('shopping', '购物服务', '惠享好物') then 'shopping' |
| | | else 'other' |
| | | end; |
| | | |
| | | -- Early demo data used a generic shopping type and stored the real type only in the |
| | | -- topic mapping. Convert it once so the mapping is no longer required for visibility. |
| | | update merchant m |
| | | set m.merchant_type_code = case ( |
| | | select mapping.category_key |
| | | from shopping_topic_merchant mapping |
| | | where mapping.merchant_id = m.id and mapping.deleted = 0 |
| | | order by mapping.id |
| | | limit 1 |
| | | ) |
| | | when 'housing' then 'housing' |
| | | when 'car' then 'car' |
| | | when 'appliance' then 'appliance' |
| | | when 'digital' then 'digital' |
| | | when 'book' then 'culture' |
| | | else m.merchant_type_code |
| | | end |
| | | where m.merchant_type_code = 'shopping' |
| | | and exists ( |
| | | select 1 from shopping_topic_merchant mapping |
| | | where mapping.merchant_id = m.id and mapping.deleted = 0 |
| | | ); |
| | | |
| | | create index idx_merchant_type_code on merchant(merchant_type_code, deleted, status); |
| | |
| | | } |
| | | |
| | | @Test |
| | | void doesNotSupportMerchantAppLoginForUnconfiguredServiceTypes() { |
| | | assertThat(service.supportsMerchantAppLogin("酒店服务")).isFalse(); |
| | | void supportsMerchantAppLoginForHotelMerchants() { |
| | | assertThat(service.supportsMerchantAppLogin("酒店服务")).isTrue(); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.talentcard.server.business.merchant; |
| | | |
| | | import static org.assertj.core.api.Assertions.assertThat; |
| | | |
| | | import java.util.Map; |
| | | |
| | | import org.junit.jupiter.api.Test; |
| | | |
| | | class MerchantTypeCatalogTests { |
| | | |
| | | @Test |
| | | void resolvesDisplayNamesCodesAndLegacyAliasesToOneCode() { |
| | | assertThat(MerchantTypeCatalog.canonicalCode("免费体检")).isEqualTo("medical"); |
| | | assertThat(MerchantTypeCatalog.canonicalCode("medical")).isEqualTo("medical"); |
| | | assertThat(MerchantTypeCatalog.canonicalCode("healthCheck")).isEqualTo("medical"); |
| | | assertThat(MerchantTypeCatalog.canonicalCode("影院")).isEqualTo("cinema"); |
| | | assertThat(MerchantTypeCatalog.canonicalCode("公寓租赁服务")).isEqualTo("apartment"); |
| | | } |
| | | |
| | | @Test |
| | | void exposesRoutingMetadataFromTheSameCatalog() { |
| | | MerchantTypeCatalog.MerchantType medical = MerchantTypeCatalog.find("免费体检").orElseThrow(); |
| | | assertThat(medical.topicName()).isEqualTo("臻享礼遇"); |
| | | assertThat(medical.detailType()).isEqualTo("MEDICAL"); |
| | | assertThat(medical.premiumCategoryKey()).isEqualTo("medical"); |
| | | assertThat(medical.premiumBusinessType()).isEqualTo("MEDICAL"); |
| | | |
| | | assertThat(MerchantTypeCatalog.codesForShoppingCategory("book")).containsExactly("culture"); |
| | | assertThat(MerchantTypeCatalog.find("酒店服务").orElseThrow().supportsMerchantAppLogin()).isTrue(); |
| | | } |
| | | |
| | | @Test |
| | | void unknownTypesHaveAnExplicitFallbackCode() { |
| | | assertThat(MerchantTypeCatalog.canonicalCode("未知类型")).isEqualTo("other"); |
| | | assertThat(MerchantTypeCatalog.find("未知类型")).isEmpty(); |
| | | } |
| | | |
| | | @Test |
| | | void coversEveryMerchantTypeOfferedByTheAdmin() { |
| | | Map<String, String> adminTypes = Map.ofEntries( |
| | | Map.entry("免费体检", "medical"), |
| | | Map.entry("配偶推荐就业", "employment"), |
| | | Map.entry("金融服务", "finance"), |
| | | Map.entry("行政审批", "approval"), |
| | | Map.entry("子女托管", "children"), |
| | | Map.entry("餐饮服务", "food"), |
| | | Map.entry("酒店服务", "hotel"), |
| | | Map.entry("公寓租赁", "apartment"), |
| | | Map.entry("加油服务", "fuel"), |
| | | Map.entry("停车服务", "parking"), |
| | | Map.entry("自助洗车", "carWash"), |
| | | Map.entry("景区服务", "scenic"), |
| | | Map.entry("购房优惠", "housing"), |
| | | Map.entry("购车服务", "car"), |
| | | Map.entry("家电服务", "appliance"), |
| | | Map.entry("数码服务", "digital"), |
| | | Map.entry("文化服务", "culture"), |
| | | Map.entry("电信服务", "telecom"), |
| | | Map.entry("口腔治疗保健", "dental"), |
| | | Map.entry("观影服务", "cinema") |
| | | ); |
| | | adminTypes.forEach((displayName, code) -> |
| | | assertThat(MerchantTypeCatalog.canonicalCode(displayName)).isEqualTo(code)); |
| | | } |
| | | } |