Files
test-repo/docs/.vitepress/theme/components/appendix/backend-layered-architecture/ControllerLayerDemo.vue
T
sanbuphy 0eba9e87e9 fix(eslint): reduce warnings in GitHub Actions deployment
- Disable formatting rules (handled by Prettier)
- Relaxed strict Vue/JS rules for demo code compatibility
- Fix syntax errors in ApiPlayground and VoiceCloningDemo
- Fix duplicate else-if condition in ApiPlayground
- Fix Promise executor async pattern in AutoregressiveAudioDemo
- Add TypeScript file support to ESLint config

Warnings reduced from 295 to 251 problems.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-18 17:38:10 +08:00

392 lines
8.2 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="controller-layer-demo">
<div class="demo-header">
<h4>🎮 Controller 请求的"接待员"</h4>
<p class="subtitle">
点击流程节点查看 Controller 如何接收和处理请求
</p>
</div>
<div class="flow-container">
<!-- 请求发起 -->
<div class="flow-step">
<div class="step-icon">
🌐
</div>
<div class="step-content">
<div class="step-title">
客户端发起请求
</div>
<div class="step-code">
POST /api/users/register
Content-Type: application/json
{
"username": "张三",
"email": "zhangsan@example.com",
"password": "123456"
}
</div>
</div>
</div>
<div class="arrow-connector">
请求到达
</div>
<!-- Controller 接收 -->
<div
class="flow-step controller-step"
:class="{ active: showDetails === 'controller' }"
@click="toggleDetails('controller')"
>
<div class="step-icon">
🎮
</div>
<div class="step-content">
<div class="step-title">
Controller 接收并解析请求
</div>
<div class="step-code">
@RestController
@RequestMapping("/api/users")
public class UserController {
@PostMapping("/register")
public ResponseEntity&lt;UserDTO&gt; register(
@RequestBody @Valid UserRegisterRequest request
) {
// 调用 Service 处理业务
UserDTO user = userService.register(request);
return ResponseEntity.ok(user);
}
}
</div>
</div>
</div>
<div class="arrow-connector">
参数校验 + 调用
</div>
<!-- 校验逻辑 -->
<div
class="flow-step validation-step"
:class="{ active: showDetails === 'validation' }"
@click="toggleDetails('validation')"
>
<div class="step-icon">
</div>
<div class="step-content">
<div class="step-title">
参数校验Controller 的职责之一
</div>
<div class="step-code">
public class UserRegisterRequest {
@NotBlank(message = "用户名不能为空")
@Size(min = 2, max = 20, message = "用户名长度2-20")
private String username;
@Email(message = "邮箱格式不正确")
private String email;
@Size(min = 6, message = "密码至少6位")
private String password;
}
</div>
<div
v-if="showDetails === 'validation'"
class="detail-panel"
>
<h5>为什么校验要放在 Controller</h5>
<ul>
<li>🛡 第一道防线尽早拦截非法请求</li>
<li>📦 减轻下游压力Service 层可以假设数据已清洗</li>
<li>🔧 关注点分离Service 专注于业务不处理格式验证</li>
</ul>
</div>
</div>
</div>
<div class="arrow-connector">
返回结果
</div>
<!-- 响应返回 -->
<div class="flow-step">
<div class="step-icon">
📤
</div>
<div class="step-content">
<div class="step-title">
Controller 封装响应返回给客户端
</div>
<div class="step-code">
HTTP/1.1 200 OK
Content-Type: application/json
{
"code": 200,
"message": "注册成功",
"data": {
"id": 10001,
"username": "张三",
"email": "zhangsan@example.com",
"createdAt": "2024-01-15T10:30:00Z"
}
}
</div>
</div>
</div>
</div>
<!-- Controller 职责总结 -->
<div class="controller-summary">
<h5>🎯 Controller 的核心职责</h5>
<div class="duty-grid">
<div class="duty-item">
<div class="duty-icon">
📡
</div>
<div class="duty-title">
接收请求
</div>
<div class="duty-desc">
映射 HTTP 请求到方法
</div>
</div>
<div class="duty-item">
<div class="duty-icon">
</div>
<div class="duty-title">
参数校验
</div>
<div class="duty-desc">
基础格式和必填校验
</div>
</div>
<div class="duty-item">
<div class="duty-icon">
🔄
</div>
<div class="duty-title">
调用 Service
</div>
<div class="duty-desc">
将请求转发给业务层
</div>
</div>
<div class="duty-item">
<div class="duty-icon">
📦
</div>
<div class="duty-title">
封装响应
</div>
<div class="duty-desc">
统一响应格式返回
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const showDetails = ref('')
const toggleDetails = (section) => {
showDetails.value = showDetails.value === section ? '' : section
}
</script>
<style scoped>
.controller-layer-demo {
padding: 24px;
background: linear-gradient(135deg, #f0f7ff 0%, #e6f0ff 100%);
border-radius: 12px;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}
.demo-header {
text-align: center;
margin-bottom: 24px;
}
.demo-header h4 {
margin: 0 0 8px 0;
color: #1a1a2e;
font-size: 18px;
}
.subtitle {
margin: 0;
color: #666;
font-size: 13px;
}
.flow-container {
display: flex;
flex-direction: column;
gap: 12px;
}
.flow-step {
display: flex;
gap: 16px;
padding: 16px;
background: white;
border-radius: 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
transition: all 0.3s ease;
cursor: pointer;
}
.flow-step:hover {
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);
}
.flow-step.active {
border: 2px solid #409eff;
box-shadow: 0 0 0 3px rgba(64, 158, 255, 0.2);
}
.controller-step {
border-left: 4px solid #67c23a;
}
.validation-step {
border-left: 4px solid #e6a23c;
}
.step-icon {
font-size: 24px;
flex-shrink: 0;
}
.step-content {
flex: 1;
}
.step-title {
font-weight: 600;
color: #303133;
margin-bottom: 8px;
font-size: 14px;
}
.step-code {
background: #f8f9fa;
padding: 12px;
border-radius: 6px;
font-family: 'Monaco', 'Menlo', monospace;
font-size: 11px;
color: #333;
white-space: pre-wrap;
line-height: 1.5;
}
.arrow-connector {
text-align: center;
padding: 8px;
font-size: 12px;
color: #909399;
font-weight: 500;
}
.detail-panel {
margin-top: 12px;
padding: 16px;
background: #f0f7ff;
border-radius: 6px;
border-left: 4px solid #409eff;
}
.detail-panel h5 {
margin: 0 0 12px 0;
color: #1a1a2e;
font-size: 14px;
}
.detail-panel ul {
margin: 0;
padding-left: 20px;
}
.detail-panel li {
margin: 6px 0;
color: #606266;
font-size: 12px;
line-height: 1.6;
}
.controller-summary {
margin-top: 24px;
padding: 20px;
background: white;
border-radius: 10px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
}
.controller-summary h5 {
margin: 0 0 16px 0;
color: #1a1a2e;
font-size: 15px;
text-align: center;
}
.duty-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
}
.duty-item {
text-align: center;
padding: 16px 12px;
background: #f8f9fa;
border-radius: 6px;
transition: all 0.3s ease;
}
.duty-item:hover {
background: #e6f7ff;
transform: translateY(-2px);
}
.duty-icon {
font-size: 28px;
margin-bottom: 8px;
}
.duty-title {
font-weight: 600;
color: #303133;
font-size: 13px;
margin-bottom: 4px;
}
.duty-desc {
color: #909399;
font-size: 11px;
}
@media (max-width: 768px) {
.duty-grid {
grid-template-columns: repeat(2, 1fr);
}
.flow-step {
flex-direction: column;
gap: 8px;
}
.step-content {
width: 100%;
}
}
</style>