4 changed files with 140 additions and 0 deletions
@ -0,0 +1,63 @@ |
|||
public class AnalyzeCommand implements Command { |
|||
|
|||
private static final Pattern URL_PATTERN = |
|||
Pattern.compile("^(https?://)?([\\w-]+\\.)+[\\w-]+(/[\\w-./?%&=]*)*$"); |
|||
|
|||
private final ConsoleView view; |
|||
private final StrategyFactory strategyFactory; |
|||
|
|||
public AnalyzeCommand(ConsoleView view, StrategyFactory strategyFactory) { |
|||
this.view = view; |
|||
this.strategyFactory = strategyFactory; |
|||
} |
|||
|
|||
@Override |
|||
public String getName() { |
|||
return "analyze"; |
|||
} |
|||
|
|||
@Override |
|||
public void execute(String[] args, List<Article> unused) { |
|||
|
|||
if (args.length < 2) { |
|||
view.printError("用法:analyze <url>"); |
|||
return; |
|||
} |
|||
|
|||
String url = args[1]; |
|||
|
|||
|
|||
if (!isValidUrl(url)) { |
|||
view.printError("无效的URL格式:" + url); |
|||
return; |
|||
} |
|||
|
|||
try { |
|||
|
|||
List<Article> parsedArticles = strategyFactory.getStrategy(url).crawl(url); |
|||
|
|||
|
|||
printAnalysisResult(url, parsedArticles); |
|||
|
|||
} catch (Exception e) { |
|||
view.printError("解析失败:" + e.getMessage()); |
|||
} |
|||
} |
|||
|
|||
private void printAnalysisResult(String url, List<Article> articles) { |
|||
view.printInfo("解析统计结果 "); |
|||
view.printInfo("目标 URL:" + url); |
|||
view.printInfo("解析到文章数量:" + articles.size()); |
|||
|
|||
if (!articles.isEmpty()) { |
|||
Article first = articles.get(0); |
|||
view.printInfo("首篇文章标题:" + first.getTitle()); |
|||
view.printInfo("首篇文章作者:" + first.getAuthor()); |
|||
view.printInfo("首篇发布日期:" + first.getPublishDate()); |
|||
} |
|||
} |
|||
|
|||
private boolean isValidUrl(String url) { |
|||
return url != null && URL_PATTERN.matcher(url).matches(); |
|||
} |
|||
} |
|||
@ -0,0 +1,35 @@ |
|||
package w10; |
|||
public class ArticleRepository { |
|||
private final List<Article> articles = new ArrayList<>(); |
|||
|
|||
public void add(Article article) { |
|||
if (article == null) { |
|||
throw new IllegalArgumentException("Article cannot be null"); |
|||
} |
|||
articles.add(article); |
|||
} |
|||
|
|||
public void addAll(List<Article> newArticles) { |
|||
if (newArticles == null) { |
|||
return; |
|||
} |
|||
|
|||
for (Article article : newArticles) { |
|||
if (article != null) { |
|||
articles.add(article); |
|||
} |
|||
} |
|||
} |
|||
|
|||
public List<Article> getAll() { |
|||
return Collections.unmodifiableList(articles); |
|||
} |
|||
|
|||
public int size() { |
|||
return articles.size(); |
|||
} |
|||
|
|||
public void clear() { |
|||
articles.clear(); |
|||
} |
|||
} |
|||
@ -0,0 +1,30 @@ |
|||
package w11; |
|||
|
|||
public class RetryUtils { |
|||
|
|||
|
|||
private static final long BASE_DELAY_MS = 500; |
|||
|
|||
@FunctionalInterface |
|||
public interface RetryTask<T> { |
|||
T run() throws Exception; |
|||
} |
|||
|
|||
|
|||
public static <T> T retry(int maxRetries, RetryTask<T> task) throws Exception { |
|||
int attempt = 0; |
|||
while (true) { |
|||
try { |
|||
return task.run(); |
|||
} catch (Exception e) { |
|||
if (attempt >= maxRetries) { |
|||
throw e; |
|||
} |
|||
|
|||
long delay = BASE_DELAY_MS * (1L << attempt); |
|||
Thread.sleep(delay); |
|||
attempt++; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
package w11; |
|||
|
|||
|
|||
public class UrlFormatException extends RuntimeException { |
|||
|
|||
public UrlFormatException(String message) { |
|||
super(message); |
|||
} |
|||
|
|||
public UrlFormatException(String message, Throwable cause) { |
|||
super(message, cause); |
|||
} |
|||
Loading…
Reference in new issue