10 changed files with 363 additions and 0 deletions
@ -0,0 +1,21 @@ |
|||
package com.example.datacollect; |
|||
|
|||
import com.example.datacollect.controller.CrawlerController; |
|||
import com.example.datacollect.model.Article; |
|||
import com.example.datacollect.view.ConsoleView; |
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
|
|||
public class Main { |
|||
|
|||
public static void main(String[] args) { |
|||
ConsoleView view = new ConsoleView(); |
|||
List<Article> articles = new ArrayList<>(); |
|||
CrawlerController controller = new CrawlerController(view, articles); |
|||
|
|||
view.printSuccess("Welcome to CLI Crawler (w9_1)! Type help for commands."); |
|||
while (true) { |
|||
controller.handle(view.readLine()); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
package com.example.datacollect.command; |
|||
|
|||
import com.example.datacollect.model.Article; |
|||
import java.util.List; |
|||
|
|||
public interface Command { |
|||
String getName(); |
|||
void execute(String[] args, List<Article> articles); |
|||
|
|||
String getDescription(); |
|||
} |
|||
@ -0,0 +1,27 @@ |
|||
package com.example.datacollect.command; |
|||
|
|||
import com.example.datacollect.model.Article; |
|||
import com.example.datacollect.view.ConsoleView; |
|||
import java.util.List; |
|||
|
|||
public class CrawlCommand implements Command { |
|||
private final ConsoleView view; |
|||
|
|||
public CrawlCommand(ConsoleView view) { |
|||
this.view = view; |
|||
} |
|||
|
|||
@Override |
|||
public String getName() { |
|||
return "crawl"; |
|||
} |
|||
|
|||
@Override |
|||
public void execute(String[] args, List<Article> articles) { |
|||
if (args.length < 2) { |
|||
view.printError("Usage: crawl <url>"); |
|||
return; |
|||
} |
|||
view.printInfo("Stub: would crawl " + args[1]); |
|||
} |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
package com.example.datacollect.command; |
|||
|
|||
import com.example.datacollect.model.Article; |
|||
import com.example.datacollect.view.ConsoleView; |
|||
import java.util.List; |
|||
|
|||
public class ExitCommand implements Command { |
|||
private final ConsoleView view; |
|||
|
|||
public ExitCommand(ConsoleView view) { |
|||
this.view = view; |
|||
} |
|||
|
|||
@Override |
|||
public String getName() { |
|||
return "exit"; |
|||
} |
|||
|
|||
@Override |
|||
public void execute(String[] args, List<Article> articles) { |
|||
view.printSuccess("Bye!"); |
|||
System.exit(0); |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
package com.example.datacollect.command; |
|||
|
|||
import com.example.datacollect.model.Article; |
|||
import com.example.datacollect.view.ConsoleView; |
|||
import java.util.List; |
|||
|
|||
public class HelpCommand implements Command { |
|||
private final ConsoleView view; |
|||
|
|||
public HelpCommand(ConsoleView view) { |
|||
this.view = view; |
|||
} |
|||
|
|||
@Override |
|||
public String getName() { |
|||
return "help"; |
|||
} |
|||
|
|||
@Override |
|||
public void execute(String[] args, List<Article> articles) { |
|||
view.printInfo("Commands: crawl <url>, list, help, exit"); |
|||
} |
|||
} |
|||
@ -0,0 +1,47 @@ |
|||
package com.example.datacollect.command; |
|||
import com.example.datacollect.model.Article; |
|||
import com.example.datacollect.view.ConsoleView; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
public class HistoryCommand implements Command { |
|||
// 静态集合,全局记录用户输入的命令
|
|||
private static final List<String> commandHistory = new ArrayList<>(); |
|||
|
|||
// 你们项目里的 View 类(和 ListCommand 一样)
|
|||
private final ConsoleView view; |
|||
|
|||
// 构造方法,接收 View(和 ListCommand 格式一致)
|
|||
public HistoryCommand(ConsoleView view) { |
|||
this.view = view; |
|||
} |
|||
|
|||
@Override |
|||
public String getName() { |
|||
return "history"; |
|||
} |
|||
|
|||
// ✅ 重点:execute 方法必须和 ListCommand 一样,带上 args 和 List<Article>
|
|||
@Override |
|||
public void execute(String[] args, List<Article> articles) { |
|||
if (commandHistory.isEmpty()) { |
|||
view.printInfo("📜 还没有输入过任何命令哦~"); |
|||
return; |
|||
} |
|||
view.printInfo("📜 你的历史命令列表:"); |
|||
for (int i = 0; i < commandHistory.size(); i++) { |
|||
view.printInfo((i + 1) + ". " + commandHistory.get(i)); |
|||
} |
|||
} |
|||
|
|||
@Override |
|||
public String getDescription() { |
|||
return "history - 查看你输入过的所有命令"; |
|||
} |
|||
|
|||
// 静态方法:给 Controller 调用,记录用户输入
|
|||
public static void record(String commandLine) { |
|||
commandHistory.add(commandLine); |
|||
} |
|||
} |
|||
|
|||
@ -0,0 +1,23 @@ |
|||
package com.example.datacollect.command; |
|||
|
|||
import com.example.datacollect.model.Article; |
|||
import com.example.datacollect.view.ConsoleView; |
|||
import java.util.List; |
|||
|
|||
public class ListCommand implements Command { |
|||
private final ConsoleView view; |
|||
|
|||
public ListCommand(ConsoleView view) { |
|||
this.view = view; |
|||
} |
|||
|
|||
@Override |
|||
public String getName() { |
|||
return "list"; |
|||
} |
|||
|
|||
@Override |
|||
public void execute(String[] args, List<Article> articles) { |
|||
view.display(articles); |
|||
} |
|||
} |
|||
@ -0,0 +1,70 @@ |
|||
package com.example.datacollect.controller; |
|||
|
|||
import com.example.datacollect.command.*; |
|||
import com.example.datacollect.model.Article; |
|||
import com.example.datacollect.view.ConsoleView; |
|||
import java.util.HashMap; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Scanner; |
|||
|
|||
|
|||
public class CrawlerController { |
|||
// 成员变量(包含新增的 aliasMap)
|
|||
private final Map<String, Command> commands = new HashMap<>(); |
|||
private final ConsoleView view; |
|||
private final List<Article> articles; |
|||
private final Map<String, String> aliasMap = new HashMap<>(); // 1️⃣ 这里
|
|||
|
|||
// 构造方法
|
|||
public CrawlerController(ConsoleView view, List<Article> articles) { |
|||
this.view = view; |
|||
this.articles = articles; |
|||
|
|||
// 注册命令
|
|||
register(new HelpCommand(view)); |
|||
register(new ListCommand(view)); |
|||
register(new CrawlCommand(view)); |
|||
register(new ExitCommand(view)); |
|||
register(new HistoryCommand(view)); |
|||
|
|||
// 2️⃣ 这里配置别名
|
|||
aliasMap.put("h", "help"); |
|||
aliasMap.put("c", "crawl"); |
|||
aliasMap.put("l", "list"); |
|||
aliasMap.put("hi", "history"); |
|||
aliasMap.put("e", "exit"); |
|||
} |
|||
|
|||
// 原来的 register 方法,不用改
|
|||
private void register(Command command) { |
|||
commands.put(command.getName(), command); |
|||
} |
|||
|
|||
public void handle(String s) { |
|||
Scanner scanner = new Scanner(System.in); |
|||
while (true) { |
|||
System.out.print("> "); |
|||
String input = scanner.nextLine().trim(); |
|||
|
|||
// 3️⃣ 这里记录命令历史
|
|||
HistoryCommand.record(input); |
|||
|
|||
String[] parts = input.split("\\s+", 2); |
|||
String commandName = parts[0]; |
|||
String[] args = parts.length > 1 ? parts[1].split("\\s+") : new String[0]; |
|||
|
|||
// 4️⃣ 这里解析别名
|
|||
if (aliasMap.containsKey(commandName)) { |
|||
commandName = aliasMap.get(commandName); |
|||
} |
|||
|
|||
// 分发命令(原来的逻辑)
|
|||
if (commands.containsKey(commandName)) { |
|||
commands.get(commandName).execute(args, articles); |
|||
} else { |
|||
view.printError("未知命令/别名:" + commandName + ",输入 help 查看所有命令"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,75 @@ |
|||
package com.example.datacollect.model; |
|||
|
|||
public class Article { |
|||
private String title; |
|||
private String url; |
|||
private String content; |
|||
private String author; |
|||
private String publishDate; |
|||
|
|||
|
|||
public Article(String title, String url, String content) { |
|||
this.title = title; |
|||
this.url = url; |
|||
this.content = content; |
|||
this.author=author; |
|||
this.publishDate=publishDate; |
|||
|
|||
} |
|||
|
|||
public String getTitle() { |
|||
return title; |
|||
} |
|||
|
|||
public void setTitle(String title) { |
|||
this.title = title; |
|||
} |
|||
|
|||
public String getUrl() { |
|||
return url; |
|||
} |
|||
|
|||
public void setUrl(String url) { |
|||
this.url = url; |
|||
} |
|||
|
|||
public String getContent() { |
|||
return content; |
|||
} |
|||
|
|||
public void setContent(String content) { |
|||
this.content = content; |
|||
} |
|||
|
|||
public String get() { |
|||
return content; |
|||
} |
|||
|
|||
public String getAuthor() { |
|||
return author; |
|||
} |
|||
public void setAuthor(String author) { |
|||
this.author = author; |
|||
} |
|||
|
|||
public String getPublishDate() { |
|||
return publishDate; |
|||
} |
|||
|
|||
public void setPublishDate(String publishDate) { |
|||
this.publishDate = publishDate; |
|||
} |
|||
|
|||
|
|||
|
|||
@Override |
|||
public String toString() { |
|||
return "Article{" + |
|||
"title='" + title + '\'' + |
|||
", author='" + author + '\'' + |
|||
", publishDate=" + publishDate + |
|||
", url='" + url + '\'' + |
|||
", content='" + content + '\'' + |
|||
'}'; |
|||
} |
|||
} |
|||
@ -0,0 +1,42 @@ |
|||
package com.example.datacollect.view; |
|||
|
|||
import com.example.datacollect.model.Article; |
|||
import java.util.List; |
|||
import java.util.Scanner; |
|||
|
|||
public class ConsoleView { |
|||
private static final String ANSI_RESET = "\u001B[0m"; |
|||
private static final String ANSI_GREEN = "\u001B[32m"; |
|||
private static final String ANSI_RED = "\u001B[31m"; |
|||
private static final String ANSI_BLUE = "\u001B[34m"; |
|||
|
|||
private final Scanner scanner = new Scanner(System.in); |
|||
|
|||
public String readLine() { |
|||
System.out.print("> "); |
|||
return scanner.nextLine(); |
|||
} |
|||
|
|||
public void printSuccess(String msg) { |
|||
System.out.println(ANSI_GREEN + msg + ANSI_RESET); |
|||
} |
|||
|
|||
public void printError(String msg) { |
|||
System.out.println(ANSI_RED + msg + ANSI_RESET); |
|||
} |
|||
|
|||
public void printInfo(String msg) { |
|||
System.out.println(ANSI_BLUE + msg + ANSI_RESET); |
|||
} |
|||
|
|||
public void display(List<Article> articles) { |
|||
if (articles.isEmpty()) { |
|||
printInfo("暂无文章,请先执行 crawl。"); |
|||
return; |
|||
} |
|||
for (int i = 0; i < articles.size(); i++) { |
|||
Article a = articles.get(i); |
|||
System.out.println((i + 1) + ". " + a.getTitle() + " | " + a.getUrl()); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue