Browse Source

上传文件至 'w9'

main
Chengwuyi 3 weeks ago
parent
commit
f020b95d6d
  1. 36
      w9/Article.java
  2. 9
      w9/Command.java
  3. 66
      w9/ConsoleView.java
  4. 60
      w9/CrawlCommand.java
  5. 57
      w9/CrawlerController.java

36
w9/Article.java

@ -0,0 +1,36 @@
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
/**
* Model: 文章实体只存放数据无任何 I/O 代码
*/
public class Article {
private String title;
private String url;
private String content;
private String author; // 作业1:新增 author
private LocalDate publishDate; // 作业1:新增 publishDate
public Article(String title, String url, String content,
String author, LocalDate publishDate) {
this.title = title;
this.url = url;
this.content = content;
this.author = author;
this.publishDate = publishDate;
}
// ── getters ──
public String getTitle() { return title; }
public String getUrl() { return url; }
public String getContent() { return content; }
public String getAuthor() { return author; }
public LocalDate getPublishDate() { return publishDate; }
@Override
public String toString() {
String dateStr = (publishDate != null)
? publishDate.format(DateTimeFormatter.ISO_LOCAL_DATE) : "unknown";
return String.format("[%s] %s (by %s, %s)", url, title, author, dateStr);
}
}

9
w9/Command.java

@ -0,0 +1,9 @@
import java.util.List;
/**
* Command 接口所有命令必须实现
*/
public interface Command {
String getName();
void execute(String[] args, List<Article> articles);
}

66
w9/ConsoleView.java

@ -0,0 +1,66 @@
import java.util.List;
import java.util.Scanner;
/**
* View: 所有 I/O 集中在这里颜色常量统一管理
* 选做暗色主题只需改 THEME_PRIMARY / THEME_SECONDARY 两个常量即可
*/
public class ConsoleView {
// ── ANSI 基础色 ──
private static final String ANSI_RESET = "\033[0m";
private static final String ANSI_GREEN = "\033[32m";
private static final String ANSI_RED = "\033[31m";
private static final String ANSI_YELLOW = "\033[33m";
private static final String ANSI_CYAN = "\033[36m";
private static final String ANSI_BOLD = "\033[1m";
// ── 选做:主题常量(暗色主题只改这两行)──
private static final String THEME_PRIMARY = ANSI_CYAN; // 暗色主题可换 ANSI_GREEN
private static final String THEME_SECONDARY = ANSI_YELLOW; // 暗色主题可换 ANSI_CYAN
private final Scanner scanner = new Scanner(System.in);
public void printSuccess(String msg) {
System.out.println(ANSI_GREEN + msg + ANSI_RESET);
}
public void printError(String msg) {
System.out.println(ANSI_RED + "[ERROR] " + msg + ANSI_RESET);
}
public void printInfo(String msg) {
System.out.println(THEME_PRIMARY + msg + ANSI_RESET);
}
public void printWarning(String msg) {
System.out.println(THEME_SECONDARY + "[WARN] " + msg + ANSI_RESET);
}
public void printBold(String msg) {
System.out.println(ANSI_BOLD + msg + ANSI_RESET);
}
/** 展示文章列表 */
public void display(List<Article> articles) {
if (articles.isEmpty()) {
printWarning("暂无文章,请先使用 crawl <url> 抓取。");
return;
}
printBold("── 文章列表 (" + articles.size() + " 篇) ──");
for (int i = 0; i < articles.size(); i++) {
Article a = articles.get(i);
if (a == null) {
printWarning((i + 1) + ". [null 条目,已跳过]");
continue;
}
System.out.printf(THEME_PRIMARY + "%2d. %s" + ANSI_RESET + "%n", i + 1, a);
}
}
/** 读取一行用户输入,显示提示符 */
public String readLine() {
System.out.print(ANSI_BOLD + "> " + ANSI_RESET);
return scanner.nextLine().trim();
}
}

60
w9/CrawlCommand.java

@ -0,0 +1,60 @@
import java.time.LocalDate;
import java.util.List;
/**
* CrawlCommand: 抓取文章并存入列表
* 选做支持别名 "c" 代替 "crawl"
*/
public class CrawlCommand implements Command {
private final ConsoleView view;
public CrawlCommand(ConsoleView v) { this.view = v; }
@Override public String getName() { return "crawl"; }
// 选做:别名支持(由 Controller 注册两个 key 指向同一实例)
public String getAlias() { return "c"; }
@Override
public void execute(String[] args, List<Article> articles) {
// 参数校验
if (args.length < 2 || args[1].isBlank()) {
view.printError("用法:crawl <url> 或 c <url>");
return;
}
String url = args[1].trim();
// 选做:URL 格式校验
if (!isValidUrl(url)) {
view.printError("URL 格式不合法,请以 http:// 或 https:// 开头:" + url);
return;
}
// Stub:模拟抓取(真实爬取留待后续实现)
// 注意:字符串拼接仅用于构建提示信息(View 层职责),
// 业务数据直接封装进 Article,不在 Command 层做字符串展示拼接。
Article article = new Article(
"抓取文章 - " + extractDomain(url),
url,
"(stub content)",
"unknown",
LocalDate.now()
);
articles.add(article);
view.printSuccess("已抓取:" + article.getTitle() + " [" + url + "]");
}
/** 选做:简单 URL 格式验证 */
private boolean isValidUrl(String url) {
return url.startsWith("http://") || url.startsWith("https://");
}
private String extractDomain(String url) {
try {
String stripped = url.replaceFirst("https?://", "");
int slash = stripped.indexOf('/');
return slash > 0 ? stripped.substring(0, slash) : stripped;
} catch (Exception e) {
return url;
}
}
}

57
w9/CrawlerController.java

@ -0,0 +1,57 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Controller: 解析输入分发命令维护历史记录
* 严格遵守 MVC不持有任何 System.out所有输出走 view
*/
public class CrawlerController {
private final ConsoleView view;
private final List<Article> articles;
private final Map<String, Command> commands = new HashMap<>();
private final List<String> history = new ArrayList<>(); // 作业2
public CrawlerController(ConsoleView view, List<Article> articles) {
this.view = view;
this.articles = articles;
registerCommands();
}
private void registerCommands() {
CrawlCommand crawl = new CrawlCommand(view);
register(crawl.getName(), crawl);
register(crawl.getAlias(), crawl); // 选做:别名 "c"
register(new ListCommand(view));
register(new HelpCommand(view));
register(new ExitCommand(view));
register(new HistoryCommand(view, history)); // 作业2,注入共享 history
}
private void register(Command cmd) {
commands.put(cmd.getName(), cmd);
}
private void register(String key, Command cmd) {
commands.put(key, cmd);
}
/** 处理一行用户输入 */
public void handle(String input) {
if (input == null || input.isBlank()) return;
history.add(input); // 作业2:记录每条命令
String[] parts = input.trim().split("\\s+");
String name = parts[0].toLowerCase();
Command cmd = commands.get(name);
if (cmd == null) {
view.printError("未知命令:" + name + "。输入 help 查看可用命令。");
} else {
cmd.execute(parts, articles);
}
}
}
Loading…
Cancel
Save