feat: 修复后端cors;增加前端自定义标题栏

This commit is contained in:
shenjianZ 2026-01-12 21:15:55 +08:00
parent 8a455d95a0
commit d4eaf9909d
8 changed files with 211 additions and 3145 deletions

View File

@ -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();

View File

@ -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",

View File

@ -19,7 +19,8 @@
"minHeight": 500,
"resizable": true,
"fullscreen": false,
"decorations": true,
"decorations": false,
"transparent": false,
"alwaysOnTop": false,
"center": true
}

View File

@ -1,6 +1,7 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import { useUserStore } from '@/stores/user'
import TitleBar from '@/components/TitleBar.vue'
const userStore = useUserStore()
@ -11,7 +12,10 @@ onMounted(() => {
</script>
<template>
<router-view />
<TitleBar />
<div class="app-content">
<router-view />
</div>
</template>
<style>
@ -22,6 +26,12 @@ onMounted(() => {
padding: 0;
}
/* 应用内容区域 - 避免被自定义标题栏遮挡 */
.app-content {
height: 100%;
padding-top: 40px; /* 标题栏高度 h-10 = 40px */
}
html,
body,
#app {

View File

@ -0,0 +1,170 @@
<script setup lang="ts">
import { ref, onMounted, watch } from 'vue'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { Minus, Square, Maximize2, X } from 'lucide-vue-next'
const appWindow = getCurrentWindow()
const isMaximized = ref(false)
const isDark = ref(false)
//
const checkTheme = () => {
isDark.value = document.documentElement.classList.contains('dark')
}
//
const checkMaximized = async () => {
try {
isMaximized.value = await appWindow.isMaximized()
} catch (error) {
console.error('Failed to check maximized state:', error)
}
}
onMounted(() => {
checkMaximized()
checkTheme()
//
const observer = new MutationObserver(() => {
checkTheme()
})
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['class']
})
})
//
const minimize = async () => {
try {
await appWindow.minimize()
} catch (error) {
console.error('Failed to minimize:', error)
}
}
// /
const toggleMaximize = async () => {
try {
await appWindow.toggleMaximize()
await checkMaximized()
} catch (error) {
console.error('Failed to toggle maximize:', error)
}
}
//
const close = async () => {
try {
await appWindow.close()
} catch (error) {
console.error('Failed to close:', error)
}
}
</script>
<template>
<!-- data-tauri-drag-region: Tauri 自动处理窗口拖拽 -->
<div
data-tauri-drag-region
class="titlebar h-10 flex items-center justify-between px-3 select-none fixed top-0 left-0 right-0 z-50"
:class="{ 'titlebar-dark': isDark }"
>
<!-- 左侧标题 -->
<div data-tauri-drag-region class="flex items-center gap-2 pointer-events-none">
<div class="titlebar-icon" :class="{ 'titlebar-icon-dark': isDark }" />
<span class="titlebar-text">News Classifier</span>
</div>
<!-- 右侧窗口控制按钮 -->
<div class="flex items-center">
<!-- 最小化按钮 -->
<button
@click="minimize"
class="titlebar-btn"
title="最小化"
>
<Minus :size="14" />
</button>
<!-- 最大化/还原按钮 -->
<button
@click="toggleMaximize"
class="titlebar-btn"
:title="isMaximized ? '还原' : '最大化'"
>
<Square v-if="isMaximized" :size="12" />
<Maximize2 v-else :size="14" />
</button>
<!-- 关闭按钮 -->
<button
@click="close"
class="titlebar-btn titlebar-btn-close"
title="关闭"
>
<X :size="14" />
</button>
</div>
</div>
</template>
<style scoped>
.titlebar {
background: hsl(var(--card));
border-bottom: 1px solid hsl(var(--border));
}
.titlebar-dark {
background: hsl(var(--sidebar));
border-bottom: 1px solid hsl(var(--sidebar-border));
}
.titlebar-icon {
width: 12px;
height: 12px;
background: hsl(var(--primary));
border-radius: 2px;
}
.titlebar-icon-dark {
background: hsl(var(--sidebar-primary));
}
.titlebar-text {
font-size: 0.8125rem;
font-weight: 500;
color: hsl(var(--foreground));
}
.titlebar-btn {
padding: 0.5rem;
display: flex;
align-items: center;
justify-content: center;
color: hsl(var(--muted-foreground));
background: transparent;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background 0.15s ease, color 0.15s ease;
}
.titlebar-btn:hover {
background: hsl(var(--accent));
color: hsl(var(--foreground));
}
.titlebar-btn-close:hover {
background: hsl(var(--destructive));
color: hsl(var(--destructive-foreground));
}
/* 防止拖拽时选中文本 */
.titlebar * {
user-select: none;
-webkit-user-select: none;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -1,494 +0,0 @@
# 基于Tauri的新闻文本分类系统 - 流程图
## 1. 系统整体架构图
```mermaid
graph TB
subgraph "客户端层 - Tauri Desktop"
Client[桌面客户端<br/>Tauri + Vue3]
end
subgraph "后端服务层 - Spring Boot"
API[API 网关]
Auth[认证模块<br/>Spring Security + JWT]
NewsSvc[新闻服务]
UserSvc[用户服务]
ClassifySvc[分类服务]
CrawlerSvc[爬虫服务]
StatisticsSvc[统计服务]
end
subgraph "数据存储层"
MySQL[(MySQL数据库)]
Redis[(Redis缓存)]
end
subgraph "机器学习模块"
ML[传统ML分类器<br/>朴素贝叶斯/SVM]
DL[深度学习分类器<br/>BERT<br/>可选]
Hybrid[混合分类器<br/>置信度融合]
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<br/>{username, password}
API->>Auth: 转发登录请求
Auth->>DB: 查询用户信息
DB-->>Auth: 返回用户数据
alt 用户存在且密码正确
Auth->>Auth: 验证通过<br/>生成JWT Token
Auth->>Redis: 缓存用户会话
Auth-->>API: 返回Token和用户信息
API-->>C: 200 OK<br/>{token, user}
C->>C: 存储Token到localStorage
else 用户不存在或密码错误
Auth-->>API: 401 Unauthorized
API-->>C: 登录失败
end
Note over C,Redis: 后续请求携带Token
C->>API: 请求其他接口<br/>Authorization: Bearer {token}
API->>Auth: 验证Token
Auth->>Redis: 检查Token有效性
Redis-->>Auth: Token有效
Auth-->>API: 验证通过
API-->>C: 返回请求数据
```
## 3. 新闻爬取与自动分类流程
```mermaid
flowchart TD
Start([定时任务触发<br/>每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分类器<br/>关键词匹配]
Hybrid --> ML2[深度学习分类器<br/>BERT<br/>可选]
ML1 --> Confidence[置信度计算]
ML2 --> Confidence
Confidence --> Threshold{置信度 >= 0.75?}
Threshold -->|是| Success[分类成功]
Threshold -->|否| Default[使用默认分类<br/>科技类]
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<br/>?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<br/>?categoryCode=TECHNOLOGY
API->>DB: 按分类查询
DB-->>API: 返回分类新闻
API-->>C: 返回结果
C-->>U: 展示分类新闻
U->>C: 搜索新闻
C->>API: GET /news/search<br/>?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[输入标题、内容、<br/>选择分类、上传封面]
Input --> Option{是否自动分类?}
Option -->|是| AutoClassify[调用分类API]
Option -->|否| ManualClassify[手动选择分类]
AutoClassify --> ClassifySvc[文本分类服务]
ClassifySvc --> Hybrid{混合分类器}
Hybrid --> ML[传统ML分类]
Hybrid --> DL[深度学习分类<br/>可选]
ML --> Result[获取分类结果<br/>+ 置信度]
DL --> Result
Result --> ShowResult[显示推荐分类<br/>置信度: 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[饼图<br/>分类分布]
Line[折线图<br/>发布趋势]
Bar[柱状图<br/>热门排行]
Gauge[仪表盘<br/>准确率]
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标签<br/>去除特殊字符<br/>统一编码]
Clean --> Segment[中文分词<br/>jieba]
Segment --> Parallel{并行分类}
Parallel --> Trad[传统ML分类器<br/>TF-IDF + 朴素贝叶斯]
Parallel --> Deep[深度学习分类器<br/>BERT<br/>可选]
Trad --> TradResult[结果1: 科技<br/>置信度: 0.82]
Deep --> DeepResult[结果2: 科技<br/>置信度: 0.88]
TradResult --> Compare[置信度比较]
DeepResult --> Compare
Compare --> Select{选择最高置信度}
Select --> CheckThreshold{置信度 >= 0.75?}
CheckThreshold -->|是| Return[返回分类结果<br/>科技, 0.88, HYBRID]
CheckThreshold -->|否| Default[返回默认分类<br/>科技, 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[设置过期时间<br/>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[定时任务触发<br/>Cron: 0 */30 * * * ?] --> Config{读取配置}
Config --> GetSources[获取启用的新闻源列表]
GetSources --> Loop{遍历每个新闻源}
Loop --> Fetch[发送HTTP请求]
Fetch --> CheckResp{响应状态码 200?}
CheckResp -->|否| ErrorLog[记录错误日志]
CheckResp -->|是| Parse[解析HTML<br/>Jsoup]
Parse --> Extract[提取新闻数据<br/>标题/内容/链接/时间]
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. **数据流转**
- 爬虫 → 分类 → 数据库 → 缓存 → 客户端
- 管理员操作 → 数据库 → 缓存更新
- 用户浏览 → 缓存/数据库 → 展示

View File

@ -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[桌面客户端<br/>Tauri + Vue3]
end
subgraph "后端服务层 - Spring Boot"
API[API 网关]
Auth[认证模块<br/>Spring Security + JWT]
NewsSvc[新闻服务]
UserSvc[用户服务]
ClassifySvc[分类服务]
CrawlerSvc[爬虫服务]
StatisticsSvc[统计服务]
end
subgraph "数据存储层"
MySQL[(MySQL数据库)]
Redis[(Redis缓存)]
end
subgraph "机器学习模块"
ML[传统ML分类器<br/>朴素贝叶斯/SVM]
DL[深度学习分类器<br/>BERT<br/>可选]
Hybrid[混合分类器<br/>置信度融合]
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<br/>{username, password}
API->>Auth: 转发登录请求
Auth->>DB: 查询用户信息
DB-->>Auth: 返回用户数据
alt 用户存在且密码正确
Auth->>Auth: 验证通过<br/>生成JWT Token
Auth->>Redis: 缓存用户会话
Auth-->>API: 返回Token和用户信息
API-->>C: 200 OK<br/>{token, user}
C->>C: 存储Token到localStorage
else 用户不存在或密码错误
Auth-->>API: 401 Unauthorized
API-->>C: 登录失败
end
Note over C,Redis: 后续请求携带Token
C->>API: 请求其他接口<br/>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([定时任务触发<br/>每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分类器<br/>关键词匹配]
Hybrid --> ML2[深度学习分类器<br/>BERT<br/>可选]
ML1 --> Confidence[置信度计算]
ML2 --> Confidence
Confidence --> Threshold{置信度 >= 0.75?}
Threshold -->|是| Success[分类成功]
Threshold -->|否| Default[使用默认分类<br/>科技类]
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<br/>?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<br/>?categoryCode=TECHNOLOGY
API->>DB: 按分类查询
DB-->>API: 返回分类新闻
API-->>C: 返回结果
C-->>U: 展示分类新闻
U->>C: 搜索新闻
C->>API: GET /news/search<br/>?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[输入标题、内容、<br/>选择分类、上传封面]
Input --> Option{是否自动分类?}
Option -->|是| AutoClassify[调用分类API]
Option -->|否| ManualClassify[手动选择分类]
AutoClassify --> ClassifySvc[文本分类服务]
ClassifySvc --> Hybrid{混合分类器}
Hybrid --> ML[传统ML分类]
Hybrid --> DL[深度学习分类<br/>可选]
ML --> Result[获取分类结果<br/>+ 置信度]
DL --> Result
Result --> ShowResult[显示推荐分类<br/>置信度: 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[饼图<br/>分类分布]
Line[折线图<br/>发布趋势]
Bar[柱状图<br/>热门排行]
Gauge[仪表盘<br/>准确率]
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标签<br/>去除特殊字符<br/>统一编码]
Clean --> Segment[中文分词<br/>jieba]
Segment --> Parallel{并行分类}
Parallel --> Trad[传统ML分类器<br/>TF-IDF + 朴素贝叶斯]
Parallel --> Deep[深度学习分类器<br/>BERT<br/>可选]
Trad --> TradResult[结果1: 科技<br/>置信度: 0.82]
Deep --> DeepResult[结果2: 科技<br/>置信度: 0.88]
TradResult --> Compare[置信度比较]
DeepResult --> Compare
Compare --> Select{选择最高置信度}
Select --> CheckThreshold{置信度 >= 0.75?}
CheckThreshold -->|是| Return[返回分类结果<br/>科技, 0.88, HYBRID]
CheckThreshold -->|否| Default[返回默认分类<br/>科技, 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[设置过期时间<br/>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[定时任务触发<br/>Cron: 0 */30 * * * ?] --> Config{读取配置}
Config --> GetSources[获取启用的新闻源列表]
GetSources --> Loop{遍历每个新闻源}
Loop --> Fetch[发送HTTP请求]
Fetch --> CheckResp{响应状态码 200?}
CheckResp -->|否| ErrorLog[记录错误日志]
CheckResp -->|是| Parse[解析HTML<br/>Jsoup]
Parse --> Extract[提取新闻数据<br/>标题/内容/链接/时间]
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
> 作者: 张俊恒