refactor: Phase 2 - 레거시 서비스 데이터 분리
- legacy_service_data.dart 파일 생성 - 12개의 static Map 데이터 이동 - 파일 크기 940줄에서 575줄로 감소 (38.8% 감소) - 기존 API 호환성 유지 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,382 +1,17 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'url_matcher/models/service_info.dart';
|
||||||
|
import 'url_matcher/data/legacy_service_data.dart';
|
||||||
|
|
||||||
/// 서비스 정보를 담는 데이터 클래스
|
// ServiceInfo를 외부에서 접근 가능하도록 export
|
||||||
class ServiceInfo {
|
export 'url_matcher/models/service_info.dart';
|
||||||
final String serviceId;
|
|
||||||
final String serviceName;
|
|
||||||
final String? serviceUrl;
|
|
||||||
final String? cancellationUrl;
|
|
||||||
final String categoryId;
|
|
||||||
final String categoryNameKr;
|
|
||||||
final String categoryNameEn;
|
|
||||||
|
|
||||||
ServiceInfo({
|
|
||||||
required this.serviceId,
|
|
||||||
required this.serviceName,
|
|
||||||
this.serviceUrl,
|
|
||||||
this.cancellationUrl,
|
|
||||||
required this.categoryId,
|
|
||||||
required this.categoryNameKr,
|
|
||||||
required this.categoryNameEn,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 구독 서비스와 웹사이트 URL 매칭을 처리하는 서비스 클래스
|
/// 구독 서비스와 웹사이트 URL 매칭을 처리하는 서비스 클래스
|
||||||
class SubscriptionUrlMatcher {
|
class SubscriptionUrlMatcher {
|
||||||
static Map<String, dynamic>? _servicesData;
|
static Map<String, dynamic>? _servicesData;
|
||||||
static bool _isInitialized = false;
|
static bool _isInitialized = false;
|
||||||
|
|
||||||
// 레거시 데이터 (JSON 로드 실패시 폴백)
|
|
||||||
// OTT 서비스
|
|
||||||
static final Map<String, String> ottServices = {
|
|
||||||
'netflix': 'https://www.netflix.com',
|
|
||||||
'넷플릭스': 'https://www.netflix.com',
|
|
||||||
'disney+': 'https://www.disneyplus.com',
|
|
||||||
'디즈니플러스': 'https://www.disneyplus.com',
|
|
||||||
'youtube premium': 'https://www.youtube.com/premium',
|
|
||||||
'유튜브 프리미엄': 'https://www.youtube.com/premium',
|
|
||||||
'watcha': 'https://watcha.com',
|
|
||||||
'왓챠': 'https://watcha.com',
|
|
||||||
'wavve': 'https://www.wavve.com',
|
|
||||||
'웨이브': 'https://www.wavve.com',
|
|
||||||
'apple tv+': 'https://tv.apple.com',
|
|
||||||
'애플 티비플러스': 'https://tv.apple.com',
|
|
||||||
'tving': 'https://www.tving.com',
|
|
||||||
'티빙': 'https://www.tving.com',
|
|
||||||
'prime video': 'https://www.primevideo.com',
|
|
||||||
'프라임 비디오': 'https://www.primevideo.com',
|
|
||||||
'amazon prime': 'https://www.amazon.com/prime',
|
|
||||||
'아마존 프라임': 'https://www.amazon.com/prime',
|
|
||||||
'coupang play': 'https://play.coupangplay.com',
|
|
||||||
'쿠팡 플레이': 'https://play.coupangplay.com',
|
|
||||||
'hulu': 'https://www.hulu.com',
|
|
||||||
'훌루': 'https://www.hulu.com',
|
|
||||||
};
|
|
||||||
|
|
||||||
// 음악 서비스
|
|
||||||
static final Map<String, String> musicServices = {
|
|
||||||
'spotify': 'https://www.spotify.com',
|
|
||||||
'스포티파이': 'https://www.spotify.com',
|
|
||||||
'apple music': 'https://music.apple.com',
|
|
||||||
'애플 뮤직': 'https://music.apple.com',
|
|
||||||
'melon': 'https://www.melon.com',
|
|
||||||
'멜론': 'https://www.melon.com',
|
|
||||||
'genie': 'https://www.genie.co.kr',
|
|
||||||
'지니': 'https://www.genie.co.kr',
|
|
||||||
'youtube music': 'https://music.youtube.com',
|
|
||||||
'유튜브 뮤직': 'https://music.youtube.com',
|
|
||||||
'bugs': 'https://music.bugs.co.kr',
|
|
||||||
'벅스': 'https://music.bugs.co.kr',
|
|
||||||
'flo': 'https://www.music-flo.com',
|
|
||||||
'플로': 'https://www.music-flo.com',
|
|
||||||
'vibe': 'https://vibe.naver.com',
|
|
||||||
'바이브': 'https://vibe.naver.com',
|
|
||||||
'tidal': 'https://www.tidal.com',
|
|
||||||
'타이달': 'https://www.tidal.com',
|
|
||||||
};
|
|
||||||
|
|
||||||
// 저장 (클라우드/파일) 서비스
|
|
||||||
static final Map<String, String> storageServices = {
|
|
||||||
'google drive': 'https://www.google.com/drive/',
|
|
||||||
'구글 드라이브': 'https://www.google.com/drive/',
|
|
||||||
'dropbox': 'https://www.dropbox.com',
|
|
||||||
'드롭박스': 'https://www.dropbox.com',
|
|
||||||
'onedrive': 'https://www.onedrive.com',
|
|
||||||
'원드라이브': 'https://www.onedrive.com',
|
|
||||||
'icloud': 'https://www.icloud.com',
|
|
||||||
'아이클라우드': 'https://www.icloud.com',
|
|
||||||
'box': 'https://www.box.com',
|
|
||||||
'박스': 'https://www.box.com',
|
|
||||||
'pcloud': 'https://www.pcloud.com',
|
|
||||||
'mega': 'https://mega.nz',
|
|
||||||
'메가': 'https://mega.nz',
|
|
||||||
'naver mybox': 'https://mybox.naver.com',
|
|
||||||
'네이버 마이박스': 'https://mybox.naver.com',
|
|
||||||
};
|
|
||||||
|
|
||||||
// 통신 · 인터넷 · TV 서비스
|
|
||||||
static final Map<String, String> telecomServices = {
|
|
||||||
'skt': 'https://www.sktelecom.com',
|
|
||||||
'sk텔레콤': 'https://www.sktelecom.com',
|
|
||||||
'kt': 'https://www.kt.com',
|
|
||||||
'lgu+': 'https://www.lguplus.com',
|
|
||||||
'lg유플러스': 'https://www.lguplus.com',
|
|
||||||
'olleh tv': 'https://www.kt.com/olleh_tv',
|
|
||||||
'올레 tv': 'https://www.kt.com/olleh_tv',
|
|
||||||
'b tv': 'https://www.skbroadband.com',
|
|
||||||
'비티비': 'https://www.skbroadband.com',
|
|
||||||
'u+모바일tv': 'https://www.lguplus.com',
|
|
||||||
'유플러스모바일tv': 'https://www.lguplus.com',
|
|
||||||
};
|
|
||||||
|
|
||||||
// 생활/라이프스타일 서비스
|
|
||||||
static final Map<String, String> lifestyleServices = {
|
|
||||||
'네이버 플러스': 'https://plus.naver.com',
|
|
||||||
'naver plus': 'https://plus.naver.com',
|
|
||||||
'카카오 구독': 'https://subscribe.kakao.com',
|
|
||||||
'kakao subscribe': 'https://subscribe.kakao.com',
|
|
||||||
'쿠팡 와우': 'https://www.coupang.com/np/coupangplus',
|
|
||||||
'coupang wow': 'https://www.coupang.com/np/coupangplus',
|
|
||||||
'스타벅스 버디': 'https://www.starbucks.co.kr',
|
|
||||||
'starbucks buddy': 'https://www.starbucks.co.kr',
|
|
||||||
'cu 구독': 'https://cu.bgfretail.com',
|
|
||||||
'gs25 구독': 'https://gs25.gsretail.com',
|
|
||||||
'현대차 구독': 'https://www.hyundai.com/kr/ko/eco/vehicle-subscription',
|
|
||||||
'lg전자 구독': 'https://www.lge.co.kr',
|
|
||||||
'삼성전자 구독': 'https://www.samsung.com/sec',
|
|
||||||
'다이슨 케어': 'https://www.dyson.co.kr',
|
|
||||||
'dyson care': 'https://www.dyson.co.kr',
|
|
||||||
'마켓컬리': 'https://www.kurly.com',
|
|
||||||
'kurly': 'https://www.kurly.com',
|
|
||||||
'헬로네이처': 'https://www.hellonature.com',
|
|
||||||
'hello nature': 'https://www.hellonature.com',
|
|
||||||
'이마트 트레이더스': 'https://www.emarttraders.co.kr',
|
|
||||||
'홈플러스': 'https://www.homeplus.co.kr',
|
|
||||||
'hellofresh': 'https://www.hellofresh.com',
|
|
||||||
'헬로프레시': 'https://www.hellofresh.com',
|
|
||||||
'bespoke post': 'https://www.bespokepost.com',
|
|
||||||
};
|
|
||||||
|
|
||||||
// 쇼핑/이커머스 서비스
|
|
||||||
static final Map<String, String> shoppingServices = {
|
|
||||||
'amazon prime': 'https://www.amazon.com/prime',
|
|
||||||
'아마존 프라임': 'https://www.amazon.com/prime',
|
|
||||||
'walmart+': 'https://www.walmart.com/plus',
|
|
||||||
'월마트플러스': 'https://www.walmart.com/plus',
|
|
||||||
'chewy': 'https://www.chewy.com',
|
|
||||||
'츄이': 'https://www.chewy.com',
|
|
||||||
'dollar shave club': 'https://www.dollarshaveclub.com',
|
|
||||||
'달러셰이브클럽': 'https://www.dollarshaveclub.com',
|
|
||||||
'instacart': 'https://www.instacart.com',
|
|
||||||
'인스타카트': 'https://www.instacart.com',
|
|
||||||
'shipt': 'https://www.shipt.com',
|
|
||||||
'십트': 'https://www.shipt.com',
|
|
||||||
'grove': 'https://grove.co',
|
|
||||||
'그로브': 'https://grove.co',
|
|
||||||
'cratejoy': 'https://www.cratejoy.com',
|
|
||||||
'shopify': 'https://www.shopify.com',
|
|
||||||
'쇼피파이': 'https://www.shopify.com',
|
|
||||||
};
|
|
||||||
|
|
||||||
// AI 서비스
|
|
||||||
static final Map<String, String> aiServices = {
|
|
||||||
'chatgpt': 'https://chat.openai.com',
|
|
||||||
'챗GPT': 'https://chat.openai.com',
|
|
||||||
'openai': 'https://openai.com',
|
|
||||||
'오픈AI': 'https://openai.com',
|
|
||||||
'claude': 'https://claude.ai',
|
|
||||||
'클로드': 'https://claude.ai',
|
|
||||||
'anthropic': 'https://www.anthropic.com',
|
|
||||||
'앤트로픽': 'https://www.anthropic.com',
|
|
||||||
'midjourney': 'https://www.midjourney.com',
|
|
||||||
'미드저니': 'https://www.midjourney.com',
|
|
||||||
'perplexity': 'https://www.perplexity.ai',
|
|
||||||
'퍼플렉시티': 'https://www.perplexity.ai',
|
|
||||||
'copilot': 'https://copilot.microsoft.com',
|
|
||||||
'코파일럿': 'https://copilot.microsoft.com',
|
|
||||||
'gemini': 'https://gemini.google.com',
|
|
||||||
'제미니': 'https://gemini.google.com',
|
|
||||||
'google ai': 'https://ai.google',
|
|
||||||
'구글 AI': 'https://ai.google',
|
|
||||||
'bard': 'https://bard.google.com',
|
|
||||||
'바드': 'https://bard.google.com',
|
|
||||||
'dall-e': 'https://openai.com/dall-e',
|
|
||||||
'달리': 'https://openai.com/dall-e',
|
|
||||||
'stable diffusion': 'https://stability.ai',
|
|
||||||
'스테이블 디퓨전': 'https://stability.ai',
|
|
||||||
};
|
|
||||||
|
|
||||||
// 프로그래밍 / 개발 서비스
|
|
||||||
static final Map<String, String> programmingServices = {
|
|
||||||
'github': 'https://github.com',
|
|
||||||
'깃허브': 'https://github.com',
|
|
||||||
'cursor': 'https://cursor.com',
|
|
||||||
'커서': 'https://cursor.com',
|
|
||||||
'jetbrains': 'https://www.jetbrains.com',
|
|
||||||
'제트브레인스': 'https://www.jetbrains.com',
|
|
||||||
'intellij': 'https://www.jetbrains.com/idea',
|
|
||||||
'인텔리제이': 'https://www.jetbrains.com/idea',
|
|
||||||
'visual studio': 'https://visualstudio.microsoft.com',
|
|
||||||
'비주얼 스튜디오': 'https://visualstudio.microsoft.com',
|
|
||||||
'aws': 'https://aws.amazon.com',
|
|
||||||
'아마존 웹서비스': 'https://aws.amazon.com',
|
|
||||||
'azure': 'https://azure.microsoft.com',
|
|
||||||
'애저': 'https://azure.microsoft.com',
|
|
||||||
'google cloud': 'https://cloud.google.com',
|
|
||||||
'구글 클라우드': 'https://cloud.google.com',
|
|
||||||
'digitalocean': 'https://www.digitalocean.com',
|
|
||||||
'디지털오션': 'https://www.digitalocean.com',
|
|
||||||
'heroku': 'https://www.heroku.com',
|
|
||||||
'헤로쿠': 'https://www.heroku.com',
|
|
||||||
'codecademy': 'https://www.codecademy.com',
|
|
||||||
'코드아카데미': 'https://www.codecademy.com',
|
|
||||||
'udemy': 'https://www.udemy.com',
|
|
||||||
'유데미': 'https://www.udemy.com',
|
|
||||||
'coursera': 'https://www.coursera.org',
|
|
||||||
'코세라': 'https://www.coursera.org',
|
|
||||||
};
|
|
||||||
|
|
||||||
// 오피스 및 협업 툴
|
|
||||||
static final Map<String, String> officeTools = {
|
|
||||||
'microsoft 365': 'https://www.microsoft.com/microsoft-365',
|
|
||||||
'마이크로소프트 365': 'https://www.microsoft.com/microsoft-365',
|
|
||||||
'office 365': 'https://www.microsoft.com/microsoft-365',
|
|
||||||
'오피스 365': 'https://www.microsoft.com/microsoft-365',
|
|
||||||
'google workspace': 'https://workspace.google.com',
|
|
||||||
'구글 워크스페이스': 'https://workspace.google.com',
|
|
||||||
'slack': 'https://slack.com',
|
|
||||||
'슬랙': 'https://slack.com',
|
|
||||||
'notion': 'https://www.notion.so',
|
|
||||||
'노션': 'https://www.notion.so',
|
|
||||||
'trello': 'https://trello.com',
|
|
||||||
'트렐로': 'https://trello.com',
|
|
||||||
'asana': 'https://asana.com',
|
|
||||||
'아사나': 'https://asana.com',
|
|
||||||
'dropbox': 'https://www.dropbox.com',
|
|
||||||
'드롭박스': 'https://www.dropbox.com',
|
|
||||||
'figma': 'https://www.figma.com',
|
|
||||||
'피그마': 'https://www.figma.com',
|
|
||||||
'adobe creative cloud': 'https://www.adobe.com/creativecloud.html',
|
|
||||||
'어도비 크리에이티브 클라우드': 'https://www.adobe.com/creativecloud.html',
|
|
||||||
};
|
|
||||||
|
|
||||||
// 기타 유명 서비스
|
|
||||||
static final Map<String, String> otherServices = {
|
|
||||||
'google one': 'https://one.google.com',
|
|
||||||
'구글 원': 'https://one.google.com',
|
|
||||||
'icloud': 'https://www.icloud.com',
|
|
||||||
'아이클라우드': 'https://www.icloud.com',
|
|
||||||
'nintendo switch online': 'https://www.nintendo.com/switch/online-service',
|
|
||||||
'닌텐도 스위치 온라인': 'https://www.nintendo.com/switch/online-service',
|
|
||||||
'playstation plus': 'https://www.playstation.com/ps-plus',
|
|
||||||
'플레이스테이션 플러스': 'https://www.playstation.com/ps-plus',
|
|
||||||
'xbox game pass': 'https://www.xbox.com/xbox-game-pass',
|
|
||||||
'엑스박스 게임 패스': 'https://www.xbox.com/xbox-game-pass',
|
|
||||||
'ea play': 'https://www.ea.com/ea-play',
|
|
||||||
'EA 플레이': 'https://www.ea.com/ea-play',
|
|
||||||
'ubisoft+': 'https://ubisoft.com/plus',
|
|
||||||
'유비소프트+': 'https://ubisoft.com/plus',
|
|
||||||
'epic games': 'https://www.epicgames.com',
|
|
||||||
'에픽 게임즈': 'https://www.epicgames.com',
|
|
||||||
'steam': 'https://store.steampowered.com',
|
|
||||||
'스팀': 'https://store.steampowered.com',
|
|
||||||
};
|
|
||||||
|
|
||||||
// 해지 안내 페이지 URL 목록 (공식 해지 안내 페이지가 있는 서비스들)
|
|
||||||
static final Map<String, String> cancellationUrls = {
|
|
||||||
// OTT 서비스 해지 안내 페이지
|
|
||||||
'netflix': 'https://help.netflix.com/ko/node/407',
|
|
||||||
'넷플릭스': 'https://help.netflix.com/ko/node/407',
|
|
||||||
'disney+':
|
|
||||||
'https://help.disneyplus.com/csp?id=csp_article_content&sys_kb_id=f0ddbe01db7601105d5e040ad3961979',
|
|
||||||
'디즈니플러스':
|
|
||||||
'https://help.disneyplus.com/csp?id=csp_article_content&sys_kb_id=f0ddbe01db7601105d5e040ad3961979',
|
|
||||||
'youtube premium': 'https://support.google.com/youtube/answer/6308278',
|
|
||||||
'유튜브 프리미엄': 'https://support.google.com/youtube/answer/6308278',
|
|
||||||
'watcha': 'https://watcha.com/settings/payment',
|
|
||||||
'왓챠': 'https://watcha.com/settings/payment',
|
|
||||||
'wavve': 'https://www.wavve.com/my',
|
|
||||||
'웨이브': 'https://www.wavve.com/my',
|
|
||||||
'apple tv+': 'https://support.apple.com/ko-kr/HT202039',
|
|
||||||
'애플 티비플러스': 'https://support.apple.com/ko-kr/HT202039',
|
|
||||||
'tving': 'https://www.tving.com/my/cancelMembership',
|
|
||||||
'티빙': 'https://www.tving.com/my/cancelMembership',
|
|
||||||
'amazon prime': 'https://www.amazon.com/gp/primecentral/managemembership',
|
|
||||||
'아마존 프라임': 'https://www.amazon.com/gp/primecentral/managemembership',
|
|
||||||
|
|
||||||
// 음악 서비스 해지 안내 페이지
|
|
||||||
'spotify': 'https://support.spotify.com/us/article/cancel-premium/',
|
|
||||||
'스포티파이': 'https://support.spotify.com/us/article/cancel-premium/',
|
|
||||||
'apple music': 'https://support.apple.com/ko-kr/HT202039',
|
|
||||||
'애플 뮤직': 'https://support.apple.com/ko-kr/HT202039',
|
|
||||||
'melon':
|
|
||||||
'https://faqs2.melon.com/customer/faq/informFaq.htm?no=17&faqId=QUES20150209000002&orderChk=date&SEARCH_KEY=&SEARCH_PAR_CATEGORY=CATE20130909000006&SEARCH_CATEGORY=CATE20130909000021',
|
|
||||||
'멜론':
|
|
||||||
'https://faqs2.melon.com/customer/faq/informFaq.htm?no=17&faqId=QUES20150209000002&orderChk=date&SEARCH_KEY=&SEARCH_PAR_CATEGORY=CATE20130909000006&SEARCH_CATEGORY=CATE20130909000021',
|
|
||||||
'youtube music': 'https://support.google.com/youtubemusic/answer/6308278',
|
|
||||||
'유튜브 뮤직': 'https://support.google.com/youtubemusic/answer/6308278',
|
|
||||||
|
|
||||||
// AI 서비스 해지 안내 페이지
|
|
||||||
'chatgpt':
|
|
||||||
'https://help.openai.com/en/articles/7730211-manage-or-cancel-your-chatgpt-plus-subscription',
|
|
||||||
'챗GPT':
|
|
||||||
'https://help.openai.com/en/articles/7730211-manage-or-cancel-your-chatgpt-plus-subscription',
|
|
||||||
'claude':
|
|
||||||
'https://help.anthropic.com/en/articles/8798313-how-do-i-cancel-my-claude-subscription',
|
|
||||||
'클로드':
|
|
||||||
'https://help.anthropic.com/en/articles/8798313-how-do-i-cancel-my-claude-subscription',
|
|
||||||
'midjourney': 'https://docs.midjourney.com/docs/manage-subscription',
|
|
||||||
'미드저니': 'https://docs.midjourney.com/docs/manage-subscription',
|
|
||||||
|
|
||||||
// 프로그래밍 / 개발 서비스 해지 안내 페이지
|
|
||||||
'github':
|
|
||||||
'https://docs.github.com/ko/billing/managing-billing-for-your-github-account/downgrading-your-github-subscription',
|
|
||||||
'깃허브':
|
|
||||||
'https://docs.github.com/ko/billing/managing-billing-for-your-github-account/downgrading-your-github-subscription',
|
|
||||||
'jetbrains':
|
|
||||||
'https://sales.jetbrains.com/hc/en-gb/articles/207240845-How-to-cancel-an-auto-renewal-subscription-',
|
|
||||||
'제트브레인스':
|
|
||||||
'https://sales.jetbrains.com/hc/en-gb/articles/207240845-How-to-cancel-an-auto-renewal-subscription-',
|
|
||||||
|
|
||||||
// 오피스 및 협업 툴 해지 안내 페이지
|
|
||||||
'microsoft 365':
|
|
||||||
'https://support.microsoft.com/en-us/office/cancel-a-microsoft-365-subscription-46e2634c-c64b-4c65-94b9-2cc9c960e91b',
|
|
||||||
'마이크로소프트 365':
|
|
||||||
'https://support.microsoft.com/en-us/office/cancel-a-microsoft-365-subscription-46e2634c-c64b-4c65-94b9-2cc9c960e91b',
|
|
||||||
'office 365':
|
|
||||||
'https://support.microsoft.com/en-us/office/cancel-a-microsoft-365-subscription-46e2634c-c64b-4c65-94b9-2cc9c960e91b',
|
|
||||||
'오피스 365':
|
|
||||||
'https://support.microsoft.com/en-us/office/cancel-a-microsoft-365-subscription-46e2634c-c64b-4c65-94b9-2cc9c960e91b',
|
|
||||||
'slack':
|
|
||||||
'https://slack.com/help/articles/360003378691-Cancel-your-Slack-subscription',
|
|
||||||
'슬랙':
|
|
||||||
'https://slack.com/help/articles/360003378691-Cancel-your-Slack-subscription',
|
|
||||||
'notion':
|
|
||||||
'https://www.notion.so/help/billing-and-payment-settings#cancel-a-subscription',
|
|
||||||
'노션':
|
|
||||||
'https://www.notion.so/help/billing-and-payment-settings#cancel-a-subscription',
|
|
||||||
'dropbox': 'https://help.dropbox.com/accounts-billing/cancellation',
|
|
||||||
'드롭박스': 'https://help.dropbox.com/accounts-billing/cancellation',
|
|
||||||
'adobe creative cloud':
|
|
||||||
'https://helpx.adobe.com/manage-account/using/cancel-subscription.html',
|
|
||||||
'어도비 크리에이티브 클라우드':
|
|
||||||
'https://helpx.adobe.com/manage-account/using/cancel-subscription.html',
|
|
||||||
|
|
||||||
// 기타 유명 서비스 해지 안내 페이지
|
|
||||||
'google one': 'https://support.google.com/googleone/answer/9140429',
|
|
||||||
'구글 원': 'https://support.google.com/googleone/answer/9140429',
|
|
||||||
'icloud': 'https://support.apple.com/ko-kr/HT207594',
|
|
||||||
'아이클라우드': 'https://support.apple.com/ko-kr/HT207594',
|
|
||||||
'nintendo switch online':
|
|
||||||
'https://en-americas-support.nintendo.com/app/answers/detail/a_id/41925/~/how-to-cancel-a-nintendo-switch-online-membership',
|
|
||||||
'닌텐도 스위치 온라인':
|
|
||||||
'https://en-americas-support.nintendo.com/app/answers/detail/a_id/41925/~/how-to-cancel-a-nintendo-switch-online-membership',
|
|
||||||
'playstation plus':
|
|
||||||
'https://www.playstation.com/support/subscriptions/cancel-playstation-plus/',
|
|
||||||
'플레이스테이션 플러스':
|
|
||||||
'https://www.playstation.com/support/subscriptions/cancel-playstation-plus/',
|
|
||||||
'xbox game pass':
|
|
||||||
'https://support.xbox.com/help/subscriptions-billing/manage-subscriptions/xbox-game-pass-how-to-cancel',
|
|
||||||
'엑스박스 게임 패스':
|
|
||||||
'https://support.xbox.com/help/subscriptions-billing/manage-subscriptions/xbox-game-pass-how-to-cancel',
|
|
||||||
};
|
|
||||||
|
|
||||||
// 모든 서비스 매핑을 합친 맵
|
|
||||||
static final Map<String, String> allServices = {
|
|
||||||
...ottServices,
|
|
||||||
...musicServices,
|
|
||||||
...storageServices,
|
|
||||||
...aiServices,
|
|
||||||
...programmingServices,
|
|
||||||
...officeTools,
|
|
||||||
...lifestyleServices,
|
|
||||||
...shoppingServices,
|
|
||||||
...telecomServices,
|
|
||||||
...otherServices,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// JSON 데이터 초기화
|
/// JSON 데이터 초기화
|
||||||
static Future<void> initialize() async {
|
static Future<void> initialize() async {
|
||||||
if (_isInitialized) return;
|
if (_isInitialized) return;
|
||||||
@@ -466,7 +101,7 @@ class SubscriptionUrlMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// JSON에서 못 찾았으면 레거시 방식으로 찾기
|
// JSON에서 못 찾았으면 레거시 방식으로 찾기
|
||||||
for (final entry in allServices.entries) {
|
for (final entry in LegacyServiceData.allServices.entries) {
|
||||||
final serviceUrl = entry.value;
|
final serviceUrl = entry.value;
|
||||||
final serviceDomain = extractDomain(serviceUrl);
|
final serviceDomain = extractDomain(serviceUrl);
|
||||||
|
|
||||||
@@ -499,7 +134,7 @@ class SubscriptionUrlMatcher {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
// 정확한 매칭을 먼저 시도
|
// 정확한 매칭을 먼저 시도
|
||||||
for (final entry in allServices.entries) {
|
for (final entry in LegacyServiceData.allServices.entries) {
|
||||||
if (lowerName.contains(entry.key.toLowerCase())) {
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
||||||
print('SubscriptionUrlMatcher: 정확한 매칭 - $lowerName -> ${entry.key}');
|
print('SubscriptionUrlMatcher: 정확한 매칭 - $lowerName -> ${entry.key}');
|
||||||
return entry.value;
|
return entry.value;
|
||||||
@@ -507,7 +142,7 @@ class SubscriptionUrlMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// OTT 서비스 검사
|
// OTT 서비스 검사
|
||||||
for (final entry in ottServices.entries) {
|
for (final entry in LegacyServiceData.ottServices.entries) {
|
||||||
if (lowerName.contains(entry.key.toLowerCase())) {
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
||||||
print(
|
print(
|
||||||
'SubscriptionUrlMatcher: OTT 서비스 매칭 - $lowerName -> ${entry.key}');
|
'SubscriptionUrlMatcher: OTT 서비스 매칭 - $lowerName -> ${entry.key}');
|
||||||
@@ -516,7 +151,7 @@ class SubscriptionUrlMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 음악 서비스 검사
|
// 음악 서비스 검사
|
||||||
for (final entry in musicServices.entries) {
|
for (final entry in LegacyServiceData.musicServices.entries) {
|
||||||
if (lowerName.contains(entry.key.toLowerCase())) {
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
||||||
print(
|
print(
|
||||||
'SubscriptionUrlMatcher: 음악 서비스 매칭 - $lowerName -> ${entry.key}');
|
'SubscriptionUrlMatcher: 음악 서비스 매칭 - $lowerName -> ${entry.key}');
|
||||||
@@ -525,7 +160,7 @@ class SubscriptionUrlMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AI 서비스 검사
|
// AI 서비스 검사
|
||||||
for (final entry in aiServices.entries) {
|
for (final entry in LegacyServiceData.aiServices.entries) {
|
||||||
if (lowerName.contains(entry.key.toLowerCase())) {
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
||||||
print(
|
print(
|
||||||
'SubscriptionUrlMatcher: AI 서비스 매칭 - $lowerName -> ${entry.key}');
|
'SubscriptionUrlMatcher: AI 서비스 매칭 - $lowerName -> ${entry.key}');
|
||||||
@@ -534,7 +169,7 @@ class SubscriptionUrlMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 개발 서비스 검사
|
// 개발 서비스 검사
|
||||||
for (final entry in programmingServices.entries) {
|
for (final entry in LegacyServiceData.programmingServices.entries) {
|
||||||
if (lowerName.contains(entry.key.toLowerCase())) {
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
||||||
print(
|
print(
|
||||||
'SubscriptionUrlMatcher: 개발 서비스 매칭 - $lowerName -> ${entry.key}');
|
'SubscriptionUrlMatcher: 개발 서비스 매칭 - $lowerName -> ${entry.key}');
|
||||||
@@ -543,7 +178,7 @@ class SubscriptionUrlMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 오피스 툴 검사
|
// 오피스 툴 검사
|
||||||
for (final entry in officeTools.entries) {
|
for (final entry in LegacyServiceData.officeTools.entries) {
|
||||||
if (lowerName.contains(entry.key.toLowerCase())) {
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
||||||
print(
|
print(
|
||||||
'SubscriptionUrlMatcher: 오피스 툴 매칭 - $lowerName -> ${entry.key}');
|
'SubscriptionUrlMatcher: 오피스 툴 매칭 - $lowerName -> ${entry.key}');
|
||||||
@@ -552,7 +187,7 @@ class SubscriptionUrlMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 기타 서비스 검사
|
// 기타 서비스 검사
|
||||||
for (final entry in otherServices.entries) {
|
for (final entry in LegacyServiceData.otherServices.entries) {
|
||||||
if (lowerName.contains(entry.key.toLowerCase())) {
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
||||||
print(
|
print(
|
||||||
'SubscriptionUrlMatcher: 기타 서비스 매칭 - $lowerName -> ${entry.key}');
|
'SubscriptionUrlMatcher: 기타 서비스 매칭 - $lowerName -> ${entry.key}');
|
||||||
@@ -561,7 +196,7 @@ class SubscriptionUrlMatcher {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 유사한 이름 검사 (퍼지 매칭) - 단어 기반으로 검색
|
// 유사한 이름 검사 (퍼지 매칭) - 단어 기반으로 검색
|
||||||
for (final entry in allServices.entries) {
|
for (final entry in LegacyServiceData.allServices.entries) {
|
||||||
final serviceWords = lowerName.split(' ');
|
final serviceWords = lowerName.split(' ');
|
||||||
final keyWords = entry.key.toLowerCase().split(' ');
|
final keyWords = entry.key.toLowerCase().split(' ');
|
||||||
|
|
||||||
@@ -671,12 +306,12 @@ class SubscriptionUrlMatcher {
|
|||||||
final String lowerText = serviceNameOrUrl.toLowerCase().trim();
|
final String lowerText = serviceNameOrUrl.toLowerCase().trim();
|
||||||
|
|
||||||
// 직접 서비스명으로 찾기
|
// 직접 서비스명으로 찾기
|
||||||
if (cancellationUrls.containsKey(lowerText)) {
|
if (LegacyServiceData.cancellationUrls.containsKey(lowerText)) {
|
||||||
return cancellationUrls[lowerText];
|
return LegacyServiceData.cancellationUrls[lowerText];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 서비스명에 부분 포함으로 찾기
|
// 서비스명에 부분 포함으로 찾기
|
||||||
for (var entry in cancellationUrls.entries) {
|
for (var entry in LegacyServiceData.cancellationUrls.entries) {
|
||||||
final String key = entry.key.toLowerCase();
|
final String key = entry.key.toLowerCase();
|
||||||
if (lowerText.contains(key) || key.contains(lowerText)) {
|
if (lowerText.contains(key) || key.contains(lowerText)) {
|
||||||
return entry.value;
|
return entry.value;
|
||||||
@@ -693,7 +328,7 @@ class SubscriptionUrlMatcher {
|
|||||||
final domain = match.group(1)?.toLowerCase() ?? '';
|
final domain = match.group(1)?.toLowerCase() ?? '';
|
||||||
|
|
||||||
// 도메인으로 서비스명 찾기
|
// 도메인으로 서비스명 찾기
|
||||||
for (var entry in cancellationUrls.entries) {
|
for (var entry in LegacyServiceData.cancellationUrls.entries) {
|
||||||
if (entry.key.toLowerCase().contains(domain)) {
|
if (entry.key.toLowerCase().contains(domain)) {
|
||||||
return entry.value;
|
return entry.value;
|
||||||
}
|
}
|
||||||
@@ -835,15 +470,15 @@ class SubscriptionUrlMatcher {
|
|||||||
static String _getCategoryForLegacyService(String serviceName) {
|
static String _getCategoryForLegacyService(String serviceName) {
|
||||||
final lowerName = serviceName.toLowerCase();
|
final lowerName = serviceName.toLowerCase();
|
||||||
|
|
||||||
if (ottServices.containsKey(lowerName)) return 'ott_services';
|
if (LegacyServiceData.ottServices.containsKey(lowerName)) return 'ott_services';
|
||||||
if (musicServices.containsKey(lowerName)) return 'music_streaming';
|
if (LegacyServiceData.musicServices.containsKey(lowerName)) return 'music_streaming';
|
||||||
if (storageServices.containsKey(lowerName)) return 'cloud_storage';
|
if (LegacyServiceData.storageServices.containsKey(lowerName)) return 'cloud_storage';
|
||||||
if (aiServices.containsKey(lowerName)) return 'ai_services';
|
if (LegacyServiceData.aiServices.containsKey(lowerName)) return 'ai_services';
|
||||||
if (programmingServices.containsKey(lowerName)) return 'dev_tools';
|
if (LegacyServiceData.programmingServices.containsKey(lowerName)) return 'dev_tools';
|
||||||
if (officeTools.containsKey(lowerName)) return 'office_tools';
|
if (LegacyServiceData.officeTools.containsKey(lowerName)) return 'office_tools';
|
||||||
if (lifestyleServices.containsKey(lowerName)) return 'lifestyle';
|
if (LegacyServiceData.lifestyleServices.containsKey(lowerName)) return 'lifestyle';
|
||||||
if (shoppingServices.containsKey(lowerName)) return 'shopping';
|
if (LegacyServiceData.shoppingServices.containsKey(lowerName)) return 'shopping';
|
||||||
if (telecomServices.containsKey(lowerName)) return 'telecom';
|
if (LegacyServiceData.telecomServices.containsKey(lowerName)) return 'telecom';
|
||||||
|
|
||||||
return 'other';
|
return 'other';
|
||||||
}
|
}
|
||||||
@@ -874,7 +509,7 @@ class SubscriptionUrlMatcher {
|
|||||||
final lowerSms = smsText.toLowerCase();
|
final lowerSms = smsText.toLowerCase();
|
||||||
|
|
||||||
// 모든 서비스명 검사
|
// 모든 서비스명 검사
|
||||||
for (final entry in allServices.entries) {
|
for (final entry in LegacyServiceData.allServices.entries) {
|
||||||
if (lowerSms.contains(entry.key.toLowerCase())) {
|
if (lowerSms.contains(entry.key.toLowerCase())) {
|
||||||
final categoryId = await findCategoryByServiceName(entry.key) ?? 'other';
|
final categoryId = await findCategoryByServiceName(entry.key) ?? 'other';
|
||||||
|
|
||||||
@@ -910,8 +545,8 @@ class SubscriptionUrlMatcher {
|
|||||||
final String lowerText = text.toLowerCase().trim();
|
final String lowerText = text.toLowerCase().trim();
|
||||||
|
|
||||||
// 정확히 일치하는 경우
|
// 정확히 일치하는 경우
|
||||||
if (allServices.containsKey(lowerText)) {
|
if (LegacyServiceData.allServices.containsKey(lowerText)) {
|
||||||
return allServices[lowerText];
|
return LegacyServiceData.allServices[lowerText];
|
||||||
}
|
}
|
||||||
|
|
||||||
// 부분 일치 검색이 활성화된 경우
|
// 부분 일치 검색이 활성화된 경우
|
||||||
@@ -920,7 +555,7 @@ class SubscriptionUrlMatcher {
|
|||||||
String? bestMatch;
|
String? bestMatch;
|
||||||
int maxLength = 0;
|
int maxLength = 0;
|
||||||
|
|
||||||
for (var entry in allServices.entries) {
|
for (var entry in LegacyServiceData.allServices.entries) {
|
||||||
final String key = entry.key;
|
final String key = entry.key;
|
||||||
|
|
||||||
// 입력된 텍스트에 서비스 키워드가 포함되어 있거나, 서비스 키워드에 입력된 텍스트가 포함된 경우
|
// 입력된 텍스트에 서비스 키워드가 포함되어 있거나, 서비스 키워드에 입력된 텍스트가 포함된 경우
|
||||||
|
|||||||
941
lib/services/subscription_url_matcher.dart.backup
Normal file
941
lib/services/subscription_url_matcher.dart.backup
Normal file
@@ -0,0 +1,941 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
/// 서비스 정보를 담는 데이터 클래스
|
||||||
|
class ServiceInfo {
|
||||||
|
final String serviceId;
|
||||||
|
final String serviceName;
|
||||||
|
final String? serviceUrl;
|
||||||
|
final String? cancellationUrl;
|
||||||
|
final String categoryId;
|
||||||
|
final String categoryNameKr;
|
||||||
|
final String categoryNameEn;
|
||||||
|
|
||||||
|
ServiceInfo({
|
||||||
|
required this.serviceId,
|
||||||
|
required this.serviceName,
|
||||||
|
this.serviceUrl,
|
||||||
|
this.cancellationUrl,
|
||||||
|
required this.categoryId,
|
||||||
|
required this.categoryNameKr,
|
||||||
|
required this.categoryNameEn,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 구독 서비스와 웹사이트 URL 매칭을 처리하는 서비스 클래스
|
||||||
|
class SubscriptionUrlMatcher {
|
||||||
|
static Map<String, dynamic>? _servicesData;
|
||||||
|
static bool _isInitialized = false;
|
||||||
|
|
||||||
|
// 레거시 데이터 (JSON 로드 실패시 폴백)
|
||||||
|
// OTT 서비스
|
||||||
|
static final Map<String, String> ottServices = {
|
||||||
|
'netflix': 'https://www.netflix.com',
|
||||||
|
'넷플릭스': 'https://www.netflix.com',
|
||||||
|
'disney+': 'https://www.disneyplus.com',
|
||||||
|
'디즈니플러스': 'https://www.disneyplus.com',
|
||||||
|
'youtube premium': 'https://www.youtube.com/premium',
|
||||||
|
'유튜브 프리미엄': 'https://www.youtube.com/premium',
|
||||||
|
'watcha': 'https://watcha.com',
|
||||||
|
'왓챠': 'https://watcha.com',
|
||||||
|
'wavve': 'https://www.wavve.com',
|
||||||
|
'웨이브': 'https://www.wavve.com',
|
||||||
|
'apple tv+': 'https://tv.apple.com',
|
||||||
|
'애플 티비플러스': 'https://tv.apple.com',
|
||||||
|
'tving': 'https://www.tving.com',
|
||||||
|
'티빙': 'https://www.tving.com',
|
||||||
|
'prime video': 'https://www.primevideo.com',
|
||||||
|
'프라임 비디오': 'https://www.primevideo.com',
|
||||||
|
'amazon prime': 'https://www.amazon.com/prime',
|
||||||
|
'아마존 프라임': 'https://www.amazon.com/prime',
|
||||||
|
'coupang play': 'https://play.coupangplay.com',
|
||||||
|
'쿠팡 플레이': 'https://play.coupangplay.com',
|
||||||
|
'hulu': 'https://www.hulu.com',
|
||||||
|
'훌루': 'https://www.hulu.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 음악 서비스
|
||||||
|
static final Map<String, String> musicServices = {
|
||||||
|
'spotify': 'https://www.spotify.com',
|
||||||
|
'스포티파이': 'https://www.spotify.com',
|
||||||
|
'apple music': 'https://music.apple.com',
|
||||||
|
'애플 뮤직': 'https://music.apple.com',
|
||||||
|
'melon': 'https://www.melon.com',
|
||||||
|
'멜론': 'https://www.melon.com',
|
||||||
|
'genie': 'https://www.genie.co.kr',
|
||||||
|
'지니': 'https://www.genie.co.kr',
|
||||||
|
'youtube music': 'https://music.youtube.com',
|
||||||
|
'유튜브 뮤직': 'https://music.youtube.com',
|
||||||
|
'bugs': 'https://music.bugs.co.kr',
|
||||||
|
'벅스': 'https://music.bugs.co.kr',
|
||||||
|
'flo': 'https://www.music-flo.com',
|
||||||
|
'플로': 'https://www.music-flo.com',
|
||||||
|
'vibe': 'https://vibe.naver.com',
|
||||||
|
'바이브': 'https://vibe.naver.com',
|
||||||
|
'tidal': 'https://www.tidal.com',
|
||||||
|
'타이달': 'https://www.tidal.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 저장 (클라우드/파일) 서비스
|
||||||
|
static final Map<String, String> storageServices = {
|
||||||
|
'google drive': 'https://www.google.com/drive/',
|
||||||
|
'구글 드라이브': 'https://www.google.com/drive/',
|
||||||
|
'dropbox': 'https://www.dropbox.com',
|
||||||
|
'드롭박스': 'https://www.dropbox.com',
|
||||||
|
'onedrive': 'https://www.onedrive.com',
|
||||||
|
'원드라이브': 'https://www.onedrive.com',
|
||||||
|
'icloud': 'https://www.icloud.com',
|
||||||
|
'아이클라우드': 'https://www.icloud.com',
|
||||||
|
'box': 'https://www.box.com',
|
||||||
|
'박스': 'https://www.box.com',
|
||||||
|
'pcloud': 'https://www.pcloud.com',
|
||||||
|
'mega': 'https://mega.nz',
|
||||||
|
'메가': 'https://mega.nz',
|
||||||
|
'naver mybox': 'https://mybox.naver.com',
|
||||||
|
'네이버 마이박스': 'https://mybox.naver.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 통신 · 인터넷 · TV 서비스
|
||||||
|
static final Map<String, String> telecomServices = {
|
||||||
|
'skt': 'https://www.sktelecom.com',
|
||||||
|
'sk텔레콤': 'https://www.sktelecom.com',
|
||||||
|
'kt': 'https://www.kt.com',
|
||||||
|
'lgu+': 'https://www.lguplus.com',
|
||||||
|
'lg유플러스': 'https://www.lguplus.com',
|
||||||
|
'olleh tv': 'https://www.kt.com/olleh_tv',
|
||||||
|
'올레 tv': 'https://www.kt.com/olleh_tv',
|
||||||
|
'b tv': 'https://www.skbroadband.com',
|
||||||
|
'비티비': 'https://www.skbroadband.com',
|
||||||
|
'u+모바일tv': 'https://www.lguplus.com',
|
||||||
|
'유플러스모바일tv': 'https://www.lguplus.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 생활/라이프스타일 서비스
|
||||||
|
static final Map<String, String> lifestyleServices = {
|
||||||
|
'네이버 플러스': 'https://plus.naver.com',
|
||||||
|
'naver plus': 'https://plus.naver.com',
|
||||||
|
'카카오 구독': 'https://subscribe.kakao.com',
|
||||||
|
'kakao subscribe': 'https://subscribe.kakao.com',
|
||||||
|
'쿠팡 와우': 'https://www.coupang.com/np/coupangplus',
|
||||||
|
'coupang wow': 'https://www.coupang.com/np/coupangplus',
|
||||||
|
'스타벅스 버디': 'https://www.starbucks.co.kr',
|
||||||
|
'starbucks buddy': 'https://www.starbucks.co.kr',
|
||||||
|
'cu 구독': 'https://cu.bgfretail.com',
|
||||||
|
'gs25 구독': 'https://gs25.gsretail.com',
|
||||||
|
'현대차 구독': 'https://www.hyundai.com/kr/ko/eco/vehicle-subscription',
|
||||||
|
'lg전자 구독': 'https://www.lge.co.kr',
|
||||||
|
'삼성전자 구독': 'https://www.samsung.com/sec',
|
||||||
|
'다이슨 케어': 'https://www.dyson.co.kr',
|
||||||
|
'dyson care': 'https://www.dyson.co.kr',
|
||||||
|
'마켓컬리': 'https://www.kurly.com',
|
||||||
|
'kurly': 'https://www.kurly.com',
|
||||||
|
'헬로네이처': 'https://www.hellonature.com',
|
||||||
|
'hello nature': 'https://www.hellonature.com',
|
||||||
|
'이마트 트레이더스': 'https://www.emarttraders.co.kr',
|
||||||
|
'홈플러스': 'https://www.homeplus.co.kr',
|
||||||
|
'hellofresh': 'https://www.hellofresh.com',
|
||||||
|
'헬로프레시': 'https://www.hellofresh.com',
|
||||||
|
'bespoke post': 'https://www.bespokepost.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 쇼핑/이커머스 서비스
|
||||||
|
static final Map<String, String> shoppingServices = {
|
||||||
|
'amazon prime': 'https://www.amazon.com/prime',
|
||||||
|
'아마존 프라임': 'https://www.amazon.com/prime',
|
||||||
|
'walmart+': 'https://www.walmart.com/plus',
|
||||||
|
'월마트플러스': 'https://www.walmart.com/plus',
|
||||||
|
'chewy': 'https://www.chewy.com',
|
||||||
|
'츄이': 'https://www.chewy.com',
|
||||||
|
'dollar shave club': 'https://www.dollarshaveclub.com',
|
||||||
|
'달러셰이브클럽': 'https://www.dollarshaveclub.com',
|
||||||
|
'instacart': 'https://www.instacart.com',
|
||||||
|
'인스타카트': 'https://www.instacart.com',
|
||||||
|
'shipt': 'https://www.shipt.com',
|
||||||
|
'십트': 'https://www.shipt.com',
|
||||||
|
'grove': 'https://grove.co',
|
||||||
|
'그로브': 'https://grove.co',
|
||||||
|
'cratejoy': 'https://www.cratejoy.com',
|
||||||
|
'shopify': 'https://www.shopify.com',
|
||||||
|
'쇼피파이': 'https://www.shopify.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
// AI 서비스
|
||||||
|
static final Map<String, String> aiServices = {
|
||||||
|
'chatgpt': 'https://chat.openai.com',
|
||||||
|
'챗GPT': 'https://chat.openai.com',
|
||||||
|
'openai': 'https://openai.com',
|
||||||
|
'오픈AI': 'https://openai.com',
|
||||||
|
'claude': 'https://claude.ai',
|
||||||
|
'클로드': 'https://claude.ai',
|
||||||
|
'anthropic': 'https://www.anthropic.com',
|
||||||
|
'앤트로픽': 'https://www.anthropic.com',
|
||||||
|
'midjourney': 'https://www.midjourney.com',
|
||||||
|
'미드저니': 'https://www.midjourney.com',
|
||||||
|
'perplexity': 'https://www.perplexity.ai',
|
||||||
|
'퍼플렉시티': 'https://www.perplexity.ai',
|
||||||
|
'copilot': 'https://copilot.microsoft.com',
|
||||||
|
'코파일럿': 'https://copilot.microsoft.com',
|
||||||
|
'gemini': 'https://gemini.google.com',
|
||||||
|
'제미니': 'https://gemini.google.com',
|
||||||
|
'google ai': 'https://ai.google',
|
||||||
|
'구글 AI': 'https://ai.google',
|
||||||
|
'bard': 'https://bard.google.com',
|
||||||
|
'바드': 'https://bard.google.com',
|
||||||
|
'dall-e': 'https://openai.com/dall-e',
|
||||||
|
'달리': 'https://openai.com/dall-e',
|
||||||
|
'stable diffusion': 'https://stability.ai',
|
||||||
|
'스테이블 디퓨전': 'https://stability.ai',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 프로그래밍 / 개발 서비스
|
||||||
|
static final Map<String, String> programmingServices = {
|
||||||
|
'github': 'https://github.com',
|
||||||
|
'깃허브': 'https://github.com',
|
||||||
|
'cursor': 'https://cursor.com',
|
||||||
|
'커서': 'https://cursor.com',
|
||||||
|
'jetbrains': 'https://www.jetbrains.com',
|
||||||
|
'제트브레인스': 'https://www.jetbrains.com',
|
||||||
|
'intellij': 'https://www.jetbrains.com/idea',
|
||||||
|
'인텔리제이': 'https://www.jetbrains.com/idea',
|
||||||
|
'visual studio': 'https://visualstudio.microsoft.com',
|
||||||
|
'비주얼 스튜디오': 'https://visualstudio.microsoft.com',
|
||||||
|
'aws': 'https://aws.amazon.com',
|
||||||
|
'아마존 웹서비스': 'https://aws.amazon.com',
|
||||||
|
'azure': 'https://azure.microsoft.com',
|
||||||
|
'애저': 'https://azure.microsoft.com',
|
||||||
|
'google cloud': 'https://cloud.google.com',
|
||||||
|
'구글 클라우드': 'https://cloud.google.com',
|
||||||
|
'digitalocean': 'https://www.digitalocean.com',
|
||||||
|
'디지털오션': 'https://www.digitalocean.com',
|
||||||
|
'heroku': 'https://www.heroku.com',
|
||||||
|
'헤로쿠': 'https://www.heroku.com',
|
||||||
|
'codecademy': 'https://www.codecademy.com',
|
||||||
|
'코드아카데미': 'https://www.codecademy.com',
|
||||||
|
'udemy': 'https://www.udemy.com',
|
||||||
|
'유데미': 'https://www.udemy.com',
|
||||||
|
'coursera': 'https://www.coursera.org',
|
||||||
|
'코세라': 'https://www.coursera.org',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 오피스 및 협업 툴
|
||||||
|
static final Map<String, String> officeTools = {
|
||||||
|
'microsoft 365': 'https://www.microsoft.com/microsoft-365',
|
||||||
|
'마이크로소프트 365': 'https://www.microsoft.com/microsoft-365',
|
||||||
|
'office 365': 'https://www.microsoft.com/microsoft-365',
|
||||||
|
'오피스 365': 'https://www.microsoft.com/microsoft-365',
|
||||||
|
'google workspace': 'https://workspace.google.com',
|
||||||
|
'구글 워크스페이스': 'https://workspace.google.com',
|
||||||
|
'slack': 'https://slack.com',
|
||||||
|
'슬랙': 'https://slack.com',
|
||||||
|
'notion': 'https://www.notion.so',
|
||||||
|
'노션': 'https://www.notion.so',
|
||||||
|
'trello': 'https://trello.com',
|
||||||
|
'트렐로': 'https://trello.com',
|
||||||
|
'asana': 'https://asana.com',
|
||||||
|
'아사나': 'https://asana.com',
|
||||||
|
'dropbox': 'https://www.dropbox.com',
|
||||||
|
'드롭박스': 'https://www.dropbox.com',
|
||||||
|
'figma': 'https://www.figma.com',
|
||||||
|
'피그마': 'https://www.figma.com',
|
||||||
|
'adobe creative cloud': 'https://www.adobe.com/creativecloud.html',
|
||||||
|
'어도비 크리에이티브 클라우드': 'https://www.adobe.com/creativecloud.html',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 기타 유명 서비스
|
||||||
|
static final Map<String, String> otherServices = {
|
||||||
|
'google one': 'https://one.google.com',
|
||||||
|
'구글 원': 'https://one.google.com',
|
||||||
|
'icloud': 'https://www.icloud.com',
|
||||||
|
'아이클라우드': 'https://www.icloud.com',
|
||||||
|
'nintendo switch online': 'https://www.nintendo.com/switch/online-service',
|
||||||
|
'닌텐도 스위치 온라인': 'https://www.nintendo.com/switch/online-service',
|
||||||
|
'playstation plus': 'https://www.playstation.com/ps-plus',
|
||||||
|
'플레이스테이션 플러스': 'https://www.playstation.com/ps-plus',
|
||||||
|
'xbox game pass': 'https://www.xbox.com/xbox-game-pass',
|
||||||
|
'엑스박스 게임 패스': 'https://www.xbox.com/xbox-game-pass',
|
||||||
|
'ea play': 'https://www.ea.com/ea-play',
|
||||||
|
'EA 플레이': 'https://www.ea.com/ea-play',
|
||||||
|
'ubisoft+': 'https://ubisoft.com/plus',
|
||||||
|
'유비소프트+': 'https://ubisoft.com/plus',
|
||||||
|
'epic games': 'https://www.epicgames.com',
|
||||||
|
'에픽 게임즈': 'https://www.epicgames.com',
|
||||||
|
'steam': 'https://store.steampowered.com',
|
||||||
|
'스팀': 'https://store.steampowered.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 해지 안내 페이지 URL 목록 (공식 해지 안내 페이지가 있는 서비스들)
|
||||||
|
static final Map<String, String> cancellationUrls = {
|
||||||
|
// OTT 서비스 해지 안내 페이지
|
||||||
|
'netflix': 'https://help.netflix.com/ko/node/407',
|
||||||
|
'넷플릭스': 'https://help.netflix.com/ko/node/407',
|
||||||
|
'disney+':
|
||||||
|
'https://help.disneyplus.com/csp?id=csp_article_content&sys_kb_id=f0ddbe01db7601105d5e040ad3961979',
|
||||||
|
'디즈니플러스':
|
||||||
|
'https://help.disneyplus.com/csp?id=csp_article_content&sys_kb_id=f0ddbe01db7601105d5e040ad3961979',
|
||||||
|
'youtube premium': 'https://support.google.com/youtube/answer/6308278',
|
||||||
|
'유튜브 프리미엄': 'https://support.google.com/youtube/answer/6308278',
|
||||||
|
'watcha': 'https://watcha.com/settings/payment',
|
||||||
|
'왓챠': 'https://watcha.com/settings/payment',
|
||||||
|
'wavve': 'https://www.wavve.com/my',
|
||||||
|
'웨이브': 'https://www.wavve.com/my',
|
||||||
|
'apple tv+': 'https://support.apple.com/ko-kr/HT202039',
|
||||||
|
'애플 티비플러스': 'https://support.apple.com/ko-kr/HT202039',
|
||||||
|
'tving': 'https://www.tving.com/my/cancelMembership',
|
||||||
|
'티빙': 'https://www.tving.com/my/cancelMembership',
|
||||||
|
'amazon prime': 'https://www.amazon.com/gp/primecentral/managemembership',
|
||||||
|
'아마존 프라임': 'https://www.amazon.com/gp/primecentral/managemembership',
|
||||||
|
|
||||||
|
// 음악 서비스 해지 안내 페이지
|
||||||
|
'spotify': 'https://support.spotify.com/us/article/cancel-premium/',
|
||||||
|
'스포티파이': 'https://support.spotify.com/us/article/cancel-premium/',
|
||||||
|
'apple music': 'https://support.apple.com/ko-kr/HT202039',
|
||||||
|
'애플 뮤직': 'https://support.apple.com/ko-kr/HT202039',
|
||||||
|
'melon':
|
||||||
|
'https://faqs2.melon.com/customer/faq/informFaq.htm?no=17&faqId=QUES20150209000002&orderChk=date&SEARCH_KEY=&SEARCH_PAR_CATEGORY=CATE20130909000006&SEARCH_CATEGORY=CATE20130909000021',
|
||||||
|
'멜론':
|
||||||
|
'https://faqs2.melon.com/customer/faq/informFaq.htm?no=17&faqId=QUES20150209000002&orderChk=date&SEARCH_KEY=&SEARCH_PAR_CATEGORY=CATE20130909000006&SEARCH_CATEGORY=CATE20130909000021',
|
||||||
|
'youtube music': 'https://support.google.com/youtubemusic/answer/6308278',
|
||||||
|
'유튜브 뮤직': 'https://support.google.com/youtubemusic/answer/6308278',
|
||||||
|
|
||||||
|
// AI 서비스 해지 안내 페이지
|
||||||
|
'chatgpt':
|
||||||
|
'https://help.openai.com/en/articles/7730211-manage-or-cancel-your-chatgpt-plus-subscription',
|
||||||
|
'챗GPT':
|
||||||
|
'https://help.openai.com/en/articles/7730211-manage-or-cancel-your-chatgpt-plus-subscription',
|
||||||
|
'claude':
|
||||||
|
'https://help.anthropic.com/en/articles/8798313-how-do-i-cancel-my-claude-subscription',
|
||||||
|
'클로드':
|
||||||
|
'https://help.anthropic.com/en/articles/8798313-how-do-i-cancel-my-claude-subscription',
|
||||||
|
'midjourney': 'https://docs.midjourney.com/docs/manage-subscription',
|
||||||
|
'미드저니': 'https://docs.midjourney.com/docs/manage-subscription',
|
||||||
|
|
||||||
|
// 프로그래밍 / 개발 서비스 해지 안내 페이지
|
||||||
|
'github':
|
||||||
|
'https://docs.github.com/ko/billing/managing-billing-for-your-github-account/downgrading-your-github-subscription',
|
||||||
|
'깃허브':
|
||||||
|
'https://docs.github.com/ko/billing/managing-billing-for-your-github-account/downgrading-your-github-subscription',
|
||||||
|
'jetbrains':
|
||||||
|
'https://sales.jetbrains.com/hc/en-gb/articles/207240845-How-to-cancel-an-auto-renewal-subscription-',
|
||||||
|
'제트브레인스':
|
||||||
|
'https://sales.jetbrains.com/hc/en-gb/articles/207240845-How-to-cancel-an-auto-renewal-subscription-',
|
||||||
|
|
||||||
|
// 오피스 및 협업 툴 해지 안내 페이지
|
||||||
|
'microsoft 365':
|
||||||
|
'https://support.microsoft.com/en-us/office/cancel-a-microsoft-365-subscription-46e2634c-c64b-4c65-94b9-2cc9c960e91b',
|
||||||
|
'마이크로소프트 365':
|
||||||
|
'https://support.microsoft.com/en-us/office/cancel-a-microsoft-365-subscription-46e2634c-c64b-4c65-94b9-2cc9c960e91b',
|
||||||
|
'office 365':
|
||||||
|
'https://support.microsoft.com/en-us/office/cancel-a-microsoft-365-subscription-46e2634c-c64b-4c65-94b9-2cc9c960e91b',
|
||||||
|
'오피스 365':
|
||||||
|
'https://support.microsoft.com/en-us/office/cancel-a-microsoft-365-subscription-46e2634c-c64b-4c65-94b9-2cc9c960e91b',
|
||||||
|
'slack':
|
||||||
|
'https://slack.com/help/articles/360003378691-Cancel-your-Slack-subscription',
|
||||||
|
'슬랙':
|
||||||
|
'https://slack.com/help/articles/360003378691-Cancel-your-Slack-subscription',
|
||||||
|
'notion':
|
||||||
|
'https://www.notion.so/help/billing-and-payment-settings#cancel-a-subscription',
|
||||||
|
'노션':
|
||||||
|
'https://www.notion.so/help/billing-and-payment-settings#cancel-a-subscription',
|
||||||
|
'dropbox': 'https://help.dropbox.com/accounts-billing/cancellation',
|
||||||
|
'드롭박스': 'https://help.dropbox.com/accounts-billing/cancellation',
|
||||||
|
'adobe creative cloud':
|
||||||
|
'https://helpx.adobe.com/manage-account/using/cancel-subscription.html',
|
||||||
|
'어도비 크리에이티브 클라우드':
|
||||||
|
'https://helpx.adobe.com/manage-account/using/cancel-subscription.html',
|
||||||
|
|
||||||
|
// 기타 유명 서비스 해지 안내 페이지
|
||||||
|
'google one': 'https://support.google.com/googleone/answer/9140429',
|
||||||
|
'구글 원': 'https://support.google.com/googleone/answer/9140429',
|
||||||
|
'icloud': 'https://support.apple.com/ko-kr/HT207594',
|
||||||
|
'아이클라우드': 'https://support.apple.com/ko-kr/HT207594',
|
||||||
|
'nintendo switch online':
|
||||||
|
'https://en-americas-support.nintendo.com/app/answers/detail/a_id/41925/~/how-to-cancel-a-nintendo-switch-online-membership',
|
||||||
|
'닌텐도 스위치 온라인':
|
||||||
|
'https://en-americas-support.nintendo.com/app/answers/detail/a_id/41925/~/how-to-cancel-a-nintendo-switch-online-membership',
|
||||||
|
'playstation plus':
|
||||||
|
'https://www.playstation.com/support/subscriptions/cancel-playstation-plus/',
|
||||||
|
'플레이스테이션 플러스':
|
||||||
|
'https://www.playstation.com/support/subscriptions/cancel-playstation-plus/',
|
||||||
|
'xbox game pass':
|
||||||
|
'https://support.xbox.com/help/subscriptions-billing/manage-subscriptions/xbox-game-pass-how-to-cancel',
|
||||||
|
'엑스박스 게임 패스':
|
||||||
|
'https://support.xbox.com/help/subscriptions-billing/manage-subscriptions/xbox-game-pass-how-to-cancel',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 모든 서비스 매핑을 합친 맵
|
||||||
|
static final Map<String, String> allServices = {
|
||||||
|
...ottServices,
|
||||||
|
...musicServices,
|
||||||
|
...storageServices,
|
||||||
|
...aiServices,
|
||||||
|
...programmingServices,
|
||||||
|
...officeTools,
|
||||||
|
...lifestyleServices,
|
||||||
|
...shoppingServices,
|
||||||
|
...telecomServices,
|
||||||
|
...otherServices,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// JSON 데이터 초기화
|
||||||
|
static Future<void> initialize() async {
|
||||||
|
if (_isInitialized) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final jsonString = await rootBundle.loadString('assets/data/subscription_services.json');
|
||||||
|
_servicesData = json.decode(jsonString);
|
||||||
|
_isInitialized = true;
|
||||||
|
print('SubscriptionUrlMatcher: JSON 데이터 로드 완료');
|
||||||
|
} catch (e) {
|
||||||
|
print('SubscriptionUrlMatcher: JSON 로드 실패 - $e');
|
||||||
|
// 로드 실패시 기존 하드코딩 데이터 사용
|
||||||
|
_isInitialized = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 도메인 추출 (www와 TLD 제외)
|
||||||
|
static String? extractDomain(String url) {
|
||||||
|
try {
|
||||||
|
final uri = Uri.parse(url);
|
||||||
|
final host = uri.host.toLowerCase();
|
||||||
|
|
||||||
|
// 도메인 부분 추출
|
||||||
|
var parts = host.split('.');
|
||||||
|
|
||||||
|
// www 제거
|
||||||
|
if (parts.isNotEmpty && parts[0] == 'www') {
|
||||||
|
parts = parts.sublist(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 서브도메인 처리 (예: music.youtube.com)
|
||||||
|
if (parts.length >= 3) {
|
||||||
|
// 서브도메인 포함 전체 도메인 반환
|
||||||
|
return parts.sublist(0, parts.length - 1).join('.');
|
||||||
|
} else if (parts.length >= 2) {
|
||||||
|
// 메인 도메인만 반환
|
||||||
|
return parts[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
} catch (e) {
|
||||||
|
print('SubscriptionUrlMatcher: 도메인 추출 실패 - $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// URL로 서비스 찾기
|
||||||
|
static Future<ServiceInfo?> findServiceByUrl(String url) async {
|
||||||
|
await initialize();
|
||||||
|
|
||||||
|
final domain = extractDomain(url);
|
||||||
|
if (domain == null) return null;
|
||||||
|
|
||||||
|
// JSON 데이터가 있으면 JSON에서 찾기
|
||||||
|
if (_servicesData != null) {
|
||||||
|
final categories = _servicesData!['categories'] as Map<String, dynamic>;
|
||||||
|
|
||||||
|
for (final categoryEntry in categories.entries) {
|
||||||
|
final categoryId = categoryEntry.key;
|
||||||
|
final categoryData = categoryEntry.value as Map<String, dynamic>;
|
||||||
|
final services = categoryData['services'] as Map<String, dynamic>;
|
||||||
|
|
||||||
|
for (final serviceEntry in services.entries) {
|
||||||
|
final serviceId = serviceEntry.key;
|
||||||
|
final serviceData = serviceEntry.value as Map<String, dynamic>;
|
||||||
|
final domains = List<String>.from(serviceData['domains'] ?? []);
|
||||||
|
|
||||||
|
// 도메인이 일치하는지 확인
|
||||||
|
for (final serviceDomain in domains) {
|
||||||
|
if (domain.contains(serviceDomain) || serviceDomain.contains(domain)) {
|
||||||
|
final names = List<String>.from(serviceData['names'] ?? []);
|
||||||
|
final urls = serviceData['urls'] as Map<String, dynamic>?;
|
||||||
|
|
||||||
|
return ServiceInfo(
|
||||||
|
serviceId: serviceId,
|
||||||
|
serviceName: names.isNotEmpty ? names[0] : serviceId,
|
||||||
|
serviceUrl: urls?['kr'] ?? urls?['en'],
|
||||||
|
cancellationUrl: null,
|
||||||
|
categoryId: _getCategoryIdByKey(categoryId),
|
||||||
|
categoryNameKr: categoryData['nameKr'] ?? '',
|
||||||
|
categoryNameEn: categoryData['nameEn'] ?? '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON에서 못 찾았으면 레거시 방식으로 찾기
|
||||||
|
for (final entry in allServices.entries) {
|
||||||
|
final serviceUrl = entry.value;
|
||||||
|
final serviceDomain = extractDomain(serviceUrl);
|
||||||
|
|
||||||
|
if (serviceDomain != null &&
|
||||||
|
(domain.contains(serviceDomain) || serviceDomain.contains(domain))) {
|
||||||
|
return ServiceInfo(
|
||||||
|
serviceId: entry.key,
|
||||||
|
serviceName: entry.key,
|
||||||
|
serviceUrl: serviceUrl,
|
||||||
|
cancellationUrl: null,
|
||||||
|
categoryId: _getCategoryForLegacyService(entry.key),
|
||||||
|
categoryNameKr: '',
|
||||||
|
categoryNameEn: '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 서비스명으로 URL 찾기 (기존 suggestUrl 메서드 유지)
|
||||||
|
static String? suggestUrl(String serviceName) {
|
||||||
|
if (serviceName.isEmpty) {
|
||||||
|
print('SubscriptionUrlMatcher: 빈 serviceName');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 소문자로 변환하여 비교
|
||||||
|
final lowerName = serviceName.toLowerCase().trim();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 정확한 매칭을 먼저 시도
|
||||||
|
for (final entry in allServices.entries) {
|
||||||
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
||||||
|
print('SubscriptionUrlMatcher: 정확한 매칭 - $lowerName -> ${entry.key}');
|
||||||
|
return entry.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OTT 서비스 검사
|
||||||
|
for (final entry in ottServices.entries) {
|
||||||
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
||||||
|
print(
|
||||||
|
'SubscriptionUrlMatcher: OTT 서비스 매칭 - $lowerName -> ${entry.key}');
|
||||||
|
return entry.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 음악 서비스 검사
|
||||||
|
for (final entry in musicServices.entries) {
|
||||||
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
||||||
|
print(
|
||||||
|
'SubscriptionUrlMatcher: 음악 서비스 매칭 - $lowerName -> ${entry.key}');
|
||||||
|
return entry.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI 서비스 검사
|
||||||
|
for (final entry in aiServices.entries) {
|
||||||
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
||||||
|
print(
|
||||||
|
'SubscriptionUrlMatcher: AI 서비스 매칭 - $lowerName -> ${entry.key}');
|
||||||
|
return entry.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 개발 서비스 검사
|
||||||
|
for (final entry in programmingServices.entries) {
|
||||||
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
||||||
|
print(
|
||||||
|
'SubscriptionUrlMatcher: 개발 서비스 매칭 - $lowerName -> ${entry.key}');
|
||||||
|
return entry.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 오피스 툴 검사
|
||||||
|
for (final entry in officeTools.entries) {
|
||||||
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
||||||
|
print(
|
||||||
|
'SubscriptionUrlMatcher: 오피스 툴 매칭 - $lowerName -> ${entry.key}');
|
||||||
|
return entry.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 기타 서비스 검사
|
||||||
|
for (final entry in otherServices.entries) {
|
||||||
|
if (lowerName.contains(entry.key.toLowerCase())) {
|
||||||
|
print(
|
||||||
|
'SubscriptionUrlMatcher: 기타 서비스 매칭 - $lowerName -> ${entry.key}');
|
||||||
|
return entry.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 유사한 이름 검사 (퍼지 매칭) - 단어 기반으로 검색
|
||||||
|
for (final entry in allServices.entries) {
|
||||||
|
final serviceWords = lowerName.split(' ');
|
||||||
|
final keyWords = entry.key.toLowerCase().split(' ');
|
||||||
|
|
||||||
|
// 단어 단위로 일치하는지 확인
|
||||||
|
for (final word in serviceWords) {
|
||||||
|
if (word.length > 2 &&
|
||||||
|
keyWords.any((keyWord) => keyWord.contains(word))) {
|
||||||
|
print(
|
||||||
|
'SubscriptionUrlMatcher: 단어 기반 매칭 - $word (in $lowerName) -> ${entry.key}');
|
||||||
|
return entry.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 추출 가능한 도메인이 있는지 확인
|
||||||
|
final domainMatch = RegExp(r'(\w+)').firstMatch(lowerName);
|
||||||
|
if (domainMatch != null && domainMatch.group(1)!.length > 2) {
|
||||||
|
final domain = domainMatch.group(1)!.trim();
|
||||||
|
if (domain.length > 2 &&
|
||||||
|
!['the', 'and', 'for', 'www'].contains(domain)) {
|
||||||
|
final url = 'https://www.$domain.com';
|
||||||
|
print('SubscriptionUrlMatcher: 도메인 추출 - $lowerName -> $url');
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print('SubscriptionUrlMatcher: 매칭 실패 - $lowerName');
|
||||||
|
return null;
|
||||||
|
} catch (e) {
|
||||||
|
print('SubscriptionUrlMatcher: URL 매칭 중 오류 발생: $e');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 해지 안내 URL 찾기 (개선된 버전)
|
||||||
|
static Future<String?> findCancellationUrl({
|
||||||
|
String? serviceName,
|
||||||
|
String? websiteUrl,
|
||||||
|
String locale = 'kr',
|
||||||
|
}) async {
|
||||||
|
await initialize();
|
||||||
|
|
||||||
|
// JSON 데이터가 있으면 JSON에서 찾기
|
||||||
|
if (_servicesData != null) {
|
||||||
|
final categories = _servicesData!['categories'] as Map<String, dynamic>;
|
||||||
|
|
||||||
|
// 1. 서비스명으로 찾기
|
||||||
|
if (serviceName != null && serviceName.isNotEmpty) {
|
||||||
|
final lowerName = serviceName.toLowerCase().trim();
|
||||||
|
|
||||||
|
for (final categoryData in categories.values) {
|
||||||
|
final services = (categoryData as Map<String, dynamic>)['services'] as Map<String, dynamic>;
|
||||||
|
|
||||||
|
for (final serviceData in services.values) {
|
||||||
|
final names = List<String>.from((serviceData as Map<String, dynamic>)['names'] ?? []);
|
||||||
|
|
||||||
|
for (final name in names) {
|
||||||
|
if (lowerName.contains(name.toLowerCase()) || name.toLowerCase().contains(lowerName)) {
|
||||||
|
final cancellationUrls = serviceData['cancellationUrls'] as Map<String, dynamic>?;
|
||||||
|
if (cancellationUrls != null) {
|
||||||
|
// 요청한 언어의 URL이 있으면 반환, 없으면 다른 언어 URL 반환
|
||||||
|
return cancellationUrls[locale] ??
|
||||||
|
cancellationUrls[locale == 'kr' ? 'en' : 'kr'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. URL로 찾기
|
||||||
|
if (websiteUrl != null && websiteUrl.isNotEmpty) {
|
||||||
|
final domain = extractDomain(websiteUrl);
|
||||||
|
if (domain != null) {
|
||||||
|
for (final categoryData in categories.values) {
|
||||||
|
final services = (categoryData as Map<String, dynamic>)['services'] as Map<String, dynamic>;
|
||||||
|
|
||||||
|
for (final serviceData in services.values) {
|
||||||
|
final domains = List<String>.from((serviceData as Map<String, dynamic>)['domains'] ?? []);
|
||||||
|
|
||||||
|
for (final serviceDomain in domains) {
|
||||||
|
if (domain.contains(serviceDomain) || serviceDomain.contains(domain)) {
|
||||||
|
final cancellationUrls = serviceData['cancellationUrls'] as Map<String, dynamic>?;
|
||||||
|
if (cancellationUrls != null) {
|
||||||
|
return cancellationUrls[locale] ??
|
||||||
|
cancellationUrls[locale == 'kr' ? 'en' : 'kr'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON에서 못 찾았으면 레거시 방식으로 찾기
|
||||||
|
return _findCancellationUrlLegacy(serviceName ?? websiteUrl ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 서비스명 또는 웹사이트 URL을 기반으로 해지 안내 페이지 URL 찾기 (레거시)
|
||||||
|
static String? _findCancellationUrlLegacy(String serviceNameOrUrl) {
|
||||||
|
if (serviceNameOrUrl.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 소문자로 변환하여 처리
|
||||||
|
final String lowerText = serviceNameOrUrl.toLowerCase().trim();
|
||||||
|
|
||||||
|
// 직접 서비스명으로 찾기
|
||||||
|
if (cancellationUrls.containsKey(lowerText)) {
|
||||||
|
return cancellationUrls[lowerText];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 서비스명에 부분 포함으로 찾기
|
||||||
|
for (var entry in cancellationUrls.entries) {
|
||||||
|
final String key = entry.key.toLowerCase();
|
||||||
|
if (lowerText.contains(key) || key.contains(lowerText)) {
|
||||||
|
return entry.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL을 통해 서비스명 추출 후 찾기
|
||||||
|
if (lowerText.startsWith('http')) {
|
||||||
|
// URL 도메인 추출 (https://www.netflix.com 에서 netflix 추출)
|
||||||
|
final domainRegex = RegExp(r'https?://(?:www\.)?([a-zA-Z0-9-]+)');
|
||||||
|
final match = domainRegex.firstMatch(lowerText);
|
||||||
|
|
||||||
|
if (match != null && match.groupCount >= 1) {
|
||||||
|
final domain = match.group(1)?.toLowerCase() ?? '';
|
||||||
|
|
||||||
|
// 도메인으로 서비스명 찾기
|
||||||
|
for (var entry in cancellationUrls.entries) {
|
||||||
|
if (entry.key.toLowerCase().contains(domain)) {
|
||||||
|
return entry.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 해지 안내 페이지를 찾지 못함
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 서비스에 공식 해지 안내 페이지가 있는지 확인
|
||||||
|
static Future<bool> hasCancellationPage(String serviceNameOrUrl) async {
|
||||||
|
// 새로운 JSON 기반 방식으로 확인
|
||||||
|
final cancellationUrl = await findCancellationUrl(
|
||||||
|
serviceName: serviceNameOrUrl,
|
||||||
|
websiteUrl: serviceNameOrUrl,
|
||||||
|
);
|
||||||
|
return cancellationUrl != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 서비스명으로 카테고리 찾기
|
||||||
|
static Future<String?> findCategoryByServiceName(String serviceName) async {
|
||||||
|
await initialize();
|
||||||
|
if (serviceName.isEmpty) return null;
|
||||||
|
|
||||||
|
final lowerName = serviceName.toLowerCase().trim();
|
||||||
|
|
||||||
|
// JSON 데이터가 있으면 JSON에서 찾기
|
||||||
|
if (_servicesData != null) {
|
||||||
|
final categories = _servicesData!['categories'] as Map<String, dynamic>;
|
||||||
|
|
||||||
|
for (final categoryEntry in categories.entries) {
|
||||||
|
final categoryId = categoryEntry.key;
|
||||||
|
final categoryData = categoryEntry.value as Map<String, dynamic>;
|
||||||
|
final services = categoryData['services'] as Map<String, dynamic>;
|
||||||
|
|
||||||
|
for (final serviceData in services.values) {
|
||||||
|
final names = List<String>.from((serviceData as Map<String, dynamic>)['names'] ?? []);
|
||||||
|
|
||||||
|
for (final name in names) {
|
||||||
|
if (lowerName.contains(name.toLowerCase()) || name.toLowerCase().contains(lowerName)) {
|
||||||
|
return _getCategoryIdByKey(categoryId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON에서 못 찾았으면 레거시 방식으로 카테고리 추측
|
||||||
|
return _getCategoryForLegacyService(serviceName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 현재 로케일에 따라 서비스 표시명 가져오기
|
||||||
|
static Future<String> getServiceDisplayName({
|
||||||
|
required String serviceName,
|
||||||
|
required String locale,
|
||||||
|
}) async {
|
||||||
|
await initialize();
|
||||||
|
|
||||||
|
if (_servicesData == null) {
|
||||||
|
return serviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
final lowerName = serviceName.toLowerCase().trim();
|
||||||
|
final categories = _servicesData!['categories'] as Map<String, dynamic>;
|
||||||
|
|
||||||
|
// JSON에서 서비스 찾기
|
||||||
|
for (final categoryData in categories.values) {
|
||||||
|
final services = (categoryData as Map<String, dynamic>)['services'] as Map<String, dynamic>;
|
||||||
|
|
||||||
|
for (final serviceData in services.values) {
|
||||||
|
final data = serviceData as Map<String, dynamic>;
|
||||||
|
final names = List<String>.from(data['names'] ?? []);
|
||||||
|
|
||||||
|
// names 배열에 있는지 확인
|
||||||
|
for (final name in names) {
|
||||||
|
if (lowerName == name.toLowerCase() ||
|
||||||
|
lowerName.contains(name.toLowerCase()) ||
|
||||||
|
name.toLowerCase().contains(lowerName)) {
|
||||||
|
// 로케일에 따라 적절한 이름 반환
|
||||||
|
if (locale == 'ko' || locale == 'kr') {
|
||||||
|
return data['nameKr'] ?? serviceName;
|
||||||
|
} else {
|
||||||
|
return data['nameEn'] ?? serviceName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// nameKr/nameEn에 직접 매칭 확인
|
||||||
|
final nameKr = (data['nameKr'] ?? '').toString().toLowerCase();
|
||||||
|
final nameEn = (data['nameEn'] ?? '').toString().toLowerCase();
|
||||||
|
|
||||||
|
if (lowerName == nameKr || lowerName == nameEn) {
|
||||||
|
if (locale == 'ko' || locale == 'kr') {
|
||||||
|
return data['nameKr'] ?? serviceName;
|
||||||
|
} else {
|
||||||
|
return data['nameEn'] ?? serviceName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 찾지 못한 경우 원래 이름 반환
|
||||||
|
return serviceName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 카테고리 키를 실제 카테고리 ID로 매핑
|
||||||
|
static String _getCategoryIdByKey(String key) {
|
||||||
|
// 여기에 실제 앱의 카테고리 ID 매핑을 추가
|
||||||
|
// 임시로 카테고리명 기반 매핑
|
||||||
|
switch (key) {
|
||||||
|
case 'music':
|
||||||
|
return 'music_streaming';
|
||||||
|
case 'ott':
|
||||||
|
return 'ott_services';
|
||||||
|
case 'storage':
|
||||||
|
return 'cloud_storage';
|
||||||
|
case 'ai':
|
||||||
|
return 'ai_services';
|
||||||
|
case 'programming':
|
||||||
|
return 'dev_tools';
|
||||||
|
case 'office':
|
||||||
|
return 'office_tools';
|
||||||
|
case 'lifestyle':
|
||||||
|
return 'lifestyle';
|
||||||
|
case 'shopping':
|
||||||
|
return 'shopping';
|
||||||
|
case 'gaming':
|
||||||
|
return 'gaming';
|
||||||
|
case 'telecom':
|
||||||
|
return 'telecom';
|
||||||
|
default:
|
||||||
|
return 'other';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 레거시 서비스명으로 카테고리 추측
|
||||||
|
static String _getCategoryForLegacyService(String serviceName) {
|
||||||
|
final lowerName = serviceName.toLowerCase();
|
||||||
|
|
||||||
|
if (ottServices.containsKey(lowerName)) return 'ott_services';
|
||||||
|
if (musicServices.containsKey(lowerName)) return 'music_streaming';
|
||||||
|
if (storageServices.containsKey(lowerName)) return 'cloud_storage';
|
||||||
|
if (aiServices.containsKey(lowerName)) return 'ai_services';
|
||||||
|
if (programmingServices.containsKey(lowerName)) return 'dev_tools';
|
||||||
|
if (officeTools.containsKey(lowerName)) return 'office_tools';
|
||||||
|
if (lifestyleServices.containsKey(lowerName)) return 'lifestyle';
|
||||||
|
if (shoppingServices.containsKey(lowerName)) return 'shopping';
|
||||||
|
if (telecomServices.containsKey(lowerName)) return 'telecom';
|
||||||
|
|
||||||
|
return 'other';
|
||||||
|
}
|
||||||
|
|
||||||
|
/// SMS에서 URL과 서비스 정보 추출
|
||||||
|
static Future<ServiceInfo?> extractServiceFromSms(String smsText) async {
|
||||||
|
await initialize();
|
||||||
|
|
||||||
|
// URL 패턴 찾기
|
||||||
|
final urlPattern = RegExp(
|
||||||
|
r'https?://(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b(?:[-a-zA-Z0-9()@:%_\+.~#?&//=]*)',
|
||||||
|
caseSensitive: false,
|
||||||
|
);
|
||||||
|
|
||||||
|
final matches = urlPattern.allMatches(smsText);
|
||||||
|
|
||||||
|
for (final match in matches) {
|
||||||
|
final url = match.group(0);
|
||||||
|
if (url != null) {
|
||||||
|
final serviceInfo = await findServiceByUrl(url);
|
||||||
|
if (serviceInfo != null) {
|
||||||
|
return serviceInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// URL로 못 찾았으면 서비스명으로 시도
|
||||||
|
final lowerSms = smsText.toLowerCase();
|
||||||
|
|
||||||
|
// 모든 서비스명 검사
|
||||||
|
for (final entry in allServices.entries) {
|
||||||
|
if (lowerSms.contains(entry.key.toLowerCase())) {
|
||||||
|
final categoryId = await findCategoryByServiceName(entry.key) ?? 'other';
|
||||||
|
|
||||||
|
return ServiceInfo(
|
||||||
|
serviceId: entry.key,
|
||||||
|
serviceName: entry.key,
|
||||||
|
serviceUrl: entry.value,
|
||||||
|
cancellationUrl: null,
|
||||||
|
categoryId: categoryId,
|
||||||
|
categoryNameKr: '',
|
||||||
|
categoryNameEn: '',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// URL이 알려진 서비스 URL인지 확인
|
||||||
|
static Future<bool> isKnownServiceUrl(String url) async {
|
||||||
|
final serviceInfo = await findServiceByUrl(url);
|
||||||
|
return serviceInfo != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 입력된 서비스 이름이나 문자열에서 매칭되는 URL을 찾아 반환 (레거시 호환성)
|
||||||
|
static String? findMatchingUrl(String text, {bool usePartialMatch = true}) {
|
||||||
|
// 입력 텍스트가 비어있거나 null인 경우
|
||||||
|
if (text.isEmpty) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 소문자로 변환하여 처리
|
||||||
|
final String lowerText = text.toLowerCase().trim();
|
||||||
|
|
||||||
|
// 정확히 일치하는 경우
|
||||||
|
if (allServices.containsKey(lowerText)) {
|
||||||
|
return allServices[lowerText];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 부분 일치 검색이 활성화된 경우
|
||||||
|
if (usePartialMatch) {
|
||||||
|
// 가장 긴 부분 매칭 찾기
|
||||||
|
String? bestMatch;
|
||||||
|
int maxLength = 0;
|
||||||
|
|
||||||
|
for (var entry in allServices.entries) {
|
||||||
|
final String key = entry.key;
|
||||||
|
|
||||||
|
// 입력된 텍스트에 서비스 키워드가 포함되어 있거나, 서비스 키워드에 입력된 텍스트가 포함된 경우
|
||||||
|
if (lowerText.contains(key) || key.contains(lowerText)) {
|
||||||
|
// 더 긴 매칭을 우선시
|
||||||
|
if (key.length > maxLength) {
|
||||||
|
maxLength = key.length;
|
||||||
|
bestMatch = entry.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestMatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
368
lib/services/url_matcher/data/legacy_service_data.dart
Normal file
368
lib/services/url_matcher/data/legacy_service_data.dart
Normal file
@@ -0,0 +1,368 @@
|
|||||||
|
/// 레거시 서비스 데이터 - 하드코딩된 서비스 URL 매핑
|
||||||
|
class LegacyServiceData {
|
||||||
|
// OTT 서비스
|
||||||
|
static final Map<String, String> ottServices = {
|
||||||
|
'netflix': 'https://www.netflix.com',
|
||||||
|
'넷플릭스': 'https://www.netflix.com',
|
||||||
|
'disney+': 'https://www.disneyplus.com',
|
||||||
|
'디즈니플러스': 'https://www.disneyplus.com',
|
||||||
|
'youtube premium': 'https://www.youtube.com/premium',
|
||||||
|
'유튜브 프리미엄': 'https://www.youtube.com/premium',
|
||||||
|
'watcha': 'https://watcha.com',
|
||||||
|
'왓챠': 'https://watcha.com',
|
||||||
|
'wavve': 'https://www.wavve.com',
|
||||||
|
'웨이브': 'https://www.wavve.com',
|
||||||
|
'apple tv+': 'https://tv.apple.com',
|
||||||
|
'애플 티비플러스': 'https://tv.apple.com',
|
||||||
|
'tving': 'https://www.tving.com',
|
||||||
|
'티빙': 'https://www.tving.com',
|
||||||
|
'prime video': 'https://www.primevideo.com',
|
||||||
|
'프라임 비디오': 'https://www.primevideo.com',
|
||||||
|
'amazon prime': 'https://www.amazon.com/prime',
|
||||||
|
'아마존 프라임': 'https://www.amazon.com/prime',
|
||||||
|
'coupang play': 'https://play.coupangplay.com',
|
||||||
|
'쿠팡 플레이': 'https://play.coupangplay.com',
|
||||||
|
'hulu': 'https://www.hulu.com',
|
||||||
|
'훌루': 'https://www.hulu.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 음악 서비스
|
||||||
|
static final Map<String, String> musicServices = {
|
||||||
|
'spotify': 'https://www.spotify.com',
|
||||||
|
'스포티파이': 'https://www.spotify.com',
|
||||||
|
'apple music': 'https://music.apple.com',
|
||||||
|
'애플 뮤직': 'https://music.apple.com',
|
||||||
|
'melon': 'https://www.melon.com',
|
||||||
|
'멜론': 'https://www.melon.com',
|
||||||
|
'genie': 'https://www.genie.co.kr',
|
||||||
|
'지니': 'https://www.genie.co.kr',
|
||||||
|
'youtube music': 'https://music.youtube.com',
|
||||||
|
'유튜브 뮤직': 'https://music.youtube.com',
|
||||||
|
'bugs': 'https://music.bugs.co.kr',
|
||||||
|
'벅스': 'https://music.bugs.co.kr',
|
||||||
|
'flo': 'https://www.music-flo.com',
|
||||||
|
'플로': 'https://www.music-flo.com',
|
||||||
|
'vibe': 'https://vibe.naver.com',
|
||||||
|
'바이브': 'https://vibe.naver.com',
|
||||||
|
'tidal': 'https://www.tidal.com',
|
||||||
|
'타이달': 'https://www.tidal.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 저장 (클라우드/파일) 서비스
|
||||||
|
static final Map<String, String> storageServices = {
|
||||||
|
'google drive': 'https://www.google.com/drive/',
|
||||||
|
'구글 드라이브': 'https://www.google.com/drive/',
|
||||||
|
'dropbox': 'https://www.dropbox.com',
|
||||||
|
'드롭박스': 'https://www.dropbox.com',
|
||||||
|
'onedrive': 'https://www.onedrive.com',
|
||||||
|
'원드라이브': 'https://www.onedrive.com',
|
||||||
|
'icloud': 'https://www.icloud.com',
|
||||||
|
'아이클라우드': 'https://www.icloud.com',
|
||||||
|
'box': 'https://www.box.com',
|
||||||
|
'박스': 'https://www.box.com',
|
||||||
|
'pcloud': 'https://www.pcloud.com',
|
||||||
|
'mega': 'https://mega.nz',
|
||||||
|
'메가': 'https://mega.nz',
|
||||||
|
'naver mybox': 'https://mybox.naver.com',
|
||||||
|
'네이버 마이박스': 'https://mybox.naver.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 통신 · 인터넷 · TV 서비스
|
||||||
|
static final Map<String, String> telecomServices = {
|
||||||
|
'skt': 'https://www.sktelecom.com',
|
||||||
|
'sk텔레콤': 'https://www.sktelecom.com',
|
||||||
|
'kt': 'https://www.kt.com',
|
||||||
|
'lgu+': 'https://www.lguplus.com',
|
||||||
|
'lg유플러스': 'https://www.lguplus.com',
|
||||||
|
'olleh tv': 'https://www.kt.com/olleh_tv',
|
||||||
|
'올레 tv': 'https://www.kt.com/olleh_tv',
|
||||||
|
'b tv': 'https://www.skbroadband.com',
|
||||||
|
'비티비': 'https://www.skbroadband.com',
|
||||||
|
'u+모바일tv': 'https://www.lguplus.com',
|
||||||
|
'유플러스모바일tv': 'https://www.lguplus.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 생활/라이프스타일 서비스
|
||||||
|
static final Map<String, String> lifestyleServices = {
|
||||||
|
'네이버 플러스': 'https://plus.naver.com',
|
||||||
|
'naver plus': 'https://plus.naver.com',
|
||||||
|
'카카오 구독': 'https://subscribe.kakao.com',
|
||||||
|
'kakao subscribe': 'https://subscribe.kakao.com',
|
||||||
|
'쿠팡 와우': 'https://www.coupang.com/np/coupangplus',
|
||||||
|
'coupang wow': 'https://www.coupang.com/np/coupangplus',
|
||||||
|
'스타벅스 버디': 'https://www.starbucks.co.kr',
|
||||||
|
'starbucks buddy': 'https://www.starbucks.co.kr',
|
||||||
|
'cu 구독': 'https://cu.bgfretail.com',
|
||||||
|
'gs25 구독': 'https://gs25.gsretail.com',
|
||||||
|
'현대차 구독': 'https://www.hyundai.com/kr/ko/eco/vehicle-subscription',
|
||||||
|
'lg전자 구독': 'https://www.lge.co.kr',
|
||||||
|
'삼성전자 구독': 'https://www.samsung.com/sec',
|
||||||
|
'다이슨 케어': 'https://www.dyson.co.kr',
|
||||||
|
'dyson care': 'https://www.dyson.co.kr',
|
||||||
|
'마켓컬리': 'https://www.kurly.com',
|
||||||
|
'kurly': 'https://www.kurly.com',
|
||||||
|
'헬로네이처': 'https://www.hellonature.com',
|
||||||
|
'hello nature': 'https://www.hellonature.com',
|
||||||
|
'이마트 트레이더스': 'https://www.emarttraders.co.kr',
|
||||||
|
'홈플러스': 'https://www.homeplus.co.kr',
|
||||||
|
'hellofresh': 'https://www.hellofresh.com',
|
||||||
|
'헬로프레시': 'https://www.hellofresh.com',
|
||||||
|
'bespoke post': 'https://www.bespokepost.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 쇼핑/이커머스 서비스
|
||||||
|
static final Map<String, String> shoppingServices = {
|
||||||
|
'amazon prime': 'https://www.amazon.com/prime',
|
||||||
|
'아마존 프라임': 'https://www.amazon.com/prime',
|
||||||
|
'walmart+': 'https://www.walmart.com/plus',
|
||||||
|
'월마트플러스': 'https://www.walmart.com/plus',
|
||||||
|
'chewy': 'https://www.chewy.com',
|
||||||
|
'츄이': 'https://www.chewy.com',
|
||||||
|
'dollar shave club': 'https://www.dollarshaveclub.com',
|
||||||
|
'달러셰이브클럽': 'https://www.dollarshaveclub.com',
|
||||||
|
'instacart': 'https://www.instacart.com',
|
||||||
|
'인스타카트': 'https://www.instacart.com',
|
||||||
|
'shipt': 'https://www.shipt.com',
|
||||||
|
'십트': 'https://www.shipt.com',
|
||||||
|
'grove': 'https://grove.co',
|
||||||
|
'그로브': 'https://grove.co',
|
||||||
|
'cratejoy': 'https://www.cratejoy.com',
|
||||||
|
'shopify': 'https://www.shopify.com',
|
||||||
|
'쇼피파이': 'https://www.shopify.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
// AI 서비스
|
||||||
|
static final Map<String, String> aiServices = {
|
||||||
|
'chatgpt': 'https://chat.openai.com',
|
||||||
|
'챗GPT': 'https://chat.openai.com',
|
||||||
|
'openai': 'https://openai.com',
|
||||||
|
'오픈AI': 'https://openai.com',
|
||||||
|
'claude': 'https://claude.ai',
|
||||||
|
'클로드': 'https://claude.ai',
|
||||||
|
'anthropic': 'https://www.anthropic.com',
|
||||||
|
'앤트로픽': 'https://www.anthropic.com',
|
||||||
|
'midjourney': 'https://www.midjourney.com',
|
||||||
|
'미드저니': 'https://www.midjourney.com',
|
||||||
|
'perplexity': 'https://www.perplexity.ai',
|
||||||
|
'퍼플렉시티': 'https://www.perplexity.ai',
|
||||||
|
'copilot': 'https://copilot.microsoft.com',
|
||||||
|
'코파일럿': 'https://copilot.microsoft.com',
|
||||||
|
'gemini': 'https://gemini.google.com',
|
||||||
|
'제미니': 'https://gemini.google.com',
|
||||||
|
'google ai': 'https://ai.google',
|
||||||
|
'구글 AI': 'https://ai.google',
|
||||||
|
'bard': 'https://bard.google.com',
|
||||||
|
'바드': 'https://bard.google.com',
|
||||||
|
'dall-e': 'https://openai.com/dall-e',
|
||||||
|
'달리': 'https://openai.com/dall-e',
|
||||||
|
'stable diffusion': 'https://stability.ai',
|
||||||
|
'스테이블 디퓨전': 'https://stability.ai',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 프로그래밍 / 개발 서비스
|
||||||
|
static final Map<String, String> programmingServices = {
|
||||||
|
'github': 'https://github.com',
|
||||||
|
'깃허브': 'https://github.com',
|
||||||
|
'cursor': 'https://cursor.com',
|
||||||
|
'커서': 'https://cursor.com',
|
||||||
|
'jetbrains': 'https://www.jetbrains.com',
|
||||||
|
'제트브레인스': 'https://www.jetbrains.com',
|
||||||
|
'intellij': 'https://www.jetbrains.com/idea',
|
||||||
|
'인텔리제이': 'https://www.jetbrains.com/idea',
|
||||||
|
'visual studio': 'https://visualstudio.microsoft.com',
|
||||||
|
'비주얼 스튜디오': 'https://visualstudio.microsoft.com',
|
||||||
|
'aws': 'https://aws.amazon.com',
|
||||||
|
'아마존 웹서비스': 'https://aws.amazon.com',
|
||||||
|
'azure': 'https://azure.microsoft.com',
|
||||||
|
'애저': 'https://azure.microsoft.com',
|
||||||
|
'google cloud': 'https://cloud.google.com',
|
||||||
|
'구글 클라우드': 'https://cloud.google.com',
|
||||||
|
'digitalocean': 'https://www.digitalocean.com',
|
||||||
|
'디지털오션': 'https://www.digitalocean.com',
|
||||||
|
'heroku': 'https://www.heroku.com',
|
||||||
|
'헤로쿠': 'https://www.heroku.com',
|
||||||
|
'codecademy': 'https://www.codecademy.com',
|
||||||
|
'코드아카데미': 'https://www.codecademy.com',
|
||||||
|
'udemy': 'https://www.udemy.com',
|
||||||
|
'유데미': 'https://www.udemy.com',
|
||||||
|
'coursera': 'https://www.coursera.org',
|
||||||
|
'코세라': 'https://www.coursera.org',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 오피스 및 협업 툴
|
||||||
|
static final Map<String, String> officeTools = {
|
||||||
|
'microsoft 365': 'https://www.microsoft.com/microsoft-365',
|
||||||
|
'마이크로소프트 365': 'https://www.microsoft.com/microsoft-365',
|
||||||
|
'office 365': 'https://www.microsoft.com/microsoft-365',
|
||||||
|
'오피스 365': 'https://www.microsoft.com/microsoft-365',
|
||||||
|
'google workspace': 'https://workspace.google.com',
|
||||||
|
'구글 워크스페이스': 'https://workspace.google.com',
|
||||||
|
'slack': 'https://slack.com',
|
||||||
|
'슬랙': 'https://slack.com',
|
||||||
|
'notion': 'https://www.notion.so',
|
||||||
|
'노션': 'https://www.notion.so',
|
||||||
|
'trello': 'https://trello.com',
|
||||||
|
'트렐로': 'https://trello.com',
|
||||||
|
'asana': 'https://asana.com',
|
||||||
|
'아사나': 'https://asana.com',
|
||||||
|
'dropbox': 'https://www.dropbox.com',
|
||||||
|
'드롭박스': 'https://www.dropbox.com',
|
||||||
|
'figma': 'https://www.figma.com',
|
||||||
|
'피그마': 'https://www.figma.com',
|
||||||
|
'adobe creative cloud': 'https://www.adobe.com/creativecloud.html',
|
||||||
|
'어도비 크리에이티브 클라우드': 'https://www.adobe.com/creativecloud.html',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 기타 유명 서비스
|
||||||
|
static final Map<String, String> otherServices = {
|
||||||
|
'google one': 'https://one.google.com',
|
||||||
|
'구글 원': 'https://one.google.com',
|
||||||
|
'icloud': 'https://www.icloud.com',
|
||||||
|
'아이클라우드': 'https://www.icloud.com',
|
||||||
|
'nintendo switch online': 'https://www.nintendo.com/switch/online-service',
|
||||||
|
'닌텐도 스위치 온라인': 'https://www.nintendo.com/switch/online-service',
|
||||||
|
'playstation plus': 'https://www.playstation.com/ps-plus',
|
||||||
|
'플레이스테이션 플러스': 'https://www.playstation.com/ps-plus',
|
||||||
|
'xbox game pass': 'https://www.xbox.com/xbox-game-pass',
|
||||||
|
'엑스박스 게임 패스': 'https://www.xbox.com/xbox-game-pass',
|
||||||
|
'ea play': 'https://www.ea.com/ea-play',
|
||||||
|
'EA 플레이': 'https://www.ea.com/ea-play',
|
||||||
|
'ubisoft+': 'https://ubisoft.com/plus',
|
||||||
|
'유비소프트+': 'https://ubisoft.com/plus',
|
||||||
|
'epic games': 'https://www.epicgames.com',
|
||||||
|
'에픽 게임즈': 'https://www.epicgames.com',
|
||||||
|
'steam': 'https://store.steampowered.com',
|
||||||
|
'스팀': 'https://store.steampowered.com',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 해지 안내 페이지 URL 목록 (공식 해지 안내 페이지가 있는 서비스들)
|
||||||
|
static final Map<String, String> cancellationUrls = {
|
||||||
|
// OTT 서비스 해지 안내 페이지
|
||||||
|
'netflix': 'https://help.netflix.com/ko/node/407',
|
||||||
|
'넷플릭스': 'https://help.netflix.com/ko/node/407',
|
||||||
|
'disney+':
|
||||||
|
'https://help.disneyplus.com/csp?id=csp_article_content&sys_kb_id=f0ddbe01db7601105d5e040ad3961979',
|
||||||
|
'디즈니플러스':
|
||||||
|
'https://help.disneyplus.com/csp?id=csp_article_content&sys_kb_id=f0ddbe01db7601105d5e040ad3961979',
|
||||||
|
'youtube premium': 'https://support.google.com/youtube/answer/6308278',
|
||||||
|
'유튜브 프리미엄': 'https://support.google.com/youtube/answer/6308278',
|
||||||
|
'watcha': 'https://watcha.com/settings/payment',
|
||||||
|
'왓챠': 'https://watcha.com/settings/payment',
|
||||||
|
'wavve': 'https://www.wavve.com/my',
|
||||||
|
'웨이브': 'https://www.wavve.com/my',
|
||||||
|
'apple tv+': 'https://support.apple.com/ko-kr/HT202039',
|
||||||
|
'애플 티비플러스': 'https://support.apple.com/ko-kr/HT202039',
|
||||||
|
'tving': 'https://www.tving.com/my/cancelMembership',
|
||||||
|
'티빙': 'https://www.tving.com/my/cancelMembership',
|
||||||
|
'amazon prime': 'https://www.amazon.com/gp/primecentral/managemembership',
|
||||||
|
'아마존 프라임': 'https://www.amazon.com/gp/primecentral/managemembership',
|
||||||
|
|
||||||
|
// 음악 서비스 해지 안내 페이지
|
||||||
|
'spotify': 'https://support.spotify.com/us/article/cancel-premium/',
|
||||||
|
'스포티파이': 'https://support.spotify.com/us/article/cancel-premium/',
|
||||||
|
'apple music': 'https://support.apple.com/ko-kr/HT202039',
|
||||||
|
'애플 뮤직': 'https://support.apple.com/ko-kr/HT202039',
|
||||||
|
'melon':
|
||||||
|
'https://faqs2.melon.com/customer/faq/informFaq.htm?no=17&faqId=QUES20150209000002&orderChk=date&SEARCH_KEY=&SEARCH_PAR_CATEGORY=CATE20130909000006&SEARCH_CATEGORY=CATE20130909000021',
|
||||||
|
'멜론':
|
||||||
|
'https://faqs2.melon.com/customer/faq/informFaq.htm?no=17&faqId=QUES20150209000002&orderChk=date&SEARCH_KEY=&SEARCH_PAR_CATEGORY=CATE20130909000006&SEARCH_CATEGORY=CATE20130909000021',
|
||||||
|
'youtube music': 'https://support.google.com/youtubemusic/answer/6308278',
|
||||||
|
'유튜브 뮤직': 'https://support.google.com/youtubemusic/answer/6308278',
|
||||||
|
|
||||||
|
// AI 서비스 해지 안내 페이지
|
||||||
|
'chatgpt':
|
||||||
|
'https://help.openai.com/en/articles/7730211-manage-or-cancel-your-chatgpt-plus-subscription',
|
||||||
|
'챗GPT':
|
||||||
|
'https://help.openai.com/en/articles/7730211-manage-or-cancel-your-chatgpt-plus-subscription',
|
||||||
|
'claude':
|
||||||
|
'https://help.anthropic.com/en/articles/8798313-how-do-i-cancel-my-claude-subscription',
|
||||||
|
'클로드':
|
||||||
|
'https://help.anthropic.com/en/articles/8798313-how-do-i-cancel-my-claude-subscription',
|
||||||
|
'midjourney': 'https://docs.midjourney.com/docs/manage-subscription',
|
||||||
|
'미드저니': 'https://docs.midjourney.com/docs/manage-subscription',
|
||||||
|
|
||||||
|
// 프로그래밍 / 개발 서비스 해지 안내 페이지
|
||||||
|
'github':
|
||||||
|
'https://docs.github.com/ko/billing/managing-billing-for-your-github-account/downgrading-your-github-subscription',
|
||||||
|
'깃허브':
|
||||||
|
'https://docs.github.com/ko/billing/managing-billing-for-your-github-account/downgrading-your-github-subscription',
|
||||||
|
'jetbrains':
|
||||||
|
'https://sales.jetbrains.com/hc/en-gb/articles/207240845-How-to-cancel-an-auto-renewal-subscription-',
|
||||||
|
'제트브레인스':
|
||||||
|
'https://sales.jetbrains.com/hc/en-gb/articles/207240845-How-to-cancel-an-auto-renewal-subscription-',
|
||||||
|
|
||||||
|
// 오피스 및 협업 툴 해지 안내 페이지
|
||||||
|
'microsoft 365':
|
||||||
|
'https://support.microsoft.com/en-us/office/cancel-a-microsoft-365-subscription-46e2634c-c64b-4c65-94b9-2cc9c960e91b',
|
||||||
|
'마이크로소프트 365':
|
||||||
|
'https://support.microsoft.com/en-us/office/cancel-a-microsoft-365-subscription-46e2634c-c64b-4c65-94b9-2cc9c960e91b',
|
||||||
|
'office 365':
|
||||||
|
'https://support.microsoft.com/en-us/office/cancel-a-microsoft-365-subscription-46e2634c-c64b-4c65-94b9-2cc9c960e91b',
|
||||||
|
'오피스 365':
|
||||||
|
'https://support.microsoft.com/en-us/office/cancel-a-microsoft-365-subscription-46e2634c-c64b-4c65-94b9-2cc9c960e91b',
|
||||||
|
'slack':
|
||||||
|
'https://slack.com/help/articles/360003378691-Cancel-your-Slack-subscription',
|
||||||
|
'슬랙':
|
||||||
|
'https://slack.com/help/articles/360003378691-Cancel-your-Slack-subscription',
|
||||||
|
'notion':
|
||||||
|
'https://www.notion.so/help/billing-and-payment-settings#cancel-a-subscription',
|
||||||
|
'노션':
|
||||||
|
'https://www.notion.so/help/billing-and-payment-settings#cancel-a-subscription',
|
||||||
|
'dropbox': 'https://help.dropbox.com/accounts-billing/cancellation',
|
||||||
|
'드롭박스': 'https://help.dropbox.com/accounts-billing/cancellation',
|
||||||
|
'adobe creative cloud':
|
||||||
|
'https://helpx.adobe.com/manage-account/using/cancel-subscription.html',
|
||||||
|
'어도비 크리에이티브 클라우드':
|
||||||
|
'https://helpx.adobe.com/manage-account/using/cancel-subscription.html',
|
||||||
|
|
||||||
|
// 기타 유명 서비스 해지 안내 페이지
|
||||||
|
'google one': 'https://support.google.com/googleone/answer/9140429',
|
||||||
|
'구글 원': 'https://support.google.com/googleone/answer/9140429',
|
||||||
|
'icloud': 'https://support.apple.com/ko-kr/HT207594',
|
||||||
|
'아이클라우드': 'https://support.apple.com/ko-kr/HT207594',
|
||||||
|
'nintendo switch online':
|
||||||
|
'https://en-americas-support.nintendo.com/app/answers/detail/a_id/41925/~/how-to-cancel-a-nintendo-switch-online-membership',
|
||||||
|
'닌텐도 스위치 온라인':
|
||||||
|
'https://en-americas-support.nintendo.com/app/answers/detail/a_id/41925/~/how-to-cancel-a-nintendo-switch-online-membership',
|
||||||
|
'playstation plus':
|
||||||
|
'https://www.playstation.com/support/subscriptions/cancel-playstation-plus/',
|
||||||
|
'플레이스테이션 플러스':
|
||||||
|
'https://www.playstation.com/support/subscriptions/cancel-playstation-plus/',
|
||||||
|
'xbox game pass':
|
||||||
|
'https://support.xbox.com/help/subscriptions-billing/manage-subscriptions/xbox-game-pass-how-to-cancel',
|
||||||
|
'엑스박스 게임 패스':
|
||||||
|
'https://support.xbox.com/help/subscriptions-billing/manage-subscriptions/xbox-game-pass-how-to-cancel',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 모든 서비스 매핑을 합친 맵
|
||||||
|
static Map<String, String> get allServices => {
|
||||||
|
...ottServices,
|
||||||
|
...musicServices,
|
||||||
|
...storageServices,
|
||||||
|
...aiServices,
|
||||||
|
...programmingServices,
|
||||||
|
...officeTools,
|
||||||
|
...lifestyleServices,
|
||||||
|
...shoppingServices,
|
||||||
|
...telecomServices,
|
||||||
|
...otherServices,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 서비스 카테고리 찾기
|
||||||
|
static String? getCategoryForService(String serviceName) {
|
||||||
|
final lowerName = serviceName.toLowerCase();
|
||||||
|
|
||||||
|
if (ottServices.containsKey(lowerName)) return 'ott';
|
||||||
|
if (musicServices.containsKey(lowerName)) return 'music';
|
||||||
|
if (storageServices.containsKey(lowerName)) return 'storage';
|
||||||
|
if (aiServices.containsKey(lowerName)) return 'ai';
|
||||||
|
if (programmingServices.containsKey(lowerName)) return 'programming';
|
||||||
|
if (officeTools.containsKey(lowerName)) return 'office';
|
||||||
|
if (lifestyleServices.containsKey(lowerName)) return 'lifestyle';
|
||||||
|
if (shoppingServices.containsKey(lowerName)) return 'shopping';
|
||||||
|
if (telecomServices.containsKey(lowerName)) return 'telecom';
|
||||||
|
if (otherServices.containsKey(lowerName)) return 'other';
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
lib/services/url_matcher/models/service_info.dart
Normal file
20
lib/services/url_matcher/models/service_info.dart
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
/// 서비스 정보를 담는 데이터 클래스
|
||||||
|
class ServiceInfo {
|
||||||
|
final String serviceId;
|
||||||
|
final String serviceName;
|
||||||
|
final String? serviceUrl;
|
||||||
|
final String? cancellationUrl;
|
||||||
|
final String categoryId;
|
||||||
|
final String categoryNameKr;
|
||||||
|
final String categoryNameEn;
|
||||||
|
|
||||||
|
ServiceInfo({
|
||||||
|
required this.serviceId,
|
||||||
|
required this.serviceName,
|
||||||
|
this.serviceUrl,
|
||||||
|
this.cancellationUrl,
|
||||||
|
required this.categoryId,
|
||||||
|
required this.categoryNameKr,
|
||||||
|
required this.categoryNameEn,
|
||||||
|
});
|
||||||
|
}
|
||||||
2
lib/services/url_matcher/url_matcher.dart
Normal file
2
lib/services/url_matcher/url_matcher.dart
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/// URL Matcher 패키지의 export 파일
|
||||||
|
export 'models/service_info.dart';
|
||||||
@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
|
|||||||
import '../models/subscription_model.dart';
|
import '../models/subscription_model.dart';
|
||||||
import '../providers/category_provider.dart';
|
import '../providers/category_provider.dart';
|
||||||
import '../services/subscription_url_matcher.dart';
|
import '../services/subscription_url_matcher.dart';
|
||||||
|
import '../services/url_matcher/data/legacy_service_data.dart';
|
||||||
|
|
||||||
/// 구독 서비스를 카테고리별로 구분하는 도우미 클래스
|
/// 구독 서비스를 카테고리별로 구분하는 도우미 클래스
|
||||||
class SubscriptionCategoryHelper {
|
class SubscriptionCategoryHelper {
|
||||||
@@ -38,7 +39,7 @@ class SubscriptionCategoryHelper {
|
|||||||
// 카테고리 ID가 없거나 카테고리를 찾을 수 없는 경우 서비스 이름 기반 분류
|
// 카테고리 ID가 없거나 카테고리를 찾을 수 없는 경우 서비스 이름 기반 분류
|
||||||
// 음악
|
// 음악
|
||||||
if (_isInCategory(
|
if (_isInCategory(
|
||||||
subscription.serviceName, SubscriptionUrlMatcher.musicServices)) {
|
subscription.serviceName, LegacyServiceData.musicServices)) {
|
||||||
if (!categorizedSubscriptions.containsKey('music')) {
|
if (!categorizedSubscriptions.containsKey('music')) {
|
||||||
categorizedSubscriptions['music'] = [];
|
categorizedSubscriptions['music'] = [];
|
||||||
}
|
}
|
||||||
@@ -46,7 +47,7 @@ class SubscriptionCategoryHelper {
|
|||||||
}
|
}
|
||||||
// OTT(동영상)
|
// OTT(동영상)
|
||||||
else if (_isInCategory(
|
else if (_isInCategory(
|
||||||
subscription.serviceName, SubscriptionUrlMatcher.ottServices)) {
|
subscription.serviceName, LegacyServiceData.ottServices)) {
|
||||||
if (!categorizedSubscriptions.containsKey('ottVideo')) {
|
if (!categorizedSubscriptions.containsKey('ottVideo')) {
|
||||||
categorizedSubscriptions['ottVideo'] = [];
|
categorizedSubscriptions['ottVideo'] = [];
|
||||||
}
|
}
|
||||||
@@ -54,7 +55,7 @@ class SubscriptionCategoryHelper {
|
|||||||
}
|
}
|
||||||
// 저장/클라우드
|
// 저장/클라우드
|
||||||
else if (_isInCategory(
|
else if (_isInCategory(
|
||||||
subscription.serviceName, SubscriptionUrlMatcher.storageServices)) {
|
subscription.serviceName, LegacyServiceData.storageServices)) {
|
||||||
if (!categorizedSubscriptions.containsKey('storageCloud')) {
|
if (!categorizedSubscriptions.containsKey('storageCloud')) {
|
||||||
categorizedSubscriptions['storageCloud'] = [];
|
categorizedSubscriptions['storageCloud'] = [];
|
||||||
}
|
}
|
||||||
@@ -62,7 +63,7 @@ class SubscriptionCategoryHelper {
|
|||||||
}
|
}
|
||||||
// 통신 · 인터넷 · TV
|
// 통신 · 인터넷 · TV
|
||||||
else if (_isInCategory(
|
else if (_isInCategory(
|
||||||
subscription.serviceName, SubscriptionUrlMatcher.telecomServices)) {
|
subscription.serviceName, LegacyServiceData.telecomServices)) {
|
||||||
if (!categorizedSubscriptions.containsKey('telecomInternetTv')) {
|
if (!categorizedSubscriptions.containsKey('telecomInternetTv')) {
|
||||||
categorizedSubscriptions['telecomInternetTv'] = [];
|
categorizedSubscriptions['telecomInternetTv'] = [];
|
||||||
}
|
}
|
||||||
@@ -70,7 +71,7 @@ class SubscriptionCategoryHelper {
|
|||||||
}
|
}
|
||||||
// 생활/라이프스타일
|
// 생활/라이프스타일
|
||||||
else if (_isInCategory(
|
else if (_isInCategory(
|
||||||
subscription.serviceName, SubscriptionUrlMatcher.lifestyleServices)) {
|
subscription.serviceName, LegacyServiceData.lifestyleServices)) {
|
||||||
if (!categorizedSubscriptions.containsKey('lifestyle')) {
|
if (!categorizedSubscriptions.containsKey('lifestyle')) {
|
||||||
categorizedSubscriptions['lifestyle'] = [];
|
categorizedSubscriptions['lifestyle'] = [];
|
||||||
}
|
}
|
||||||
@@ -78,7 +79,7 @@ class SubscriptionCategoryHelper {
|
|||||||
}
|
}
|
||||||
// 쇼핑/이커머스
|
// 쇼핑/이커머스
|
||||||
else if (_isInCategory(
|
else if (_isInCategory(
|
||||||
subscription.serviceName, SubscriptionUrlMatcher.shoppingServices)) {
|
subscription.serviceName, LegacyServiceData.shoppingServices)) {
|
||||||
if (!categorizedSubscriptions.containsKey('shoppingEcommerce')) {
|
if (!categorizedSubscriptions.containsKey('shoppingEcommerce')) {
|
||||||
categorizedSubscriptions['shoppingEcommerce'] = [];
|
categorizedSubscriptions['shoppingEcommerce'] = [];
|
||||||
}
|
}
|
||||||
@@ -86,7 +87,7 @@ class SubscriptionCategoryHelper {
|
|||||||
}
|
}
|
||||||
// 프로그래밍
|
// 프로그래밍
|
||||||
else if (_isInCategory(subscription.serviceName,
|
else if (_isInCategory(subscription.serviceName,
|
||||||
SubscriptionUrlMatcher.programmingServices)) {
|
LegacyServiceData.programmingServices)) {
|
||||||
if (!categorizedSubscriptions.containsKey('programming')) {
|
if (!categorizedSubscriptions.containsKey('programming')) {
|
||||||
categorizedSubscriptions['programming'] = [];
|
categorizedSubscriptions['programming'] = [];
|
||||||
}
|
}
|
||||||
@@ -94,7 +95,7 @@ class SubscriptionCategoryHelper {
|
|||||||
}
|
}
|
||||||
// 협업/오피스
|
// 협업/오피스
|
||||||
else if (_isInCategory(
|
else if (_isInCategory(
|
||||||
subscription.serviceName, SubscriptionUrlMatcher.officeTools)) {
|
subscription.serviceName, LegacyServiceData.officeTools)) {
|
||||||
if (!categorizedSubscriptions.containsKey('collaborationOffice')) {
|
if (!categorizedSubscriptions.containsKey('collaborationOffice')) {
|
||||||
categorizedSubscriptions['collaborationOffice'] = [];
|
categorizedSubscriptions['collaborationOffice'] = [];
|
||||||
}
|
}
|
||||||
@@ -102,7 +103,7 @@ class SubscriptionCategoryHelper {
|
|||||||
}
|
}
|
||||||
// AI 서비스
|
// AI 서비스
|
||||||
else if (_isInCategory(
|
else if (_isInCategory(
|
||||||
subscription.serviceName, SubscriptionUrlMatcher.aiServices)) {
|
subscription.serviceName, LegacyServiceData.aiServices)) {
|
||||||
if (!categorizedSubscriptions.containsKey('aiService')) {
|
if (!categorizedSubscriptions.containsKey('aiService')) {
|
||||||
categorizedSubscriptions['aiService'] = [];
|
categorizedSubscriptions['aiService'] = [];
|
||||||
}
|
}
|
||||||
@@ -110,7 +111,7 @@ class SubscriptionCategoryHelper {
|
|||||||
}
|
}
|
||||||
// 기타
|
// 기타
|
||||||
else if (_isInCategory(
|
else if (_isInCategory(
|
||||||
subscription.serviceName, SubscriptionUrlMatcher.otherServices)) {
|
subscription.serviceName, LegacyServiceData.otherServices)) {
|
||||||
if (!categorizedSubscriptions.containsKey('other')) {
|
if (!categorizedSubscriptions.containsKey('other')) {
|
||||||
categorizedSubscriptions['other'] = [];
|
categorizedSubscriptions['other'] = [];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user