From d4eaf9909db10b4248543712188d81c04d2f4935 Mon Sep 17 00:00:00 2001 From: shenjianZ Date: Mon, 12 Jan 2026 21:15:55 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BF=AE=E5=A4=8D=E5=90=8E=E7=AB=AFcor?= =?UTF-8?q?s=EF=BC=9B=E5=A2=9E=E5=8A=A0=E5=89=8D=E7=AB=AF=E8=87=AA?= =?UTF-8?q?=E5=AE=9A=E4=B9=89=E6=A0=87=E9=A2=98=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../newsclassifier/config/SecurityConfig.java | 22 + client/src-tauri/capabilities/default.json | 6 + client/src-tauri/tauri.conf.json | 3 +- client/src/App.vue | 12 +- client/src/components/TitleBar.vue | 170 ++ docs/模块开发任务清单.md | 1665 ----------------- docs/系统流程图.md | 494 ----- docs/系统流程详解.md | 984 ---------- 8 files changed, 211 insertions(+), 3145 deletions(-) create mode 100644 client/src/components/TitleBar.vue delete mode 100644 docs/模块开发任务清单.md delete mode 100644 docs/系统流程图.md delete mode 100644 docs/系统流程详解.md diff --git a/backend/src/main/java/com/aisi/newsclassifier/config/SecurityConfig.java b/backend/src/main/java/com/aisi/newsclassifier/config/SecurityConfig.java index 8c6fcec..d946b0a 100644 --- a/backend/src/main/java/com/aisi/newsclassifier/config/SecurityConfig.java +++ b/backend/src/main/java/com/aisi/newsclassifier/config/SecurityConfig.java @@ -12,6 +12,12 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.CorsConfigurationSource; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; + +import java.util.Arrays; +import java.util.List; @Configuration @EnableMethodSecurity @@ -25,6 +31,7 @@ public class SecurityConfig { public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .csrf(AbstractHttpConfigurer::disable) + .cors(cors -> cors.configurationSource(corsConfigurationSource())) .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)) .authorizeHttpRequests(auth -> auth .requestMatchers("/v3/api-docs/**","/swagger-ui/**", "/swagger-ui.html").permitAll() @@ -39,6 +46,21 @@ public class SecurityConfig { return http.build(); } + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOriginPatterns(List.of("*")); + configuration.setAllowedMethods(List.of("*")); + configuration.setAllowedHeaders(List.of("*")); + configuration.setAllowCredentials(true); + configuration.setExposedHeaders(List.of("*")); + configuration.setMaxAge(3600L); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + return source; + } + @Bean public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); diff --git a/client/src-tauri/capabilities/default.json b/client/src-tauri/capabilities/default.json index d0d7558..7a40984 100644 --- a/client/src-tauri/capabilities/default.json +++ b/client/src-tauri/capabilities/default.json @@ -7,6 +7,12 @@ ], "permissions": [ "core:default", + "core:window:allow-minimize", + "core:window:allow-maximize", + "core:window:allow-unmaximize", + "core:window:allow-close", + "core:window:allow-is-maximized", + "core:window:allow-toggle-maximize", "opener:default", { "identifier": "http:default", diff --git a/client/src-tauri/tauri.conf.json b/client/src-tauri/tauri.conf.json index 02991d1..796bae6 100644 --- a/client/src-tauri/tauri.conf.json +++ b/client/src-tauri/tauri.conf.json @@ -19,7 +19,8 @@ "minHeight": 500, "resizable": true, "fullscreen": false, - "decorations": true, + "decorations": false, + "transparent": false, "alwaysOnTop": false, "center": true } diff --git a/client/src/App.vue b/client/src/App.vue index 771a0f3..cb58276 100644 --- a/client/src/App.vue +++ b/client/src/App.vue @@ -1,6 +1,7 @@ diff --git a/docs/模块开发任务清单.md b/docs/模块开发任务清单.md deleted file mode 100644 index 7351c52..0000000 --- a/docs/模块开发任务清单.md +++ /dev/null @@ -1,1665 +0,0 @@ -# 新闻文本分类系统 - 模块开发任务清单 - -> 本文档详细列出每个模块需要完成的具体代码任务,参考现有工程结构。 -> 注意:爬虫模块使用 Python 实现,而非 Java。 - ---- - -## 目录 - -1. [爬虫模块 (Python)](#1-爬虫模块-python) -2. [后端服务模块 (Spring Boot)](#2-后端服务模块-spring-boot) -3. [前端桌面模块 (Tauri + Vue3)](#3-前端桌面模块-tauri--vue3) -4. [机器学习分类模块 (Python)](#4-机器学习分类模块-python) - ---- - -## 1. 爬虫模块 (Python) - - - -## 2. 后端服务模块 (Spring Boot) - -### 模块目录结构 - - -``` - -#### 任务 2.1.10: `application.yml` - 应用配置文件 - -```yaml -spring: - application: - name: news-classifier - datasource: - driver-class-name: com.mysql.cj.jdbc.Driver - url: jdbc:mysql://localhost:3306/news_classifier?useUnicode=true&characterEncoding=utf8mb4&serverTimezone=Asia/Shanghai - username: root - password: your_password - data: - redis: - host: localhost - port: 6379 - database: 0 - -# MyBatis-Plus配置 -mybatis-plus: - mapper-locations: classpath:mapper/**/*.xml - type-aliases-package: com.newsclassifier.entity - configuration: - map-underscore-to-camel-case: true - log-impl: org.apache.ibatis.logging.stdout.StdOutImpl - global-config: - db-config: - id-type: auto - logic-delete-field: deleted - logic-delete-value: 1 - logic-not-delete-value: 0 - -# JWT配置 -jwt: - secret: your-secret-key-at-least-256-bits-long-for-hs256-algorithm - expiration: 86400000 - -# 分类器配置 -classifier: - mode: hybrid # traditional, deep_learning, hybrid - confidence: - threshold: 0.75 - hybrid-min: 0.6 - bert: - service-url: http://localhost:5000/api/predict - timeout: 5000 - -# 日志配置 -logging: - level: - com.newsclassifier: debug - pattern: - console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" -``` - ---- - -## 3. 前端桌面模块 (Tauri + Vue3) - -### 模块目录结构 - -``` -client/src/ -├── api/ # API接口 -│ ├── index.ts -│ ├── auth.ts -│ ├── news.ts -│ ├── category.ts -│ └── classifier.ts -├── assets/ # 静态资源 -│ ├── images/ -│ ├── styles/ -│ │ ├── main.css -│ │ └── tailwind.css -│ └── fonts/ -├── components/ # 组件 -│ ├── ui/ # 基础UI组件 -│ │ ├── button/ -│ │ ├── input/ -│ │ ├── dialog/ -│ │ └── table/ -│ ├── layout/ # 布局组件 -│ │ ├── Header.vue -│ │ ├── Sidebar.vue -│ │ └── Footer.vue -│ ├── news/ # 新闻相关组件 -│ │ ├── NewsCard.vue -│ │ ├── NewsList.vue -│ │ ├── NewsDetail.vue -│ │ └── CategoryFilter.vue -│ └── charts/ # 图表组件 -│ ├── CategoryChart.vue -│ └── TrendChart.vue -├── composables/ # 组合式函数 -│ ├── useAuth.ts -│ ├── useNews.ts -│ ├── useClassifier.ts -│ └── useToast.ts -├── layouts/ # 布局 -│ ├── DefaultLayout.vue -│ ├── AuthLayout.vue -│ └── EmptyLayout.vue -├── router/ # 路由 (部分完成) -│ └── index.ts -├── stores/ # 状态管理 (部分完成) -│ ├── user.ts -│ ├── news.ts -│ └── category.ts -├── types/ # TypeScript类型 -│ ├── api.d.ts -│ ├── news.d.ts -│ └── user.d.ts -├── utils/ # 工具函数 -│ ├── request.ts -│ ├── storage.ts -│ ├── format.ts -│ └── validate.ts -├── views/ # 页面 -│ ├── auth/ -│ │ ├── Login.vue -│ │ └── Register.vue -│ ├── news/ -│ │ ├── NewsList.vue -│ │ ├── NewsDetail.vue -│ │ └── NewsSearch.vue -│ ├── category/ -│ │ ├── CategoryManage.vue -│ │ └── CategoryStats.vue -│ ├── classifier/ -│ │ ├── ClassifierPage.vue -│ │ └── ModelCompare.vue -│ └── admin/ -│ ├── Dashboard.vue -│ ├── UserManage.vue -│ └── SystemLog.vue -├── App.vue -└── main.ts -``` - -### 3.1 需要完成的具体文件 - -#### 任务 3.1.1: `utils/request.ts` - HTTP请求封装 - -```typescript -import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios' - -// 响应数据类型 -interface ApiResponse { - code: number - message: string - data: T -} - -// 创建axios实例 -const service: AxiosInstance = axios.create({ - baseURL: import.meta.env.VITE_API_BASE_URL || 'http://localhost:8080/api', - timeout: 15000, - headers: { - 'Content-Type': 'application/json' - } -}) - -// 请求拦截器 -service.interceptors.request.use( - (config) => { - const token = localStorage.getItem('token') - if (token) { - config.headers.Authorization = `Bearer ${token}` - } - return config - }, - (error) => { - return Promise.reject(error) - } -) - -// 响应拦截器 -service.interceptors.response.use( - (response: AxiosResponse) => { - const { code, message, data } = response.data - - if (code === 200) { - return data - } else { - // 处理错误 - return Promise.reject(new Error(message || '请求失败')) - } - }, - (error) => { - // 处理HTTP错误 - return Promise.reject(error) - } -) - -// 封装请求方法 -export const http = { - get(url: string, config?: AxiosRequestConfig): Promise { - return service.get(url, config) - }, - - post(url: string, data?: any, config?: AxiosRequestConfig): Promise { - return service.post(url, data, config) - }, - - put(url: string, data?: any, config?: AxiosRequestConfig): Promise { - return service.put(url, data, config) - }, - - delete(url: string, config?: AxiosRequestConfig): Promise { - return service.delete(url, config) - } -} - -export default service -``` - -#### 任务 3.1.2: `api/news.ts` - 新闻API - -```typescript -import { http } from './request' - -// 新闻查询参数 -export interface NewsQueryParams { - page?: number - size?: number - categoryId?: number - categoryCode?: string - keyword?: string - status?: number -} - -// 新闻详情 -export interface NewsDetail { - id: number - title: string - content: string - summary: string - source: string - sourceUrl: string - author: string - categoryId: number - categoryCode: string - coverImage: string - publishTime: string - viewCount: number - likeCount: number - commentCount: number - classifierType: string - confidence: number -} - -// 分页响应 -export interface PageResponse { - total: number - records: T[] - current: number - size: number -} - -// 新闻API -export const newsApi = { - // 分页查询 - getNewsPage(params: NewsQueryParams): Promise> { - return http.get('/news/page', { params }) - }, - - // 获取详情 - getNewsDetail(id: number): Promise { - return http.get(`/news/${id}`) - }, - - // 搜索新闻 - searchNews(keyword: string, page = 1, size = 20): Promise> { - return http.get('/news/search', { params: { keyword, page, size } }) - }, - - // 手动分类 - manualClassify(id: number, categoryId: number): Promise { - return http.post(`/news/${id}/classify`, null, { params: { categoryId } }) - } -} -``` - -#### 任务 3.1.3: `composables/useNews.ts` - 新闻组合式函数 - -```typescript -import { ref, computed } from 'vue' -import { newsApi, type NewsQueryParams, type NewsDetail, type PageResponse } from '@/api/news' - -export function useNews() { - const loading = ref(false) - const newsList = ref([]) - const total = ref(0) - const currentNews = ref(null) - - // 分页查询 - const fetchNewsPage = async (params: NewsQueryParams) => { - loading.value = true - try { - const result: PageResponse = await newsApi.getNewsPage(params) - newsList.value = result.records - total.value = result.total - } catch (error) { - console.error('获取新闻列表失败:', error) - throw error - } finally { - loading.value = false - } - } - - // 获取详情 - const fetchNewsDetail = async (id: number) => { - loading.value = true - try { - currentNews.value = await newsApi.getNewsDetail(id) - return currentNews.value - } catch (error) { - console.error('获取新闻详情失败:', error) - throw error - } finally { - loading.value = false - } - } - - // 搜索新闻 - const searchNews = async (keyword: string, page = 1, size = 20) => { - loading.value = true - try { - const result: PageResponse = await newsApi.searchNews(keyword, page, size) - newsList.value = result.records - total.value = result.total - } catch (error) { - console.error('搜索新闻失败:', error) - throw error - } finally { - loading.value = false - } - } - - return { - loading: computed(() => loading.value), - newsList: computed(() => newsList.value), - total: computed(() => total.value), - currentNews: computed(() => currentNews.value), - fetchNewsPage, - fetchNewsDetail, - searchNews - } -} -``` - -#### 任务 3.1.4: `views/news/NewsList.vue` - 新闻列表页面 - -```vue - - - - - -``` - -#### 任务 3.1.5: `views/classifier/ClassifierPage.vue` - 分类器页面 - -```vue - - - - - -``` - -#### 任务 3.1.6: `router/index.ts` - 路由配置 (更新) - -```typescript -import { createRouter, createWebHistory } from 'vue-router' -import type { RouteRecordRaw } from 'vue-router' - -const routes: RouteRecordRaw[] = [ - { - path: '/login', - name: 'Login', - component: () => import('@/views/auth/Login.vue'), - meta: { layout: 'EmptyLayout' } - }, - { - path: '/', - name: 'Home', - component: () => import('@/views/news/NewsList.vue'), - meta: { requiresAuth: true } - }, - { - path: '/news', - name: 'NewsList', - component: () => import('@/views/news/NewsList.vue'), - meta: { requiresAuth: true } - }, - { - path: '/news/:id', - name: 'NewsDetail', - component: () => import('@/views/news/NewsDetail.vue'), - meta: { requiresAuth: true } - }, - { - path: '/classifier', - name: 'Classifier', - component: () => import('@/views/classifier/ClassifierPage.vue'), - meta: { requiresAuth: true } - }, - { - path: '/category', - name: 'CategoryStats', - component: () => import('@/views/category/CategoryStats.vue'), - meta: { requiresAuth: true } - }, - { - path: '/admin', - name: 'AdminDashboard', - component: () => import('@/views/admin/Dashboard.vue'), - meta: { requiresAuth: true, requiresAdmin: true } - } -] - -const router = createRouter({ - history: createWebHistory(), - routes -}) - -// 路由守卫 -router.beforeEach((to, from, next) => { - const token = localStorage.getItem('token') - - if (to.meta.requiresAuth && !token) { - next('/login') - } else if (to.meta.requiresAdmin) { - // 检查管理员权限 - const userRole = localStorage.getItem('userRole') - if (userRole !== 'ADMIN') { - next('/') - } else { - next() - } - } else { - next() - } -}) - -export default router -``` - ---- - -## 4. 机器学习分类模块 (Python) - -### 模块目录结构 - -``` -ml-module/ -├── data/ -│ ├── raw/ # 原始数据 -│ ├── processed/ # 处理后的数据 -│ │ ├── training_data.csv -│ │ └── test_data.csv -│ └── external/ # 外部数据集 -├── models/ # 训练好的模型 -│ ├── traditional/ -│ │ ├── nb_vectorizer.pkl -│ │ ├── nb_classifier.pkl -│ │ ├── svm_vectorizer.pkl -│ │ └── svm_classifier.pkl -│ ├── deep_learning/ -│ │ └── bert_finetuned/ -│ └── hybrid/ -│ └── config.json -├── src/ -│ ├── __init__.py -│ ├── traditional/ # 传统机器学习 -│ │ ├── __init__.py -│ │ ├── train_model.py # (已有) -│ │ ├── predict.py -│ │ └── evaluate.py -│ ├── deep_learning/ # 深度学习 -│ │ ├── __init__.py -│ │ ├── bert_model.py -│ │ ├── train_bert.py -│ │ └── predict_bert.py -│ ├── hybrid/ # 混合策略 -│ │ ├── __init__.py -│ │ ├── hybrid_classifier.py -│ │ └── rule_engine.py -│ ├── utils/ -│ │ ├── __init__.py -│ │ ├── preprocessing.py # 数据预处理 -│ │ └── metrics.py # 评估指标 -│ └── api/ # API服务 -│ ├── __init__.py -│ └── server.py # FastAPI服务 -├── notebooks/ # Jupyter notebooks -│ ├── data_exploration.ipynb -│ └── model_comparison.ipynb -├── tests/ # 测试 -│ ├── test_traditional.py -│ ├── test_bert.py -│ └── test_hybrid.py -├── requirements.txt -├── setup.py -└── README.md -``` - -### 4.1 需要完成的具体文件 - -#### 任务 4.1.1: `src/traditional/predict.py` - 传统模型预测 - -```python -""" -传统机器学习模型预测 -""" - -import os -import joblib -import jieba -from typing import Dict, Any - -# 分类映射 -CATEGORY_MAP = { - 'POLITICS': '时政', - 'FINANCE': '财经', - 'TECHNOLOGY': '科技', - 'SPORTS': '体育', - 'ENTERTAINMENT': '娱乐', - 'HEALTH': '健康', - 'EDUCATION': '教育', - 'LIFE': '生活', - 'INTERNATIONAL': '国际', - 'MILITARY': '军事' -} - - -class TraditionalPredictor: - """传统机器学习预测器""" - - def __init__(self, model_type='nb', model_dir='../../models/traditional'): - self.model_type = model_type - self.model_dir = model_dir - self.vectorizer = None - self.classifier = None - self._load_model() - - def _load_model(self): - """加载模型""" - vectorizer_path = os.path.join(self.model_dir, f'{self.model_type}_vectorizer.pkl') - classifier_path = os.path.join(self.model_dir, f'{self.model_type}_classifier.pkl') - - self.vectorizer = joblib.load(vectorizer_path) - self.classifier = joblib.load(classifier_path) - print(f"模型加载成功: {self.model_type}") - - def preprocess(self, title: str, content: str) -> str: - """预处理文本""" - text = title + ' ' + content - # jieba分词 - words = jieba.cut(text) - return ' '.join(words) - - def predict(self, title: str, content: str) -> Dict[str, Any]: - """ - 预测 - :return: 预测结果字典 - """ - # 预处理 - processed = self.preprocess(title, content) - - # 特征提取 - tfidf = self.vectorizer.transform([processed]) - - # 预测 - prediction = self.classifier.predict(tfidf)[0] - probabilities = self.classifier.predict_proba(tfidf)[0] - - # 获取各类别概率 - prob_dict = {} - for i, prob in enumerate(probabilities): - category_code = self.classifier.classes_[i] - prob_dict[category_code] = float(prob) - - return { - 'categoryCode': prediction, - 'categoryName': CATEGORY_MAP.get(prediction, '未知'), - 'confidence': float(probabilities.max()), - 'probabilities': prob_dict - } - - -# API入口 -def predict_single(title: str, content: str, model_type='nb') -> Dict[str, Any]: - """ - 单条预测API - """ - predictor = TraditionalPredictor(model_type) - return predictor.predict(title, content) - - -if __name__ == '__main__': - # 测试 - result = predict_single( - title="华为发布新款折叠屏手机", - content="华为今天正式发布了新一代折叠屏手机,搭载最新麒麟芯片..." - ) - print(result) -``` - -#### 任务 4.1.2: `src/deep_learning/bert_model.py` - BERT模型 - -```python -""" -BERT文本分类模型 -""" - -import torch -from transformers import ( - BertTokenizer, - BertForSequenceClassification, - Trainer, - TrainingArguments -) -from typing import Dict, Any, List - - -# 分类映射 -CATEGORY_MAP = { - 'POLITICS': '时政', - 'FINANCE': '财经', - 'TECHNOLOGY': '科技', - 'SPORTS': '体育', - 'ENTERTAINMENT': '娱乐', - 'HEALTH': '健康', - 'EDUCATION': '教育', - 'LIFE': '生活', - 'INTERNATIONAL': '国际', - 'MILITARY': '军事' -} - -# 反向映射 -ID_TO_LABEL = {i: label for i, label in enumerate(CATEGORY_MAP.keys())} -LABEL_TO_ID = {label: i for i, label in enumerate(CATEGORY_MAP.keys())} - - -class BertClassifier: - """BERT文本分类器""" - - def __init__(self, model_name='bert-base-chinese', num_labels=10): - self.model_name = model_name - self.num_labels = num_labels - self.tokenizer = None - self.model = None - self.device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') - - def load_model(self, model_path): - """加载微调后的模型""" - self.tokenizer = BertTokenizer.from_pretrained(model_path) - self.model = BertForSequenceClassification.from_pretrained( - model_path, - num_labels=self.num_labels - ) - self.model.to(self.device) - self.model.eval() - print(f"BERT模型加载成功: {model_path}") - - def predict(self, title: str, content: str) -> Dict[str, Any]: - """ - 预测 - """ - if self.model is None or self.tokenizer is None: - raise ValueError("模型未加载,请先调用load_model") - - # 组合标题和内容 - text = f"{title} [SEP] {content}" - - # 分词 - inputs = self.tokenizer( - text, - return_tensors='pt', - truncation=True, - max_length=512, - padding='max_length' - ) - - # 预测 - with torch.no_grad(): - inputs = {k: v.to(self.device) for k, v in inputs.items()} - outputs = self.model(**inputs) - logits = outputs.logits - - # 获取预测结果 - probs = torch.softmax(logits, dim=-1) - confidence, predicted_id = torch.max(probs, dim=-1) - - predicted_id = predicted_id.item() - confidence = confidence.item() - - # 获取各类别概率 - prob_dict = {} - for i, prob in enumerate(probs[0].cpu().numpy()): - category_code = ID_TO_LABEL[i] - prob_dict[category_code] = float(prob) - - return { - 'categoryCode': ID_TO_LABEL[predicted_id], - 'categoryName': CATEGORY_MAP.get(ID_TO_LABEL[predicted_id], '未知'), - 'confidence': confidence, - 'probabilities': prob_dict - } - - -# 数据集类 -class NewsDataset(torch.utils.data.Dataset): - """新闻数据集""" - - def __init__(self, texts, labels, tokenizer, max_length=512): - self.texts = texts - self.labels = labels - self.tokenizer = tokenizer - self.max_length = max_length - - def __len__(self): - return len(self.texts) - - def __getitem__(self, idx): - text = self.texts[idx] - label = self.labels[idx] - - encoding = self.tokenizer( - text, - truncation=True, - max_length=self.max_length, - padding='max_length', - return_tensors='pt' - ) - - return { - 'input_ids': encoding['input_ids'].flatten(), - 'attention_mask': encoding['attention_mask'].flatten(), - 'labels': torch.tensor(label, dtype=torch.long) - } - - -if __name__ == '__main__': - # 测试 - classifier = BertClassifier() - # classifier.load_model('./models/deep_learning/bert_finetuned') - # - # result = classifier.predict( - # title="华为发布新款折叠屏手机", - # content="华为今天正式发布了新一代折叠屏手机..." - # ) - # print(result) - print("BERT分类器初始化成功") -``` - -#### 任务 4.1.3: `src/hybrid/hybrid_classifier.py` - 混合分类器 - -```python -""" -混合策略分类器 -结合规则引擎和机器学习模型 -""" - -import time -from typing import Dict, Any -from ..traditional.predict import TraditionalPredictor -from ..deep_learning.bert_model import BertClassifier - - -class HybridClassifier: - """混合分类器""" - - def __init__(self): - # 初始化各个分类器 - self.nb_predictor = TraditionalPredictor('nb') - self.bert_classifier = BertClassifier() - - # 配置参数 - self.config = { - 'confidence_threshold': 0.75, # 高置信度阈值 - 'hybrid_min_confidence': 0.60, # 混合模式最低阈值 - 'use_bert_threshold': 0.70, # 使用BERT的阈值 - 'rule_priority': True # 规则优先 - } - - # 规则关键词字典 - self.rule_keywords = { - 'POLITICS': ['政府', '政策', '选举', '国务院', '主席', '总理'], - 'FINANCE': ['股市', '经济', '金融', '投资', '基金', '银行'], - 'TECHNOLOGY': ['芯片', 'AI', '人工智能', '5G', '互联网', '科技'], - 'SPORTS': ['比赛', '冠军', '联赛', '球员', '教练', 'NBA'], - 'ENTERTAINMENT': ['明星', '电影', '电视剧', '娱乐圈', '歌手'], - 'HEALTH': ['健康', '医疗', '疾病', '治疗', '疫苗'], - 'EDUCATION': ['教育', '学校', '大学', '考试', '招生'], - 'LIFE': ['生活', '美食', '旅游', '购物'], - 'INTERNATIONAL': ['国际', '美国', '欧洲', '日本', '外交'], - 'MILITARY': ['军事', '武器', '军队', '国防', '战争'] - } - - def rule_match(self, title: str, content: str) -> tuple[str | None, float]: - """ - 规则匹配 - :return: (category_code, confidence) - """ - text = title + ' ' + content - - # 计算每个类别的关键词匹配数 - matches = {} - for category, keywords in self.rule_keywords.items(): - count = sum(1 for kw in keywords if kw in text) - if count > 0: - matches[category] = count - - if not matches: - return None, 0.0 - - # 返回匹配最多的类别 - best_category = max(matches, key=matches.get) - confidence = min(0.9, matches[best_category] * 0.15) # 规则置信度 - - return best_category, confidence - - def predict(self, title: str, content: str, use_bert=True) -> Dict[str, Any]: - """ - 混合预测 - """ - start_time = time.time() - - # 1. 先尝试规则匹配 - rule_category, rule_confidence = self.rule_match(title, content) - - # 2. 传统机器学习预测 - nb_result = self.nb_predictor.predict(title, content) - nb_confidence = nb_result['confidence'] - - # 决策逻辑 - final_result = None - classifier_type = 'HYBRID' - - # 规则优先且规则置信度高 - if self.config['rule_priority'] and rule_confidence >= self.config['confidence_threshold']: - final_result = { - 'categoryCode': rule_category, - 'categoryName': nb_result['categoryName'], # 从映射获取 - 'confidence': rule_confidence, - 'classifierType': 'RULE', - 'reason': '规则匹配' - } - # 传统模型置信度足够高 - elif nb_confidence >= self.config['confidence_threshold']: - final_result = { - **nb_result, - 'classifierType': 'ML', - 'reason': '传统模型高置信度' - } - # 需要使用BERT - elif use_bert: - # TODO: 加载BERT模型预测 - # bert_result = self.bert_classifier.predict(title, content) - # 如果BERT置信度也不高,选择最高的 - final_result = { - **nb_result, - 'classifierType': 'HYBRID', - 'reason': '混合决策' - } - else: - # 不使用BERT,直接返回传统模型结果 - final_result = { - **nb_result, - 'classifierType': 'ML', - 'reason': '默认传统模型' - } - - # 计算耗时 - duration = int((time.time() - start_time) * 1000) - final_result['duration'] = duration - - return final_result - - -if __name__ == '__main__': - # 测试 - classifier = HybridClassifier() - - test_cases = [ - { - 'title': '国务院发布最新经济政策', - 'content': '国务院今天发布了新的经济政策...' - }, - { - 'title': '华为发布新款折叠屏手机', - 'content': '华为今天正式发布了新一代折叠屏手机...' - } - ] - - for case in test_cases: - result = classifier.predict(case['title'], case['content']) - print(f"标题: {case['title']}") - print(f"结果: {result['categoryName']} ({result['confidence']:.2f})") - print(f"分类器: {result['classifierType']}") - print(f"原因: {result.get('reason', 'N/A')}") - print(f"耗时: {result['duration']}ms") - print("-" * 50) -``` - -#### 任务 4.1.4: `src/api/server.py` - FastAPI服务 - -```python -""" -机器学习模型API服务 -使用FastAPI提供RESTful API -""" - -from fastapi import FastAPI, HTTPException -from fastapi.middleware.cors import CORSMiddleware -from pydantic import BaseModel -from typing import Optional -import logging - -# 导入分类器 -import sys -import os -sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from traditional.predict import TraditionalPredictor -from hybrid.hybrid_classifier import HybridClassifier - -# 配置日志 -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) - -# 创建FastAPI应用 -app = FastAPI( - title="新闻分类API", - description="提供新闻文本分类服务", - version="1.0.0" -) - -# 配置CORS -app.add_middleware( - CORSMiddleware, - allow_origins=["*"], - allow_credentials=True, - allow_methods=["*"], - allow_headers=["*"], -) - - -# 请求模型 -class ClassifyRequest(BaseModel): - title: str - content: str - mode: Optional[str] = 'hybrid' # traditional, hybrid - - -# 响应模型 -class ClassifyResponse(BaseModel): - categoryCode: str - categoryName: str - confidence: float - classifierType: str - duration: int - probabilities: Optional[dict] = None - - -# 初始化分类器 -nb_predictor = None -hybrid_classifier = None - - -@app.on_event("startup") -async def startup_event(): - """启动时加载模型""" - global nb_predictor, hybrid_classifier - - logger.info("加载模型...") - - try: - nb_predictor = TraditionalPredictor('nb') - logger.info("朴素贝叶斯模型加载成功") - except Exception as e: - logger.error(f"朴素贝叶斯模型加载失败: {e}") - - try: - hybrid_classifier = HybridClassifier() - logger.info("混合分类器初始化成功") - except Exception as e: - logger.error(f"混合分类器初始化失败: {e}") - - -@app.get("/") -async def root(): - """健康检查""" - return { - "status": "ok", - "message": "新闻分类API服务运行中" - } - - -@app.get("/health") -async def health_check(): - """健康检查""" - return { - "status": "healthy", - "models": { - "nb_loaded": nb_predictor is not None, - "hybrid_loaded": hybrid_classifier is not None - } - } - - -@app.post("/api/predict", response_model=ClassifyResponse) -async def predict(request: ClassifyRequest): - """ - 文本分类接口 - - - **title**: 新闻标题 - - **content**: 新闻内容 - - **mode**: 分类模式 (traditional, hybrid) - """ - try: - if request.mode == 'traditional': - result = nb_predictor.predict(request.title, request.content) - result['classifierType'] = 'ML' - else: # hybrid - result = hybrid_classifier.predict(request.title, request.content) - - return ClassifyResponse(**result) - - except Exception as e: - logger.error(f"预测失败: {e}") - raise HTTPException(status_code=500, detail=str(e)) - - -@app.post("/api/batch-predict") -async def batch_predict(requests: list[ClassifyRequest]): - """ - 批量分类接口 - """ - results = [] - for req in requests: - try: - if req.mode == 'traditional': - result = nb_predictor.predict(req.title, req.content) - result['classifierType'] = 'ML' - else: - result = hybrid_classifier.predict(req.title, req.content) - results.append(result) - except Exception as e: - results.append({ - 'error': str(e), - 'title': req.title - }) - - return {"results": results} - - -if __name__ == '__main__': - import uvicorn - - uvicorn.run( - app, - host="0.0.0.0", - port=5000, - log_level="info" - ) -``` - -#### 任务 4.1.5: `src/utils/metrics.py` - 评估指标 - -```python -""" -模型评估指标工具 -""" - -import numpy as np -from sklearn.metrics import ( - accuracy_score, - precision_recall_fscore_support, - confusion_matrix, - classification_report -) -from typing import List, Dict, Any -import matplotlib.pyplot as plt -import seaborn as sns - - -class ClassificationMetrics: - """分类评估指标""" - - @staticmethod - def compute_all(y_true: List, y_pred: List, labels: List[str]) -> Dict[str, Any]: - """ - 计算所有指标 - """ - accuracy = accuracy_score(y_true, y_pred) - - precision, recall, f1, support = precision_recall_fscore_support( - y_true, y_pred, average='weighted', zero_division=0 - ) - - # 每个类别的指标 - precision_per_class, recall_per_class, f1_per_class, support_per_class = \ - precision_recall_fscore_support(y_true, y_pred, average=None, zero_division=0) - - per_class_metrics = {} - for i, label in enumerate(labels): - per_class_metrics[label] = { - 'precision': float(precision_per_class[i]), - 'recall': float(recall_per_class[i]), - 'f1': float(f1_per_class[i]), - 'support': int(support_per_class[i]) - } - - return { - 'accuracy': float(accuracy), - 'precision': float(precision), - 'recall': float(recall), - 'f1': float(f1), - 'per_class': per_class_metrics - } - - @staticmethod - def plot_confusion_matrix(y_true: List, y_pred: List, labels: List[str], save_path: str = None): - """ - 绘制混淆矩阵 - """ - cm = confusion_matrix(y_true, y_pred) - - plt.figure(figsize=(10, 8)) - sns.heatmap( - cm, - annot=True, - fmt='d', - cmap='Blues', - xticklabels=labels, - yticklabels=labels - ) - plt.xlabel('预测标签') - plt.ylabel('真实标签') - plt.title('混淆矩阵') - - if save_path: - plt.savefig(save_path, dpi=300, bbox_inches='tight') - plt.close() - - @staticmethod - def print_report(y_true: List, y_pred: List, labels: List[str]): - """ - 打印分类报告 - """ - report = classification_report( - y_true, y_pred, - target_names=labels, - zero_division=0 - ) - print(report) - - -if __name__ == '__main__': - # 测试 - y_true = ['POLITICS', 'TECHNOLOGY', 'FINANCE', 'POLITICS', 'TECHNOLOGY'] - y_pred = ['POLITICS', 'TECHNOLOGY', 'FINANCE', 'TECHNOLOGY', 'TECHNOLOGY'] - labels = ['POLITICS', 'TECHNOLOGY', 'FINANCE'] - - metrics = ClassificationMetrics() - result = metrics.compute_all(y_true, y_pred, labels) - print(result) -``` - -#### 任务 4.1.6: `requirements.txt` - 依赖文件 - -```txt -# 机器学习模块依赖 -numpy>=1.24.0 -pandas>=2.0.0 -scikit-learn>=1.3.0 -jieba>=0.42.0 -joblib>=1.3.0 - -# 深度学习 -torch>=2.0.0 -transformers>=4.30.0 - -# API服务 -fastapi>=0.100.0 -uvicorn[standard]>=0.23.0 -pydantic>=2.0.0 - -# 数据可视化 -matplotlib>=3.7.0 -seaborn>=0.12.0 - -# 工具 -python-dotenv>=1.0.0 -pyyaml>=6.0 -``` - ---- - -## 总结 - -### 开发顺序建议 - -1. **第一阶段:基础框架** - - 后端:数据库连接、实体类、基础配置 - - 前端:路由配置、状态管理、API封装 - -2. **第二阶段:核心功能** - - 爬虫模块(Python) - - 传统机器学习分类器 - - 后端API接口 - - 前端新闻列表页面 - -3. **第三阶段:高级功能** - - BERT深度学习分类器 - - 混合策略分类器 - - 前端分类器对比页面 - - 统计图表 - -4. **第四阶段:完善优化** - - 用户认证 - - 数据可视化 - - 性能优化 - - 异常处理 - -### 关键注意事项 - -1. **爬虫模块使用 Python**,通过 RESTful API 与 Java 后端通信 -2. **分类器模块独立部署**,提供 HTTP 接口供后端调用 -3. **前后端分离**,使用 JWT 进行身份认证 -4. **数据库表结构**已在 `schema.sql` 中定义,需严格遵守 -5. **API 统一响应格式**使用 `Result` 包装 diff --git a/docs/系统流程图.md b/docs/系统流程图.md deleted file mode 100644 index 28fcc0d..0000000 --- a/docs/系统流程图.md +++ /dev/null @@ -1,494 +0,0 @@ -# 基于Tauri的新闻文本分类系统 - 流程图 - -## 1. 系统整体架构图 - -```mermaid -graph TB - subgraph "客户端层 - Tauri Desktop" - Client[桌面客户端
Tauri + Vue3] - end - - subgraph "后端服务层 - Spring Boot" - API[API 网关] - Auth[认证模块
Spring Security + JWT] - NewsSvc[新闻服务] - UserSvc[用户服务] - ClassifySvc[分类服务] - CrawlerSvc[爬虫服务] - StatisticsSvc[统计服务] - end - - subgraph "数据存储层" - MySQL[(MySQL数据库)] - Redis[(Redis缓存)] - end - - subgraph "机器学习模块" - ML[传统ML分类器
朴素贝叶斯/SVM] - DL[深度学习分类器
BERT
可选] - Hybrid[混合分类器
置信度融合] - end - - Client -->|HTTP/WebSocket| API - API --> Auth - API --> NewsSvc - API --> UserSvc - API --> ClassifySvc - API --> CrawlerSvc - API --> StatisticsSvc - - NewsSvc --> MySQL - NewsSvc --> Redis - UserSvc --> MySQL - UserSvc --> Redis - - ClassifySvc --> ML - ClassifySvc --> DL - ClassifySvc --> Hybrid - - CrawlerSvc -->|抓取新闻| ClassifySvc - CrawlerSvc -->|存储| MySQL - - ML --> MySQL - DL --> MySQL - Hybrid --> MySQL - - style Client fill:#e1f5ff - style API fill:#fff4e6 - style MySQL fill:#f0f0f0 - style Redis fill:#f0f0f0 - style ML fill:#e8f5e9 - style DL fill:#e8f5e9 - style Hybrid fill:#e8f5e9 -``` - -## 2. 用户登录认证流程 - -```mermaid -sequenceDiagram - participant C as 桌面客户端 - participant API as 后端API - participant Auth as 认证模块 - participant DB as MySQL数据库 - participant Redis as Redis缓存 - - C->>API: POST /auth/login
{username, password} - API->>Auth: 转发登录请求 - Auth->>DB: 查询用户信息 - DB-->>Auth: 返回用户数据 - - alt 用户存在且密码正确 - Auth->>Auth: 验证通过
生成JWT Token - Auth->>Redis: 缓存用户会话 - Auth-->>API: 返回Token和用户信息 - API-->>C: 200 OK
{token, user} - C->>C: 存储Token到localStorage - else 用户不存在或密码错误 - Auth-->>API: 401 Unauthorized - API-->>C: 登录失败 - end - - Note over C,Redis: 后续请求携带Token - C->>API: 请求其他接口
Authorization: Bearer {token} - API->>Auth: 验证Token - Auth->>Redis: 检查Token有效性 - Redis-->>Auth: Token有效 - Auth-->>API: 验证通过 - API-->>C: 返回请求数据 -``` - -## 3. 新闻爬取与自动分类流程 - -```mermaid -flowchart TD - Start([定时任务触发
每30分钟]) --> Crawl[新闻爬虫模块] - - Crawl -->|并行抓取| Source1[今日头条] - Crawl -->|并行抓取| Source2[腾讯新闻] - Crawl -->|并行抓取| Source3[新浪新闻] - Crawl -->|可配置| SourceN[其他新闻源...] - - Source1 --> Clean1[数据清洗] - Source2 --> Clean2[数据清洗] - Source3 --> Clean3[数据清洗] - SourceN --> CleanN[数据清洗] - - Clean1 --> Merge{合并数据} - Clean2 --> Merge - Clean3 --> Merge - CleanN --> Merge - - Merge --> Dedup[去重处理] - Dedup --> Validate[数据验证] - - Validate -->|标题和内容完整| Classify[文本分类服务] - Validate -->|数据不完整| Discard[丢弃] - - Classify --> Hybrid{混合分类器} - - Hybrid --> ML1[传统ML分类器
关键词匹配] - Hybrid --> ML2[深度学习分类器
BERT
可选] - - ML1 --> Confidence[置信度计算] - ML2 --> Confidence - - Confidence --> Threshold{置信度 >= 0.75?} - - Threshold -->|是| Success[分类成功] - Threshold -->|否| Default[使用默认分类
科技类] - - Success --> Save[保存到数据库] - Default --> Save - - Save --> Cache[更新Redis缓存] - Cache --> End([流程结束]) - - Discard --> Log[记录日志] - Log --> End - - style Start fill:#e3f2fd - style End fill:#e8f5e9 - style Classify fill:#fff3e0 - style Save fill:#f3e5f5 - style Cache fill:#f3e5f5 -``` - -## 4. 新闻浏览与展示流程 - -```mermaid -sequenceDiagram - participant U as 用户 - participant C as 客户端 - participant Cache as Redis缓存 - participant API as 后端API - participant DB as MySQL数据库 - participant ML as 分类服务 - - U->>C: 点击新闻列表 - C->>Cache: 查询缓存 - alt 缓存命中 - Cache-->>C: 返回缓存数据 - C-->>U: 展示新闻列表 - else 缓存未命中 - C->>API: GET /news/list
?page=1&size=20 - API->>DB: 分页查询新闻 - DB-->>API: 返回新闻数据 - API->>Cache: 写入缓存 - Cache-->>API: 写入成功 - API-->>C: 返回新闻列表 - C-->>U: 展示新闻列表 - end - - U->>C: 点击某条新闻 - C->>API: GET /news/{id} - API->>DB: 查询新闻详情 - DB-->>API: 返回详情数据 - API->>DB: 增加浏览量+1 - API-->>C: 返回详情 - C-->>U: 展示新闻详情 - - U->>C: 按分类筛选 - C->>API: GET /news/category
?categoryCode=TECHNOLOGY - API->>DB: 按分类查询 - DB-->>API: 返回分类新闻 - API-->>C: 返回结果 - C-->>U: 展示分类新闻 - - U->>C: 搜索新闻 - C->>API: GET /news/search
?keyword=人工智能 - API->>DB: 全文搜索 - alt 使用Elasticsearch - API->>DB: ES搜索引擎 - end - DB-->>API: 返回搜索结果 - API-->>C: 返回结果 - C-->>U: 展示搜索结果 -``` - -## 5. 手动发布新闻与分类流程 - -```mermaid -flowchart TD - Start([管理员登录]) --> Verify{验证管理员权限} - - Verify -->|权限不足| Deny[提示权限不足] - Verify -->|权限通过| Access[进入管理后台] - - Access --> Click[点击发布新闻] - Click --> Form[填写新闻表单] - Form --> Input[输入标题、内容、
选择分类、上传封面] - - Input --> Option{是否自动分类?} - - Option -->|是| AutoClassify[调用分类API] - Option -->|否| ManualClassify[手动选择分类] - - AutoClassify --> ClassifySvc[文本分类服务] - ClassifySvc --> Hybrid{混合分类器} - - Hybrid --> ML[传统ML分类] - Hybrid --> DL[深度学习分类
可选] - - ML --> Result[获取分类结果
+ 置信度] - DL --> Result - - Result --> ShowResult[显示推荐分类
置信度: 0.85] - ShowResult --> Confirm{管理员确认?} - - Confirm -->|修改| ManualClassify - Confirm -->|确认| Validate[验证数据] - - ManualClassify --> Validate - - Validate --> Check{数据是否完整?} - - Check -->|否| Error[提示错误信息] - Check -->|是| Save[保存到数据库] - - Save --> Status[状态: 已发布] - Status --> UpdateCache[更新Redis缓存] - UpdateCache --> Log[记录操作日志] - Log --> End([发布成功]) - - Error --> Form - - Deny --> End - - style Start fill:#e3f2fd - style End fill:#e8f5e9 - style ClassifySvc fill:#fff3e0 - style Save fill:#f3e5f5 - style UpdateCache fill:#f3e5f5 -``` - -## 6. 数据可视化统计流程 - -```mermaid -flowchart LR - subgraph "数据采集" - DB[(MySQL数据库)] - Cache[(Redis缓存)] - end - - subgraph "统计计算" - Count[分类统计] - Trend[趋势分析] - Hot[热门排行] - Accuracy[准确率分析] - end - - subgraph "数据返回" - JSON[JSON数据] - end - - subgraph "前端展示" - Pie[饼图
分类分布] - Line[折线图
发布趋势] - Bar[柱状图
热门排行] - Gauge[仪表盘
准确率] - end - - DB --> Count - Cache --> Count - Count --> JSON - - DB --> Trend - Trend --> JSON - - DB --> Hot - Cache --> Hot - Hot --> JSON - - DB --> Accuracy - Accuracy --> JSON - - JSON --> Pie - JSON --> Line - JSON --> Bar - JSON --> Gauge - - style DB fill:#f0f0f0 - style Cache fill:#f0f0f0 - style JSON fill:#e1f5ff - style Pie fill:#fff3e0 - style Line fill:#fff3e0 - style Bar fill:#fff3e0 - style Gauge fill:#fff3e0 -``` - -## 7. 系统状态流转图 - -```mermaid -stateDiagram-v2 - [*] --> 未登录 - - 未登录 --> 已登录: 登录成功 - 未登录 --> 未登录: 登录失败 - - 已登录 --> 浏览新闻: 查看列表 - 浏览新闻 --> 已登录: 返回 - - 已登录 --> 查看详情: 点击新闻 - 查看详情 --> 已登录: 返回 - - 已登录 --> 搜索新闻: 输入关键词 - 搜索新闻 --> 已登录: 返回 - - 已登录 --> 查看统计: 点击统计页 - 查看统计 --> 已登录: 返回 - - 已登录 --> 管理员后台: 角色为ADMIN - 管理员后台 --> 已登录: 返回 - - 管理员后台 --> 发布新闻: 填写表单 - 发布新闻 --> 管理员后台: 发布成功 - - 管理员后台 --> 管理分类: 分类管理 - 管理分类 --> 管理员后台: 返回 - - 管理员后台 --> 管理用户: 用户管理 - 管理用户 --> 管理员后台: 返回 - - 已登录 --> 未登录: 登出/Token过期 -``` - -## 8. 文本分类算法决策流程 - -```mermaid -flowchart TD - Input[输入: 新闻标题 + 内容] --> Preprocess{文本预处理} - - Preprocess --> Clean[去除HTML标签
去除特殊字符
统一编码] - Clean --> Segment[中文分词
jieba] - - Segment --> Parallel{并行分类} - - Parallel --> Trad[传统ML分类器
TF-IDF + 朴素贝叶斯] - Parallel --> Deep[深度学习分类器
BERT
可选] - - Trad --> TradResult[结果1: 科技
置信度: 0.82] - Deep --> DeepResult[结果2: 科技
置信度: 0.88] - - TradResult --> Compare[置信度比较] - DeepResult --> Compare - - Compare --> Select{选择最高置信度} - - Select --> CheckThreshold{置信度 >= 0.75?} - - CheckThreshold -->|是| Return[返回分类结果
科技, 0.88, HYBRID] - CheckThreshold -->|否| Default[返回默认分类
科技, 0.50, HYBRID] - - Return --> Output[输出分类结果] - Default --> Output - - style Input fill:#e3f2fd - style Output fill:#e8f5e9 - style Parallel fill:#fff3e0 - style Compare fill:#f3e5f5 - style Return fill:#c8e6c9 - style Default fill:#ffccbc -``` - -## 9. 缓存更新策略流程 - -```mermaid -flowchart TD - Request[请求到达] --> CheckCache{检查Redis缓存} - - CheckCache -->|命中| Hit[返回缓存数据] - CheckCache -->|未命中| Miss[查询MySQL数据库] - - Miss --> QueryDB[执行SQL查询] - QueryDB --> Result[获取查询结果] - - Result --> WriteCache[写入Redis缓存] - WriteCache --> SetExpire[设置过期时间
30分钟] - - SetExpire --> Return[返回数据] - - Hit --> Return - - Return --> End[请求结束] - - style CheckCache fill:#fff3e0 - style WriteCache fill:#f3e5f5 - style Hit fill:#c8e6c9 - style Miss fill:#ffccbc -``` - -## 10. 新闻爬虫详细工作流程 - -```mermaid -flowchart TD - Trigger[定时任务触发
Cron: 0 */30 * * * ?] --> Config{读取配置} - - Config --> GetSources[获取启用的新闻源列表] - - GetSources --> Loop{遍历每个新闻源} - - Loop --> Fetch[发送HTTP请求] - Fetch --> CheckResp{响应状态码 200?} - - CheckResp -->|否| ErrorLog[记录错误日志] - CheckResp -->|是| Parse[解析HTML
Jsoup] - - Parse --> Extract[提取新闻数据
标题/内容/链接/时间] - - Extract --> ValidateData{数据验证} - - ValidateData -->|标题为空| ErrorLog - ValidateData -->|内容为空| ErrorLog - ValidateData -->|数据完整| Continue[继续处理] - - Continue --> CheckDup{检查URL是否重复?} - - CheckDup -->|已存在| Skip[跳过该新闻] - CheckDup -->|不存在| Process[处理新闻] - - Process --> Classify[调用分类服务] - Classify --> SaveDB[保存到MySQL] - SaveDB --> UpdateCache[更新Redis缓存] - - UpdateCache --> Next{还有新闻源?} - - ErrorLog --> Next - Skip --> Next - - Next -->|是| Loop - Next -->|否| Summary[生成爬取报告] - Summary --> Finish([任务结束]) - - style Trigger fill:#e3f2fd - style Finish fill:#e8f5e9 - style Classify fill:#fff3e0 - style SaveDB fill:#f3e5f5 - style UpdateCache fill:#f3e5f5 -``` - ---- - -## 流程图说明 - -### 关键模块交互说明 - -1. **客户端-后端交互** - - 使用RESTful API进行通信 - - JWT Token进行身份认证 - - 响应格式统一: `{code, message, data, timestamp}` - -2. **文本分类流程** - - 支持传统机器学习(朴素贝叶斯/SVM) - - 可选深度学习(BERT) - - 混合分类器选择置信度最高的结果 - - 置信度低于阈值时使用默认分类 - -3. **缓存策略** - - Redis缓存热点数据 - - 缓存过期时间: 30分钟 - - Cache-Aside模式 - -4. **数据流转** - - 爬虫 → 分类 → 数据库 → 缓存 → 客户端 - - 管理员操作 → 数据库 → 缓存更新 - - 用户浏览 → 缓存/数据库 → 展示 diff --git a/docs/系统流程详解.md b/docs/系统流程详解.md deleted file mode 100644 index 24d5078..0000000 --- a/docs/系统流程详解.md +++ /dev/null @@ -1,984 +0,0 @@ -# 基于Tauri的新闻文本分类系统 - 完整流程详解 - -> 本文档详细说明系统中所有模块的工作流程和交互细节 - ---- - -## 目录 - -1. [系统整体架构](#1-系统整体架构) -2. [用户登录认证流程](#2-用户登录认证流程) -3. [新闻爬取与自动分类流程](#3-新闻爬取与自动分类流程) -4. [新闻浏览与展示流程](#4-新闻浏览与展示流程) -5. [手动发布新闻与分类流程](#5-手动发布新闻与分类流程) -6. [数据可视化统计流程](#6-数据可视化统计流程) -7. [系统状态流转](#7-系统状态流转) -8. [文本分类算法决策流程](#8-文本分类算法决策流程) -9. [缓存更新策略](#9-缓存更新策略) -10. [新闻爬虫详细工作流程](#10-新闻爬虫详细工作流程) - ---- - -## 1. 系统整体架构 - -### 1.1 架构分层图 - -```mermaid -graph TB - subgraph "客户端层 - Tauri Desktop" - Client[桌面客户端
Tauri + Vue3] - end - - subgraph "后端服务层 - Spring Boot" - API[API 网关] - Auth[认证模块
Spring Security + JWT] - NewsSvc[新闻服务] - UserSvc[用户服务] - ClassifySvc[分类服务] - CrawlerSvc[爬虫服务] - StatisticsSvc[统计服务] - end - - subgraph "数据存储层" - MySQL[(MySQL数据库)] - Redis[(Redis缓存)] - end - - subgraph "机器学习模块" - ML[传统ML分类器
朴素贝叶斯/SVM] - DL[深度学习分类器
BERT
可选] - Hybrid[混合分类器
置信度融合] - end - - Client -->|HTTP/WebSocket| API - API --> Auth - API --> NewsSvc - API --> UserSvc - API --> ClassifySvc - API --> CrawlerSvc - API --> StatisticsSvc - - NewsSvc --> MySQL - NewsSvc --> Redis - UserSvc --> MySQL - UserSvc --> Redis - - ClassifySvc --> ML - ClassifySvc --> DL - ClassifySvc --> Hybrid - - CrawlerSvc -->|抓取新闻| ClassifySvc - CrawlerSvc -->|存储| MySQL - - ML --> MySQL - DL --> MySQL - Hybrid --> MySQL - - style Client fill:#e1f5ff - style API fill:#fff4e6 - style MySQL fill:#f0f0f0 - style Redis fill:#f0f0f0 - style ML fill:#e8f5e9 - style DL fill:#e8f5e9 - style Hybrid fill:#e8f5e9 -``` - -### 1.2 架构层次说明 - -| 层次 | 职责 | 技术实现 | -|------|------|----------| -| **客户端层** | 用户界面、交互逻辑 | Tauri 2.x + Vue 3 + TypeScript | -| **网关层** | 统一入口、请求路由 | Spring MVC | -| **业务层** | 核心业务逻辑 | Spring Service | -| **数据层** | 数据持久化、缓存 | MyBatis-Plus + Redis | -| **算法层** | 文本分类算法 | Scikit-learn / Transformers | - -### 1.3 客户端-后端交互 - -``` -┌─────────────────────────────────────────────────────────────────────────────┐ -│ 系统分层架构 │ -├─────────────────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌─────────────────────────────────────────────────────────────────────┐ │ -│ │ 客户端层 (Tauri Desktop) │ │ -│ │ • 跨平台桌面应用 (Windows/macOS/Linux) │ │ -│ │ • 使用系统WebView,体积小 (~50MB) │ │ -│ │ • IPC通信: 前端 ↔ Rust安全通信 │ │ -│ │ • 技术栈: Vue 3 + TypeScript + Tailwind CSS │ │ -│ └─────────────────────────────────────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────────────┐ │ -│ │ 后端服务层 (Spring Boot 3.x) │ │ -│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ -│ │ │ API网关 │ │ 认证模块 │ │ 新闻服务 │ │ 分类服务 │ │ │ -│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │ -│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ -│ │ │ 爬虫服务 │ │ 统计服务 │ │ 用户服务 │ │ │ -│ │ └──────────┘ └──────────┘ └──────────┘ │ │ -│ └─────────────────────────────────────────────────────────────────────┘ │ -│ │ │ -│ ┌──────────────────────────┼──────────────────────────┐ │ -│ ▼ ▼ ▼ │ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ -│ │ MySQL │ │ Redis │ │ Elasticsearch│ │ -│ │ • 用户数据 │ │ • 会话缓存 │ │ • 全文检索 │ │ -│ │ • 新闻数据 │ │ • 热点数据 │ │ (可选) │ │ -│ └──────────────┘ └──────────────┘ └──────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────────────┘ -``` - ---- - -## 2. 用户登录认证流程 - -### 2.1 登录认证时序图 - -```mermaid -sequenceDiagram - participant C as 桌面客户端 - participant API as 后端API - participant Auth as 认证模块 - participant DB as MySQL数据库 - participant Redis as Redis缓存 - - C->>API: POST /auth/login
{username, password} - API->>Auth: 转发登录请求 - Auth->>DB: 查询用户信息 - DB-->>Auth: 返回用户数据 - - alt 用户存在且密码正确 - Auth->>Auth: 验证通过
生成JWT Token - Auth->>Redis: 缓存用户会话 - Auth-->>API: 返回Token和用户信息 - API-->>C: 200 OK
{token, user} - C->>C: 存储Token到localStorage - else 用户不存在或密码错误 - Auth-->>API: 401 Unauthorized - API-->>C: 登录失败 - end - - Note over C,Redis: 后续请求携带Token - C->>API: 请求其他接口
Authorization: Bearer {token} - API->>Auth: 验证Token - Auth->>Redis: 检查Token有效性 - Redis-->>Auth: Token有效 - Auth-->>API: 验证通过 - API-->>C: 返回请求数据 -``` - -### 2.2 JWT认证机制 - -``` -┌─────────────────────────────────────────────────────────────────────┐ -│ JWT认证机制 │ -├─────────────────────────────────────────────────────────────────────┤ -│ │ -│ JWT Token结构: │ -│ ┌──────────────────┬──────────────────┬──────────────────┐ │ -│ │ Header │ Payload │ Signature │ │ -│ ├──────────────────┼──────────────────┼──────────────────┤ │ -│ │ { │ { │ [签名部分] │ │ -│ │ "alg": "HS256",│ "userId": 1, │ │ │ -│ │ "typ": "JWT" │ "username": │ HMACSHA256( │ │ -│ │ } │ "admin", │ base64UrlEncode│ │ -│ │ │ "role": "ADMIN",│ (header) + │ │ -│ │ │ "exp": 1234567890"." + base64UrlEncode│ │ -│ │ │ } │ (payload), │ │ -│ │ │ │ secret) │ │ -│ └──────────────────┴──────────────────┴──────────────────┘ │ -│ │ -│ 密码加密: BCrypt (单向Hash,每次加密结果不同) │ -│ • 工作因子: 10 (计算复杂度 2^10) │ -│ • 存储格式: $2a$10$... │ -│ │ -│ Token存储策略: │ -│ • 客户端: localStorage存储Token │ -│ • 服务端: Redis缓存Token与用户映射 (24小时过期) │ -│ • 请求头: Authorization: Bearer {token} │ -│ │ -└─────────────────────────────────────────────────────────────────────┘ -``` - -### 2.3 RBAC权限模型 - -``` -┌─────────────────────────────────────────────────────────────────────┐ -│ RBAC权限模型 │ -├─────────────────────────────────────────────────────────────────────┤ -│ │ -│ 角色: │ -│ ┌────────────────┐ ┌────────────────┐ │ -│ │ ADMIN │ │ USER │ │ -│ │ • 所有权限 │ │ • 查看新闻 │ │ -│ │ • 用户管理 │ │ • 搜索新闻 │ │ -│ │ • 新闻管理 │ │ • 查看统计 │ │ -│ │ • 分类管理 │ │ • 收藏新闻 │ │ -│ │ • 系统配置 │ │ │ │ -│ └────────────────┘ └────────────────┘ │ -│ │ -│ 权限注解 (Spring Security): │ -│ • @PreAuthorize("hasRole('ADMIN')") // 需要管理员权限 │ -│ • @PreAuthorize("hasAnyRole('ADMIN', 'USER')") // 用户或管理员 │ -│ • @PreAuthorize("isAuthenticated()") // 需要登录 │ -│ │ -└─────────────────────────────────────────────────────────────────────┘ -``` - ---- - -## 3. 新闻爬取与自动分类流程 - -### 3.1 爬虫与分类流程图 - -```mermaid -flowchart TD - Start([定时任务触发
每30分钟]) --> Crawl[新闻爬虫模块] - - Crawl -->|并行抓取| Source1[今日头条] - Crawl -->|并行抓取| Source2[腾讯新闻] - Crawl -->|并行抓取| Source3[新浪新闻] - Crawl -->|可配置| SourceN[其他新闻源...] - - Source1 --> Clean1[数据清洗] - Source2 --> Clean2[数据清洗] - Source3 --> Clean3[数据清洗] - SourceN --> CleanN[数据清洗] - - Clean1 --> Merge{合并数据} - Clean2 --> Merge - Clean3 --> Merge - CleanN --> Merge - - Merge --> Dedup[去重处理] - Dedup --> Validate[数据验证] - - Validate -->|标题和内容完整| Classify[文本分类服务] - Validate -->|数据不完整| Discard[丢弃] - - Classify --> Hybrid{混合分类器} - - Hybrid --> ML1[传统ML分类器
关键词匹配] - Hybrid --> ML2[深度学习分类器
BERT
可选] - - ML1 --> Confidence[置信度计算] - ML2 --> Confidence - - Confidence --> Threshold{置信度 >= 0.75?} - - Threshold -->|是| Success[分类成功] - Threshold -->|否| Default[使用默认分类
科技类] - - Success --> Save[保存到数据库] - Default --> Save - - Save --> Cache[更新Redis缓存] - Cache --> End([流程结束]) - - Discard --> Log[记录日志] - Log --> End - - style Start fill:#e3f2fd - style End fill:#e8f5e9 - style Classify fill:#fff3e0 - style Save fill:#f3e5f5 - style Cache fill:#f3e5f5 -``` - -### 3.2 文本分类详细流程 - -``` -┌─────────────────────────────────────────────────────────────────────┐ -│ 文本分类算法详细流程 │ -├─────────────────────────────────────────────────────────────────────┤ -│ │ -│ 输入: 标题 "华为发布新款折叠屏手机" │ -│ 内容 "华为今天正式发布了新一代折叠屏手机,搭载最新麒麟芯片..." │ -│ │ │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ 文本预处理 │ │ -│ │ ┌─────────────────────────────────────────────────────────┐ │ │ -│ │ 1. 去除HTML标签、特殊字符 │ │ │ -│ │ 2. 中文分词 (jieba) │ │ │ -│ │ "华为 发布 新款 折叠屏 手机 ..." │ │ │ -│ │ 3. 去除停用词 (的、了、是...) │ │ │ -│ │ 4. 合并标题和内容: title + " " + content │ │ │ -│ └─────────────────────────────────────────────────────────┘ │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ 并行分类 (多线程) │ │ -│ ├─────────────────────────────────────────────────────────────┤ │ -│ │ │ │ -│ │ 分类器1: 传统机器学习 (朴素贝叶斯) │ │ -│ │ ┌──────────────────────────────────────────────────────┐ │ │ -│ │ │ 1. TF-IDF特征提取 │ │ │ -│ │ │ 向量化: [华为:0.5, 手机:0.3, 发布:0.2, ...] │ │ │ -│ │ │ 2. 朴素贝叶斯预测 │ │ │ -│ │ │ 3. 输出结果: TECHNOLOGY, 0.82 │ │ │ -│ │ └──────────────────────────────────────────────────────┘ │ │ -│ │ │ │ -│ │ 分类器2: 关键词匹配 (规则引擎) │ │ -│ │ ┌──────────────────────────────────────────────────────┐ │ │ -│ │ │ 1. 关键词匹配统计 │ │ │ -│ │ │ 科技类关键词: "科技", "手机", "华为", "芯片"... │ │ │ -│ │ │ 匹配数: 5 │ │ │ -│ │ │ 2. 输出结果: TECHNOLOGY, 0.50 │ │ │ -│ │ └──────────────────────────────────────────────────────┘ │ │ -│ │ │ │ -│ │ 分类器3: 深度学习 BERT (可选) │ │ -│ │ ┌──────────────────────────────────────────────────────┐ │ │ -│ │ │ 1. 文本编码 (BERT Tokenizer) │ │ │ -│ │ │ 2. 模型推理 │ │ │ -│ │ │ 3. 输出结果: TECHNOLOGY, 0.88 │ │ │ -│ │ └──────────────────────────────────────────────────────┘ │ │ -│ │ │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ 置信度融合 │ │ -│ │ 结果比较: 选择最高置信度 0.88 (BERT) │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ 最终输出: │ -│ { │ -│ "categoryCode": "TECHNOLOGY", │ -│ "categoryName": "科技", │ -│ "confidence": 0.88, │ -│ "classifierType": "HYBRID" │ -│ } │ -│ │ -└─────────────────────────────────────────────────────────────────────┘ -``` - -### 3.3 传统ML分类器原理 - -``` -┌─────────────────────────────────────────────────────────────────────┐ -│ 传统机器学习分类器工作原理 │ -├─────────────────────────────────────────────────────────────────────┤ -│ │ -│ 训练阶段: │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ 训练数据 (5000条标注新闻) │ │ -│ │ ┌──────────┬──────────┬────────────┐ │ │ -│ │ │ 标题 │ 内容 │ 分类标签 │ │ │ -│ │ ├──────────┼──────────┼────────────┤ │ │ -│ │ │华为手机 │华为发布...│ TECHNOLOGY │ │ │ -│ │ │股市波动 │今日股市...│ FINANCE │ │ │ -│ │ └──────────┴──────────┴────────────┘ │ │ -│ │ │ │ -│ │ ▼ │ -│ │ TF-IDF向量化 │ │ -│ │ • TF(t,d) = 词t在文档d中出现次数 / 文档d总词数 │ │ -│ │ • IDF(t) = log(总文档数 / 包含词t的文档数) │ │ -│ │ • TF-IDF = TF × IDF │ │ -│ │ │ │ -│ │ ▼ │ -│ │ 朴素贝叶斯训练 │ │ -│ │ • P(科技|向量) ∝ P(向量|科技) × P(科技) │ │ -│ │ • 保存模型: vectorizer.pkl, classifier.pkl │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -│ │ -│ 预测阶段: │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ 新新闻: "iPhone 16 Pro Max评测" │ │ -│ │ → 加载模型 → TF-IDF转换 → 朴素贝叶斯预测 → 返回分类结果 │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -│ │ -└─────────────────────────────────────────────────────────────────────┘ -``` - ---- - -## 4. 新闻浏览与展示流程 - -### 4.1 浏览流程时序图 - -```mermaid -sequenceDiagram - participant U as 用户 - participant C as 客户端 - participant Cache as Redis缓存 - participant API as 后端API - participant DB as MySQL数据库 - participant ML as 分类服务 - - U->>C: 点击新闻列表 - C->>Cache: 查询缓存 - alt 缓存命中 - Cache-->>C: 返回缓存数据 - C-->>U: 展示新闻列表 - else 缓存未命中 - C->>API: GET /news/list
?page=1&size=20 - API->>DB: 分页查询新闻 - DB-->>API: 返回新闻数据 - API->>Cache: 写入缓存 - Cache-->>API: 写入成功 - API-->>C: 返回新闻列表 - C-->>U: 展示新闻列表 - end - - U->>C: 点击某条新闻 - C->>API: GET /news/{id} - API->>DB: 查询新闻详情 - DB-->>API: 返回详情数据 - API->>DB: 增加浏览量+1 - API-->>C: 返回详情 - C-->>U: 展示新闻详情 - - U->>C: 按分类筛选 - C->>API: GET /news/category
?categoryCode=TECHNOLOGY - API->>DB: 按分类查询 - DB-->>API: 返回分类新闻 - API-->>C: 返回结果 - C-->>U: 展示分类新闻 - - U->>C: 搜索新闻 - C->>API: GET /news/search
?keyword=人工智能 - API->>DB: 全文搜索 - alt 使用Elasticsearch - API->>DB: ES搜索引擎 - end - DB-->>API: 返回搜索结果 - API-->>C: 返回结果 - C-->>U: 展示搜索结果 -``` - -### 4.2 缓存策略详解 - -``` -┌─────────────────────────────────────────────────────────────────────┐ -│ Cache-Aside缓存策略 │ -├─────────────────────────────────────────────────────────────────────┤ -│ │ -│ 读流程: │ -│ 应用 ──查询──▶ Redis ──未命中──▶ MySQL ──返回──▶ 应用 ──写入──▶ Redis│ -│ │ │ -│ ▼ │ -│ Redis ──命中──▶ 应用 │ -│ │ -│ 写流程: │ -│ 应用 ──写入──▶ MySQL ──删除──▶ Redis │ -│ │ -│ 缓存Key设计: │ -│ • 新闻列表: news:list:{page}:{size} │ -│ • 分类新闻: news:category:{code}:{page}:{size} │ -│ • 新闻详情: news:detail:{id} │ -│ • 热门新闻: news:hot (5分钟更新) │ -│ • 搜索结果: news:search:{keyword}:{page} (10分钟过期) │ -│ │ -└─────────────────────────────────────────────────────────────────────┘ -``` - ---- - -## 5. 手动发布新闻与分类流程 - -### 5.1 发布流程图 - -```mermaid -flowchart TD - Start([管理员登录]) --> Verify{验证管理员权限} - - Verify -->|权限不足| Deny[提示权限不足] - Verify -->|权限通过| Access[进入管理后台] - - Access --> Click[点击发布新闻] - Click --> Form[填写新闻表单] - Form --> Input[输入标题、内容、
选择分类、上传封面] - - Input --> Option{是否自动分类?} - - Option -->|是| AutoClassify[调用分类API] - Option -->|否| ManualClassify[手动选择分类] - - AutoClassify --> ClassifySvc[文本分类服务] - ClassifySvc --> Hybrid{混合分类器} - - Hybrid --> ML[传统ML分类] - Hybrid --> DL[深度学习分类
可选] - - ML --> Result[获取分类结果
+ 置信度] - DL --> Result - - Result --> ShowResult[显示推荐分类
置信度: 0.85] - ShowResult --> Confirm{管理员确认?} - - Confirm -->|修改| ManualClassify - Confirm -->|确认| Validate[验证数据] - - ManualClassify --> Validate - - Validate --> Check{数据是否完整?} - - Check -->|否| Error[提示错误信息] - Check -->|是| Save[保存到数据库] - - Save --> Status[状态: 已发布] - Status --> UpdateCache[更新Redis缓存] - UpdateCache --> Log[记录操作日志] - Log --> End([发布成功]) - - Error --> Form - - Deny --> End - - style Start fill:#e3f2fd - style End fill:#e8f5e9 - style ClassifySvc fill:#fff3e0 - style Save fill:#f3e5f5 - style UpdateCache fill:#f3e5f5 -``` - -### 5.2 发布表单示例 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 新闻发布表单 │ -│ │ -│ 标题: [iPhone 16 Pro Max深度评测________] │ -│ │ -│ 内容: [苹果最新旗舰iPhone 16 Pro Max震撼上市...] │ -│ [搭载A18 Pro芯片,性能提升30%...] │ -│ [影像系统全面升级...] │ -│ │ -│ 来源: [科技日报____] 作者: [张三____] │ -│ │ -│ 分类: [科技 ▼] 或 [🤖 自动分类] 置顶: [ ] 热门: [ ] │ -│ │ -│ 封面: [选择文件...] │ -│ │ -│ [🤖 自动分类] [💾 保存草稿] [✓ 立即发布] │ -│ │ -└─────────────────────────────────────────────────────────────┘ - -自动分类结果: -┌─────────────────────────────────────────────────────────────┐ -│ 推荐分类: 科技 │ -│ 置信度: ████████░░ 88% │ -│ 分类器: 混合分类器 (BERT) │ -│ [✓ 确认使用] [✏️ 修改选择] │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## 6. 数据可视化统计流程 - -### 6.1 统计数据流程图 - -```mermaid -flowchart LR - subgraph "数据采集" - DB[(MySQL数据库)] - Cache[(Redis缓存)] - end - - subgraph "统计计算" - Count[分类统计] - Trend[趋势分析] - Hot[热门排行] - Accuracy[准确率分析] - end - - subgraph "数据返回" - JSON[JSON数据] - end - - subgraph "前端展示" - Pie[饼图
分类分布] - Line[折线图
发布趋势] - Bar[柱状图
热门排行] - Gauge[仪表盘
准确率] - end - - DB --> Count - Cache --> Count - Count --> JSON - - DB --> Trend - Trend --> JSON - - DB --> Hot - Cache --> Hot - Hot --> JSON - - DB --> Accuracy - Accuracy --> JSON - - JSON --> Pie - JSON --> Line - JSON --> Bar - JSON --> Gauge - - style DB fill:#f0f0f0 - style Cache fill:#f0f0f0 - style JSON fill:#e1f5ff - style Pie fill:#fff3e0 - style Line fill:#fff3e0 - style Bar fill:#fff3e0 - style Gauge fill:#fff3e0 -``` - -### 6.2 统计图表示例 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 统计仪表盘 │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ -│ │ 10,523 │ │ 156 │ │ 85.2% │ │ -│ │ 总新闻数 │ │ 今日新增 │ │ 分类准确率 │ │ -│ └─────────────┘ └─────────────┘ └─────────────┘ │ -│ │ -│ 分类分布 (饼图) 发布趋势 (折线图) │ -│ ┌───────────────────┐ ┌───────────────────┐ │ -│ │ 时政 15% │ │ 200├─● │ │ -│ │ ████████ │ │ 150├─ ●─● │ │ -│ │ 财经 22% │ │ 100├─ ●─● │ │ -│ │ ████████████ │ │ 50├─ ●─● │ │ -│ │ 科技 18% │ │ 0└────────────────────▶ │ │ -│ │ █████████ │ │ 12/18 12/19 12/20 │ │ -│ └───────────────────┘ └───────────────────┘ │ -│ │ -│ 热门排行 (柱状图) │ -│ ┌─────────────────────────────────────────────────────────┐│ -│ │ 浏览量 ││ -│ │ 5000├─ █ ││ -│ │ 4000├─ █ █ ││ -│ │ 3000├─ █ █ █ ││ -│ │ 2000├─ █ █ █ █ ││ -│ │ 1000├─ █ █ █ █ █ ││ -│ │ 0└────────────────────────────▶ ││ -│ │ 新闻1 新闻2 新闻3 新闻4 新闻5 ││ -│ └─────────────────────────────────────────────────────────┘│ -│ │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## 7. 系统状态流转 - -### 7.1 用户状态机 - -```mermaid -stateDiagram-v2 - [*] --> 未登录 - - 未登录 --> 已登录: 登录成功 - 未登录 --> 未登录: 登录失败 - - 已登录 --> 浏览新闻: 查看列表 - 浏览新闻 --> 已登录: 返回 - - 已登录 --> 查看详情: 点击新闻 - 查看详情 --> 已登录: 返回 - - 已登录 --> 搜索新闻: 输入关键词 - 搜索新闻 --> 已登录: 返回 - - 已登录 --> 查看统计: 点击统计页 - 查看统计 --> 已登录: 返回 - - 已登录 --> 管理员后台: 角色为ADMIN - 管理员后台 --> 已登录: 返回 - - 管理员后台 --> 发布新闻: 填写表单 - 发布新闻 --> 管理员后台: 发布成功 - - 管理员后台 --> 管理分类: 分类管理 - 管理分类 --> 管理员后台: 返回 - - 管理员后台 --> 管理用户: 用户管理 - 管理用户 --> 管理员后台: 返回 - - 已登录 --> 未登录: 登出/Token过期 -``` - -### 7.2 新闻状态流转 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 新闻状态流转 │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ ┌──────┐ 发布 ┌──────┐ 下架 ┌──────┐ │ -│ │ 草稿 │─────────────▶│ 已发布│─────────────▶│ 已下架│ │ -│ │ 0 │ │ 1 │ │ 2 │ │ -│ └──────┘ └──────┘ └──────┘ │ -│ │ ▲ │ │ -│ │ │ │ │ -│ └─────────────────────┴────────────────────┘ │ -│ 重新发布/编辑 │ -│ │ -│ 状态说明: │ -│ • 0 = 草稿: 编辑中,不对用户展示 │ -│ • 1 = 已发布: 正常展示,可被搜索和浏览 │ -│ • 2 = 已下架: 不展示,但保留数据 │ -│ │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## 8. 文本分类算法决策流程 - -### 8.1 分类决策流程图 - -```mermaid -flowchart TD - Input[输入: 新闻标题 + 内容] --> Preprocess{文本预处理} - - Preprocess --> Clean[去除HTML标签
去除特殊字符
统一编码] - Clean --> Segment[中文分词
jieba] - - Segment --> Parallel{并行分类} - - Parallel --> Trad[传统ML分类器
TF-IDF + 朴素贝叶斯] - Parallel --> Deep[深度学习分类器
BERT
可选] - - Trad --> TradResult[结果1: 科技
置信度: 0.82] - Deep --> DeepResult[结果2: 科技
置信度: 0.88] - - TradResult --> Compare[置信度比较] - DeepResult --> Compare - - Compare --> Select{选择最高置信度} - - Select --> CheckThreshold{置信度 >= 0.75?} - - CheckThreshold -->|是| Return[返回分类结果
科技, 0.88, HYBRID] - CheckThreshold -->|否| Default[返回默认分类
科技, 0.50, HYBRID] - - Return --> Output[输出分类结果] - Default --> Output - - style Input fill:#e3f2fd - style Output fill:#e8f5e9 - style Parallel fill:#fff3e0 - style Compare fill:#f3e5f5 - style Return fill:#c8e6c9 - style Default fill:#ffccbc -``` - -### 8.2 分类器对比 - -| 分类器 | 优点 | 缺点 | 适用场景 | -|--------|------|------|----------| -| **传统ML** | 训练快、资源少、易部署 | 准确率较低、需要特征工程 | 基础分类、快速上线 | -| **深度学习(BERT)** | 准确率高、语义理解强 | 训练慢、资源消耗大 | 高精度要求场景 | -| **混合分类器** | 兼顾准确率和效率 | 配置复杂 | 生产环境推荐 | - ---- - -## 9. 缓存更新策略 - -### 9.1 缓存流程图 - -```mermaid -flowchart TD - Request[请求到达] --> CheckCache{检查Redis缓存} - - CheckCache -->|命中| Hit[返回缓存数据] - CheckCache -->|未命中| Miss[查询MySQL数据库] - - Miss --> QueryDB[执行SQL查询] - QueryDB --> Result[获取查询结果] - - Result --> WriteCache[写入Redis缓存] - WriteCache --> SetExpire[设置过期时间
30分钟] - - SetExpire --> Return[返回数据] - - Hit --> Return - - Return --> End[请求结束] - - style CheckCache fill:#fff3e0 - style WriteCache fill:#f3e5f5 - style Hit fill:#c8e6c9 - style Miss fill:#ffccbc -``` - -### 9.2 缓存过期策略 - -``` -┌─────────────────────────────────────────────────────────────┐ -│ 缓存过期策略 │ -├─────────────────────────────────────────────────────────────┤ -│ │ -│ 数据类型 过期时间 更新触发 │ -│ ─────────────────────────────────────────────────────── │ -│ 新闻列表 30分钟 自动过期 │ -│ 分类新闻 30分钟 自动过期 │ -│ 新闻详情 1小时 自动过期 │ -│ 热门新闻 5分钟 定时任务更新 │ -│ 统计数据 10分钟 定时任务更新 │ -│ 用户会话 24小时 登录/登出 │ -│ │ -│ 主动失效场景: │ -│ • 管理员发布新闻 → 清除列表缓存 │ -│ • 管理员修改分类 → 清除分类缓存 │ -│ • 管理员删除新闻 → 清除详情缓存 │ -│ │ -└─────────────────────────────────────────────────────────────┘ -``` - ---- - -## 10. 新闻爬虫详细工作流程 - -### 10.1 爬虫工作流程图 - -```mermaid -flowchart TD - Trigger[定时任务触发
Cron: 0 */30 * * * ?] --> Config{读取配置} - - Config --> GetSources[获取启用的新闻源列表] - - GetSources --> Loop{遍历每个新闻源} - - Loop --> Fetch[发送HTTP请求] - Fetch --> CheckResp{响应状态码 200?} - - CheckResp -->|否| ErrorLog[记录错误日志] - CheckResp -->|是| Parse[解析HTML
Jsoup] - - Parse --> Extract[提取新闻数据
标题/内容/链接/时间] - - Extract --> ValidateData{数据验证} - - ValidateData -->|标题为空| ErrorLog - ValidateData -->|内容为空| ErrorLog - ValidateData -->|数据完整| Continue[继续处理] - - Continue --> CheckDup{检查URL是否重复?} - - CheckDup -->|已存在| Skip[跳过该新闻] - CheckDup -->|不存在| Process[处理新闻] - - Process --> Classify[调用分类服务] - Classify --> SaveDB[保存到MySQL] - SaveDB --> UpdateCache[更新Redis缓存] - - UpdateCache --> Next{还有新闻源?} - - ErrorLog --> Next - Skip --> Next - - Next -->|是| Loop - Next -->|否| Summary[生成爬取报告] - Summary --> Finish([任务结束]) - - style Trigger fill:#e3f2fd - style Finish fill:#e8f5e9 - style Classify fill:#fff3e0 - style SaveDB fill:#f3e5f5 - style UpdateCache fill:#f3e5f5 -``` - -### 10.2 爬虫配置示例 - -```yaml -# 爬虫配置 -crawler: - enabled: true - cron: "0 */30 * * * ?" # 每30分钟执行 - user-agent: "Mozilla/5.0 ..." - timeout: 10000 # 10秒超时 - - sources: - - name: example - url: https://example.com/news - enabled: false - selector: - title: ".news-title" - content: ".news-content" - link: "a" - time: ".news-time" - - - name: tech-news - url: https://tech-news.com - enabled: false - # ... 更多配置 -``` - ---- - -## 总结 - -### 系统完整数据流 - -``` -┌─────────────────────────────────────────────────────────────────────────┐ -│ 系统完整数据流总览 │ -├─────────────────────────────────────────────────────────────────────────┤ -│ │ -│ ┌────────────────┐ ┌────────────────┐ │ -│ │ 新闻爬取流程 │ │ 用户发布流程 │ │ -│ │ • 定时任务 │ │ • 管理员操作 │ │ -│ │ • 数据清洗 │ │ • 手动输入 │ │ -│ │ • 去重验证 │ │ • 自动分类推荐│ │ -│ └───────┬────────┘ └───────┬────────┘ │ -│ │ │ │ -│ ▼ ▼ │ -│ ┌──────────────────────────────────────────────────────────────┐ │ -│ │ 文本分类服务 │ │ -│ │ ┌────────────┐ ┌────────────┐ ┌────────────┐ │ │ -│ │ │传统ML分类器│ │深度学习分类器│ │混合分类器 │ │ │ -│ │ │朴素贝叶斯 │ │BERT │ │置信度融合 │ │ │ -│ │ └────────────┘ └────────────┘ └────────────┘ │ │ -│ └──────────────────────────────┬───────────────────────────────┘ │ -│ │ │ -│ ▼ │ -│ ┌──────────────────────────────────────────────────────────────┐ │ -│ │ MySQL数据库 │ │ -│ │ • news (新闻表) │ │ -│ │ • sys_user (用户表) │ │ -│ │ • news_category (分类表) │ │ -│ │ • sys_log (日志表) │ │ -│ └──────────────────────────────┬───────────────────────────────┘ │ -│ │ │ -│ ┌───────────┴───────────┐ │ -│ ▼ ▼ │ -│ ┌──────────────────┐ ┌──────────────────┐ │ -│ │ Redis缓存 │ │ 前端客户端展示 │ │ -│ │ • 热点数据 │ │ • 新闻列表 │ │ -│ │ • 会话数据 │ │ • 新闻详情 │ │ -│ │ • 查询缓存 │ │ • 统计图表 │ │ -│ └──────────────────┘ └──────────────────┘ │ -│ │ -│ 用户交互流程: │ -│ 用户登录 → 浏览新闻 → 搜索/筛选 → 查看详情 → 收藏新闻 → 查看统计 │ -│ ▲ │ │ -│ │ JWT Token │ │ -│ └────────────────── 认证 ← Spring Security + JWT ─────────────┘ │ -│ │ -└──────────────────────────────────────────────────────────────────────────┘ -``` - -### 关键技术点总结 - -| 模块 | 关键技术 | 说明 | -|------|----------|------| -| **认证** | JWT + BCrypt | 无状态认证,密码安全加密 | -| **缓存** | Redis + Cache-Aside | 读写分离,过期策略 | -| **分类** | 朴素贝叶斯 + BERT | 传统ML与深度学习结合 | -| **爬虫** | Jsoup + HttpClient | 支持多源并行抓取 | -| **API** | RESTful + 统一响应 | 标准化接口设计 | -| **前端** | Vue 3 + Pinia | 组合式API,状态管理 | - ---- - -> 文档版本: v1.0 -> 更新日期: 2025-12-24 -> 作者: 张俊恒