From bc2efbdb52190fe2e7be9920bbed6902d4ee28aa Mon Sep 17 00:00:00 2001
From: shinnosuke <tantao@1hoper.com>
Date: 星期五, 21 八月 2026 11:53:37 +0800
Subject: [PATCH] fix: centralize merchant type integration

---
 talent-card-server/src/main/java/com/talentcard/server/business/admin/AdminBusinessService.java |   92 +++++++++++++++++++++++++++++++--------------
 1 files changed, 63 insertions(+), 29 deletions(-)

diff --git a/talent-card-server/src/main/java/com/talentcard/server/business/admin/AdminBusinessService.java b/talent-card-server/src/main/java/com/talentcard/server/business/admin/AdminBusinessService.java
index 4f05581..5a3e462 100644
--- a/talent-card-server/src/main/java/com/talentcard/server/business/admin/AdminBusinessService.java
+++ b/talent-card-server/src/main/java/com/talentcard/server/business/admin/AdminBusinessService.java
@@ -17,6 +17,8 @@
 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;
@@ -28,12 +30,6 @@
 
 @Service
 public class AdminBusinessService {
-
-	private static final Set<String> MERCHANT_APP_LOGIN_TYPES = Set.of(
-			"餐饮服务", "景区服务", "免费体检", "配偶推荐就业", "金融服务", "行政审批", "子女托管",
-			"酒店服务", "公寓租赁", "加油服务", "停车服务", "自助洗车", "购房优惠", "购车服务",
-			"家电服务", "数码服务", "文化服务", "电信服务", "口腔治疗保健"
-	);
 
 	private final JdbcTemplate jdbcTemplate;
 
@@ -255,9 +251,9 @@
 			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 = ?");
@@ -362,16 +358,18 @@
 
 	@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(),
@@ -386,11 +384,12 @@
 				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)) {
@@ -412,7 +411,7 @@
 	}
 
 	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, "请输入商家地址");
 		}
 	}
@@ -423,11 +422,13 @@
 	}
 
 	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}$")) {
@@ -476,21 +477,24 @@
 	}
 
 	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(),
@@ -510,8 +514,9 @@
 				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()) {
@@ -549,7 +554,7 @@
 	}
 
 	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',
@@ -560,7 +565,7 @@
 	}
 
 	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(
@@ -572,6 +577,35 @@
 				       '具体适用油品、优惠额度、可用时段和活动规则以加油站说明为准。', 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
@@ -657,9 +691,9 @@
 			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 = ?");

--
Gitblit v1.7.1