| | |
| | | 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 = ?"); |