diff --git a/w9/AI审计架构.png b/w9/AI审计架构.png
new file mode 100644
index 0000000..b332c7c
Binary files /dev/null and b/w9/AI审计架构.png differ
diff --git a/w9/java-cli/.gitignore b/w9/java-cli/.gitignore
new file mode 100644
index 0000000..92378b3
--- /dev/null
+++ b/w9/java-cli/.gitignore
@@ -0,0 +1,4 @@
+*.jar
+*.class
+*.log
+target/
\ No newline at end of file
diff --git a/w9/java-cli/.idea/compiler.xml b/w9/java-cli/.idea/compiler.xml
new file mode 100644
index 0000000..ff21975
--- /dev/null
+++ b/w9/java-cli/.idea/compiler.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/w9/java-cli/.idea/jarRepositories.xml b/w9/java-cli/.idea/jarRepositories.xml
new file mode 100644
index 0000000..712ab9d
--- /dev/null
+++ b/w9/java-cli/.idea/jarRepositories.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/w9/java-cli/.idea/misc.xml b/w9/java-cli/.idea/misc.xml
new file mode 100644
index 0000000..5e4e294
--- /dev/null
+++ b/w9/java-cli/.idea/misc.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/w9/java-cli/.idea/workspace.xml b/w9/java-cli/.idea/workspace.xml
new file mode 100644
index 0000000..f6fde0c
--- /dev/null
+++ b/w9/java-cli/.idea/workspace.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ "associatedIndex": 1
+}
+
+
+
+
+
+ {
+ "keyToString": {
+ "ModuleVcsDetector.initialDetectionPerformed": "true",
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "RunOnceActivity.typescript.service.memoryLimit.init": "true",
+ "kotlin-language-version-configured": "true",
+ "last_opened_file_path": "D:/桌面/java-cli",
+ "nodejs_package_manager_path": "npm",
+ "vue.rearranger.settings.migration": "true"
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+ 1779980718835
+
+
+ 1779980718835
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/w9/java-cli/pom.xml b/w9/java-cli/pom.xml
new file mode 100644
index 0000000..01bc611
--- /dev/null
+++ b/w9/java-cli/pom.xml
@@ -0,0 +1,45 @@
+
+ 4.0.0
+ com.example
+ datacollect-cli
+ 0.1.0
+
+ 11
+ 11
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.3.0
+
+
+
+ com.example.datacollect.Main
+
+
+
+ jar-with-dependencies
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
+
diff --git a/w9/java-cli/src/main/java/com/example/datacollect/Main.java b/w9/java-cli/src/main/java/com/example/datacollect/Main.java
new file mode 100644
index 0000000..17052ae
--- /dev/null
+++ b/w9/java-cli/src/main/java/com/example/datacollect/Main.java
@@ -0,0 +1,22 @@
+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 articles = new ArrayList<>();
+ List history = new ArrayList<>();
+ CrawlerController controller = new CrawlerController(view, articles, history);
+
+ view.printSuccess("Welcome to CLI Crawler (w9_1)! Type help for commands.");
+ while (true) {
+ controller.handle(view.readLine());
+ }
+ }
+}
diff --git a/w9/java-cli/src/main/java/com/example/datacollect/command/Command.java b/w9/java-cli/src/main/java/com/example/datacollect/command/Command.java
new file mode 100644
index 0000000..24e59a6
--- /dev/null
+++ b/w9/java-cli/src/main/java/com/example/datacollect/command/Command.java
@@ -0,0 +1,9 @@
+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 articles);
+}
diff --git a/w9/java-cli/src/main/java/com/example/datacollect/command/CrawlCommand.java b/w9/java-cli/src/main/java/com/example/datacollect/command/CrawlCommand.java
new file mode 100644
index 0000000..ecb37c4
--- /dev/null
+++ b/w9/java-cli/src/main/java/com/example/datacollect/command/CrawlCommand.java
@@ -0,0 +1,40 @@
+package com.example.datacollect.command;
+
+import com.example.datacollect.model.Article;
+import com.example.datacollect.view.ConsoleView;
+import java.util.List;
+import java.util.regex.Pattern;
+
+public class CrawlCommand implements Command {
+ private final ConsoleView view;
+ private static final Pattern URL_PATTERN = Pattern.compile(
+ "^https?://[a-zA-Z0-9][a-zA-Z0-9-]*\\.[a-zA-Z]{2,}(/[^\\s]*)?$"
+ );
+
+ public CrawlCommand(ConsoleView view) {
+ this.view = view;
+ }
+
+ @Override
+ public String getName() {
+ return "crawl";
+ }
+
+ private boolean isValidUrl(String url) {
+ return url != null && URL_PATTERN.matcher(url).matches();
+ }
+
+ @Override
+ public void execute(String[] args, List articles) {
+ if (args.length < 2) {
+ view.printError("Usage: crawl ");
+ return;
+ }
+ String url = args[1];
+ if (!isValidUrl(url)) {
+ view.printError("Invalid URL format. Only HTTP/HTTPS URLs are allowed.");
+ return;
+ }
+ view.printInfo("Stub: would crawl " + url);
+ }
+}
diff --git a/w9/java-cli/src/main/java/com/example/datacollect/command/ExitCommand.java b/w9/java-cli/src/main/java/com/example/datacollect/command/ExitCommand.java
new file mode 100644
index 0000000..6946d85
--- /dev/null
+++ b/w9/java-cli/src/main/java/com/example/datacollect/command/ExitCommand.java
@@ -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 articles) {
+ view.printSuccess("Bye!");
+ System.exit(0);
+ }
+}
diff --git a/w9/java-cli/src/main/java/com/example/datacollect/command/HelpCommand.java b/w9/java-cli/src/main/java/com/example/datacollect/command/HelpCommand.java
new file mode 100644
index 0000000..f1efcd1
--- /dev/null
+++ b/w9/java-cli/src/main/java/com/example/datacollect/command/HelpCommand.java
@@ -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 articles) {
+ view.printInfo("Commands: crawl , list, help, exit, history, (c as alias for crawl)");
+ }
+}
diff --git a/w9/java-cli/src/main/java/com/example/datacollect/command/HistoryCommand.java b/w9/java-cli/src/main/java/com/example/datacollect/command/HistoryCommand.java
new file mode 100644
index 0000000..570232a
--- /dev/null
+++ b/w9/java-cli/src/main/java/com/example/datacollect/command/HistoryCommand.java
@@ -0,0 +1,31 @@
+package com.example.datacollect.command;
+
+import com.example.datacollect.model.Article;
+import com.example.datacollect.view.ConsoleView;
+import java.util.List;
+
+public class HistoryCommand implements Command {
+ private final ConsoleView view;
+ private final List history;
+
+ public HistoryCommand(ConsoleView view, List history) {
+ this.view = view;
+ this.history = history;
+ }
+
+ @Override
+ public String getName() {
+ return "history";
+ }
+
+ @Override
+ public void execute(String[] args, List articles) {
+ if (history.isEmpty()) {
+ view.printInfo("No command history.");
+ return;
+ }
+ for (int i = 0; i < history.size(); i++) {
+ view.printInfo((i + 1) + ". " + history.get(i));
+ }
+ }
+}
\ No newline at end of file
diff --git a/w9/java-cli/src/main/java/com/example/datacollect/command/ListCommand.java b/w9/java-cli/src/main/java/com/example/datacollect/command/ListCommand.java
new file mode 100644
index 0000000..8eba2cd
--- /dev/null
+++ b/w9/java-cli/src/main/java/com/example/datacollect/command/ListCommand.java
@@ -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 articles) {
+ view.display(articles);
+ }
+}
diff --git a/w9/java-cli/src/main/java/com/example/datacollect/controller/CrawlerController.java b/w9/java-cli/src/main/java/com/example/datacollect/controller/CrawlerController.java
new file mode 100644
index 0000000..12b31c9
--- /dev/null
+++ b/w9/java-cli/src/main/java/com/example/datacollect/controller/CrawlerController.java
@@ -0,0 +1,55 @@
+package com.example.datacollect.controller;
+
+import com.example.datacollect.command.Command;
+import com.example.datacollect.command.CrawlCommand;
+import com.example.datacollect.command.ExitCommand;
+import com.example.datacollect.command.HelpCommand;
+import com.example.datacollect.command.HistoryCommand;
+import com.example.datacollect.command.ListCommand;
+import com.example.datacollect.model.Article;
+import com.example.datacollect.view.ConsoleView;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class CrawlerController {
+ private final Map commands = new HashMap<>();
+ private final Map aliases = new HashMap<>();
+ private final ConsoleView view;
+ private final List articles;
+ private final List history;
+
+ public CrawlerController(ConsoleView view, List articles, List history) {
+ this.view = view;
+ this.articles = articles;
+ this.history = history;
+ register(new HelpCommand(view));
+ register(new ListCommand(view));
+ register(new CrawlCommand(view));
+ register(new ExitCommand(view));
+ register(new HistoryCommand(view, history));
+ aliases.put("c", "crawl");
+ }
+
+ private void register(Command command) {
+ commands.put(command.getName(), command);
+ }
+
+ public void handle(String input) {
+ String text = input == null ? "" : input.trim();
+ if (text.isEmpty()) {
+ return;
+ }
+ history.add(text);
+
+ String[] args = text.split("\\s+");
+ String cmdName = args[0].toLowerCase();
+ cmdName = aliases.getOrDefault(cmdName, cmdName);
+ Command command = commands.get(cmdName);
+ if (command == null) {
+ view.printError("Unknown command: " + cmdName);
+ return;
+ }
+ command.execute(args, articles);
+ }
+}
diff --git a/w9/java-cli/src/main/java/com/example/datacollect/model/Article.java b/w9/java-cli/src/main/java/com/example/datacollect/model/Article.java
new file mode 100644
index 0000000..7f4d424
--- /dev/null
+++ b/w9/java-cli/src/main/java/com/example/datacollect/model/Article.java
@@ -0,0 +1,73 @@
+package com.example.datacollect.model;
+
+import java.time.LocalDate;
+
+public class Article {
+ private String title;
+ private String url;
+ private String content;
+ private String author;
+ private LocalDate publishDate;
+
+ public Article(String title, String url, String content) {
+ this.title = title;
+ this.url = url;
+ this.content = content;
+ }
+
+ 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;
+ }
+
+ 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 getAuthor() {
+ return author;
+ }
+
+ public void setAuthor(String author) {
+ this.author = author;
+ }
+
+ public LocalDate getPublishDate() {
+ return publishDate;
+ }
+
+ public void setPublishDate(LocalDate publishDate) {
+ this.publishDate = publishDate;
+ }
+
+ @Override
+ public String toString() {
+ return "Article{"
+ + "title='" + title + '\''
+ + ", url='" + url + '\''
+ + '}';
+ }
+}
diff --git a/w9/java-cli/src/main/java/com/example/datacollect/view/ConsoleView.java b/w9/java-cli/src/main/java/com/example/datacollect/view/ConsoleView.java
new file mode 100644
index 0000000..e52049b
--- /dev/null
+++ b/w9/java-cli/src/main/java/com/example/datacollect/view/ConsoleView.java
@@ -0,0 +1,43 @@
+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 boolean DARK_MODE = true;
+ private static final String ANSI_RESET = "\u001B[0m";
+ private static final String ANSI_GREEN = DARK_MODE ? "\u001B[92m" : "\u001B[32m";
+ private static final String ANSI_RED = DARK_MODE ? "\u001B[91m" : "\u001B[31m";
+ private static final String ANSI_BLUE = DARK_MODE ? "\u001B[94m" : "\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 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());
+ }
+ }
+}
diff --git a/w9/思考题.png b/w9/思考题.png
new file mode 100644
index 0000000..e0d6659
Binary files /dev/null and b/w9/思考题.png differ