当前位置: 首页 > news >正文

.NetCore+Vue快速生产框架开发详细方案

文章目录

    • 1. 项目概述
      • 1.1 项目背景
      • 1.2 项目目标
      • 1.3 核心功能
    • 2. 技术栈选择
      • 2.1 后端技术栈
      • 2.2 前端技术栈
      • 2.3 开发工具
    • 3. 系统架构设计
      • 3.1 整体架构
      • 3.2 后端架构设计
      • 3.3 前端架构设计
      • 3.4 微服务考虑
    • 4. 后端.NET核心设计
      • 4.1 项目结构
      • 4.2 核心模块设计
        • 4.2.1 用户模块
        • 4.2.2 角色权限模块
        • 4.2.3 代码生成模块
      • 4.3 基础设施实现
        • 4.3.1 数据访问层
        • 4.3.2 缓存实现
      • 4.4 Web API设计
        • 4.4.1 控制器基类
        • 4.4.2 用户控制器示例
      • 4.5 中间件设计
        • 4.5.1 异常处理中间件
        • 4.5.2 性能监控中间件
    • 5. 前端Vue架构设计
      • 5.1 项目结构
      • 5.2 核心模块实现
        • 5.2.1 API服务封装
        • 5.2.2 状态管理(Pinia)
        • 5.2.3 路由配置
        • 5.2.4 权限指令
      • 5.3 组件设计
        • 5.3.1 通用表格组件
        • 5.3.2 表单对话框组件
      • 5.4 页面示例
        • 5.4.1 用户管理页面
        • 5.4.2 用户表单对话框
    • 6. 数据库设计
      • 6.1 核心表结构
        • 6.1.1 用户表(Users)
        • 6.1.2 角色表(Roles)
        • 6.1.3 权限表(Permissions)
        • 6.1.4 用户角色关联表(UserRoles)
        • 6.1.5 角色权限关联表(RolePermissions)
        • 6.1.6 系统日志表(SystemLogs)
      • 6.2 索引设计
      • 6.3 数据初始化脚本
    • 7. API设计规范
      • 7.1 RESTful API设计原则
      • 7.2 请求与响应规范
        • 7.2.1 请求头
        • 7.2.2 查询参数
        • 7.2.3 响应格式
      • 7.3 版本控制
      • 7.4 数据格式
      • 7.5 错误代码
    • 8. 认证与授权方案
      • 8.1 认证流程
      • 8.2 JWT实现
      • 8.3 授权策略
      • 8.4 刷新令牌机制
    • 9. 异常处理与日志系统
      • 9.1 异常处理策略
        • 9.1.1 自定义异常体系
        • 9.1.2 全局异常处理
      • 9.2 日志系统设计
        • 9.2.1 日志配置
        • 9.2.2 结构化日志
        • 9.2.3 请求日志中间件
    • 10. 性能优化策略
      • 10.1 数据库优化
        • 10.1.1 查询优化
        • 10.1.2 索引优化
        • 10.1.3 批量操作
      • 10.2 缓存策略
        • 10.2.1 多级缓存
        • 10.2.2 缓存失效策略
      • 10.3 响应压缩
      • 10.4 异步编程
      • 10.5 性能监控
    • 11. 测试策略
      • 11.1 测试金字塔
      • 11.2 单元测试
        • 11.2.1 测试示例
        • 11.2.2 测试覆盖率目标
      • 11.3 集成测试
        • 11.3.1 API测试
        • 11.3.2 数据库测试
      • 11.4 端到端测试
        • 11.4.1 UI测试
        • 11.4.2 API契约测试
      • 11.5 性能测试
    • 12. 部署方案
      • 12.1 部署架构
      • 12.2 Docker化
        • 12.2.1 后端Dockerfile
        • 12.2.2 前端Dockerfile
        • 12.2.3 Nginx配置
      • 12.3 Kubernetes部署
        • 12.3.1 后端部署
        • 12.3.2 前端部署
      • 12.4 CI/CD流程
        • 12.4.1 GitHub Actions工作流
    • 13. 监控与运维
      • 13.1 监控体系
        • 13.1.1 应用性能监控(APM)
        • 13.1.2 健康检查
      • 13.2 日志聚合
        • 13.2.1 ELK Stack配置
        • 13.2.2 Logstash配置
      • 13.3 告警系统
        • 13.3.1 Prometheus + Alertmanager
        • 13.3.2 告警规则
      • 13.4 运维工具
        • 13.4.1 数据库迁移
        • 13.4.2 维护任务
    • 14. 开发流程与规范
      • 14.1 Git工作流
      • 14.2 代码审查
      • 14.3 编码规范
        • 14.3.1 C#编码规范
        • 14.3.2 TypeScript/Vue编码规范
      • 14.4 提交消息规范
    • 15. 安全考虑
      • 15.1 安全防护措施
        • 15.1.1 输入验证
        • 15.1.2 输出编码
        • 15.1.3 CSRF防护
      • 15.2 安全头配置
      • 15.3 安全审计
        • 15.3.1 依赖安全检查
        • 15.3.2 静态代码分析
    • 16. 扩展性设计
      • 16.1 插件系统设计
      • 16.2 模块化设计
        • 16.2.1 模块定义
        • 16.2.2 用户模块示例
      • 16.3 动态功能开关
    • 17. 项目里程碑
      • 17.1 开发阶段
      • 17.2 发布计划
      • 17.3 迭代计划
    • 18. 团队分工
      • 18.1 角色与职责
      • 18.2 协作流程
      • 18.3 沟通工具
    • 19. 风险评估
      • 19.1 技术风险
      • 19.2 项目风险
      • 19.3 运营风险
    • 20. 附录
      • 20.1 技术参考资料
      • 20.2 工具和资源
      • 20.3 术语表

1. 项目概述

1.1 项目背景

随着企业数字化转型加速,快速开发高质量应用的需求日益增长。传统开发模式存在开发周期长、维护成本高、技术栈不统一等问题。本项目旨在构建一个基于.NET后端和Vue前端的快速生产框架,提供标准化、模块化的开发解决方案,显著提升开发效率,降低技术门槛。

1.2 项目目标

  1. 开发一个可快速生成企业级应用的基础框架
  2. 实现前后端分离架构,支持高并发、高可用性
  3. 提供丰富的内置功能模块,减少重复开发
  4. 建立完善的开发规范和最佳实践
  5. 支持快速部署和持续集成/持续交付(CI/CD)
  6. 确保系统安全性和可扩展性

1.3 核心功能

  1. 用户管理:注册、登录、权限控制
  2. 角色权限:基于角色的访问控制(RBAC)
  3. 代码生成:自动生成基础CRUD代码
  4. 数据管理:通用数据访问层
  5. 文件服务:统一文件上传下载
  6. 日志系统:操作日志、异常日志
  7. 消息通知:站内信、邮件通知
  8. 系统监控:性能监控、健康检查
  9. API文档:自动化API文档生成
  10. 配置中心:统一配置管理

2. 技术栈选择

2.1 后端技术栈

  1. 核心框架:.NET 6/7
    • 选择理由:长期支持版本,性能优化,跨平台支持
  2. Web框架:ASP.NET Core
    • 选择理由:高性能、模块化、支持RESTful
  3. ORM框架:Entity Framework Core
    • 选择理由:LINQ支持,代码优先,迁移方便
  4. 依赖注入:内置IoC容器
    • 选择理由:轻量级,与ASP.NET Core深度集成
  5. 缓存系统:Redis
    • 选择理由:高性能,支持分布式缓存
  6. 消息队列:RabbitMQ
    • 选择理由:稳定可靠,社区支持好
  7. 日志系统:Serilog + ELK
    • 选择理由:结构化日志,便于分析
  8. API文档:Swagger/OpenAPI
    • 选择理由:行业标准,自动生成文档
  9. 认证授权:IdentityServer4
    • 选择理由:OAuth2/OpenID Connect实现
  10. 测试框架:xUnit + Moq
    • 选择理由:.NET生态主流选择

2.2 前端技术栈

  1. 核心框架:Vue 3
    • 选择理由:组合式API,更好的TypeScript支持
  2. 状态管理:Pinia
    • 选择理由:Vue官方推荐,替代Vuex
  3. UI组件库:Element Plus
    • 选择理由:丰富组件,良好文档,企业级应用验证
  4. 路由:Vue Router 4
    • 选择理由:Vue官方路由解决方案
  5. HTTP客户端:Axios
    • 选择理由:广泛使用,拦截器支持
  6. 构建工具:Vite
    • 选择理由:极速启动,热更新快
  7. CSS预处理器:Sass
    • 选择理由:变量、混入等高级功能
  8. 代码规范:ESLint + Prettier
    • 选择理由:统一代码风格,提高可维护性
  9. 可视化:ECharts
    • 选择理由:丰富图表类型,高度可定制
  10. 富文本编辑器:Quill
    • 选择理由:轻量级,扩展性强

2.3 开发工具

  1. IDE
    • Visual Studio 2022 (后端)
    • VS Code (前端)
  2. 数据库工具
    • SQL Server Management Studio
    • Azure Data Studio
  3. 版本控制:Git + GitHub/GitLab
  4. CI/CD:GitHub Actions/Jenkins
  5. 容器化:Docker + Kubernetes
  6. 项目管理:Jira/禅道
  7. 文档协作:Confluence/Wiki
  8. API测试:Postman/Insomnia

3. 系统架构设计

3.1 整体架构

采用分层架构设计,确保各层职责清晰,耦合度低:

┌─────────────────────────────────────────────────┐
│                   客户端层                      │
│  (Web, Mobile, Desktop, 第三方系统)            │
└───────────────┬───────────────────┬────────────┘│                   │▼                   ▼
┌─────────────────────────────────────────────────┐
│                   表现层                        │
│  (Vue前端应用, API Gateway)                     │
└───────────────┬───────────────────┬────────────┘│                   │▼                   ▼
┌─────────────────────────────────────────────────┐
│                   应用层                        │
│  (Web API, 业务逻辑, DTO转换)                   │
└───────────────┬───────────────────┬────────────┘│                   │▼                   ▼
┌─────────────────────────────────────────────────┐
│                   领域层                        │
│  (领域模型, 领域服务, 仓储接口)                  │
└───────────────┬───────────────────┬────────────┘│                   │▼                   ▼
┌─────────────────────────────────────────────────┐
│                   基础设施层                     │
│  (数据访问, 文件存储, 缓存, 消息队列)            │
└───────────────┬───────────────────┬────────────┘│                   │▼                   ▼
┌─────────────────────────────────────────────────┐
│                   数据存储层                     │
│  (关系数据库, NoSQL, 文件系统)                   │
└─────────────────────────────────────────────────┘

3.2 后端架构设计

采用清洁架构(Onion Architecture)原则:

  1. 核心领域层:包含业务实体和核心业务逻辑
  2. 应用服务层:协调领域对象完成用例
  3. 接口适配层:包含Web API、身份验证等
  4. 基础设施层:实现持久化、外部服务等

3.3 前端架构设计

采用模块化、组件化设计:

  1. 核心模块:基础配置、工具函数、全局样式
  2. 业务模块:按功能划分的独立模块
  3. 共享组件:可复用的UI组件
  4. 状态管理:集中式状态管理
  5. 路由系统:基于权限的动态路由

3.4 微服务考虑

虽然初始版本采用单体架构,但设计上为未来微服务化预留扩展点:

  1. 使用清晰的接口定义服务边界
  2. 避免紧耦合的跨服务调用
  3. 每个模块有独立的数据上下文
  4. 使用事件驱动架构减少直接依赖

4. 后端.NET核心设计

4.1 项目结构

FastProductionFramework/
├── src/
│   ├── FastProductionFramework.Core/          # 核心领域模型
│   ├── FastProductionFramework.Application/   # 应用服务层
│   ├── FastProductionFramework.Infrastructure/ # 基础设施实现
│   ├── FastProductionFramework.WebApi/        # Web API项目
│   └── FastProductionFramework.Tests/         # 单元测试
├── docs/                                      # 文档
└── scripts/                                   # 部署脚本

4.2 核心模块设计

4.2.1 用户模块
public class User : EntityBase
{public string Username { get; set; }public string Email { get; set; }public string PasswordHash { get; set; }public string PhoneNumber { get; set; }public bool IsActive { get; set; }public DateTime? LastLoginTime { get; set; }public virtual ICollection<UserRole> UserRoles { get; set; }
}public interface IUserService
{Task<UserDto> AuthenticateAsync(string username, string password);Task<UserDto> GetByIdAsync(int id);Task<PagedResult<UserDto>> GetAllAsync(int pageNumber, int pageSize);Task<UserDto> CreateAsync(CreateUserDto userDto);Task UpdateAsync(int id, UpdateUserDto userDto);Task DeleteAsync(int id);
}
4.2.2 角色权限模块
public class Role : EntityBase
{public string Name { get; set; }public string Description { get; set; }public virtual ICollection<RolePermission> RolePermissions { get; set; }public virtual ICollection<UserRole> UserRoles { get; set; }
}public class Permission : EntityBase
{public string Name { get; set; }public string Code { get; set; }public string Module { get; set; }public virtual ICollection<RolePermission> RolePermissions { get; set; }
}public interface IRoleService
{Task AssignPermissionsToRoleAsync(int roleId, IEnumerable<int> permissionIds);Task<bool> CheckPermissionAsync(int userId, string permissionCode);
}
4.2.3 代码生成模块
public class CodeGenerator
{public async Task GenerateEntityAsync(string entityName, IEnumerable<PropertyDefinition> properties){// 生成实体类// 生成DbContext配置// 生成仓储接口// 生成服务接口// 生成DTOs// 生成控制器}public async Task GenerateCrudApiAsync(string entityName){// 生成完整的CRUD API}
}public class PropertyDefinition
{public string Name { get; set; }public string Type { get; set; }public bool IsRequired { get; set; }public int? MaxLength { get; set; }
}

4.3 基础设施实现

4.3.1 数据访问层
public class Repository<T> : IRepository<T> where T : class
{private readonly AppDbContext _context;public Repository(AppDbContext context){_context = context;}public async Task<T> GetByIdAsync(int id){return await _context.Set<T>().FindAsync(id);}public async Task<IEnumerable<T>> GetAllAsync(){return await _context.Set<T>().ToListAsync();}public async Task AddAsync(T entity){await _context.Set<T>().AddAsync(entity);await _context.SaveChangesAsync();}// 其他CRUD方法...
}public class UnitOfWork : IUnitOfWork
{private readonly AppDbContext _context;public UnitOfWork(AppDbContext context){_context = context;}public async Task<int> CommitAsync(){return await _context.SaveChangesAsync();}public void Rollback(){_context.ChangeTracker.Entries().ToList().ForEach(x => x.Reload());}
}
4.3.2 缓存实现
public class RedisCacheService : ICacheService
{private readonly IConnectionMultiplexer _redis;public RedisCacheService(IConnectionMultiplexer redis){_redis = redis;}public async Task<T> GetAsync<T>(string key){var db = _redis.GetDatabase();var value = await db.StringGetAsync(key);return value.HasValue ? JsonSerializer.Deserialize<T>(value) : default;}public async Task SetAsync<T>(string key, T value, TimeSpan? expiry = null){var db = _redis.GetDatabase();await db.StringSetAsync(key, JsonSerializer.Serialize(value), expiry);}public async Task RemoveAsync(string key){var db = _redis.GetDatabase();await db.KeyDeleteAsync(key);}
}

4.4 Web API设计

4.4.1 控制器基类
[ApiController]
[Route("api/[controller]")]
[Authorize]
public abstract class BaseApiController : ControllerBase
{protected readonly IMediator _mediator;protected BaseApiController(IMediator mediator){_mediator = mediator;}protected IActionResult HandleResult<T>(Result<T> result){if (result == null) return NotFound();if (result.IsSuccess && result.Value != null)return Ok(result.Value);if (result.IsSuccess && result.Value == null)return NotFound();return BadRequest(result.Error);}
}
4.4.2 用户控制器示例
public class UsersController : BaseApiController
{[HttpGet][Authorize(Permissions.Users.View)]public async Task<ActionResult<PagedResult<UserDto>>> GetAll([FromQuery] UserQueryParams queryParams){var query = new GetUsersQuery { QueryParams = queryParams };var result = await _mediator.Send(query);return HandleResult(result);}[HttpGet("{id}")][Authorize(Permissions.Users.View)]public async Task<ActionResult<UserDto>> Get(int id){var query = new GetUserQuery { Id = id };var result = await _mediator.Send(query);return HandleResult(result);}[HttpPost][Authorize(Permissions.Users.Create)]public async Task<ActionResult<UserDto>> Create(CreateUserCommand command){var result = await _mediator.Send(command);return HandleResult(result);}// 其他CRUD端点...
}

4.5 中间件设计

4.5.1 异常处理中间件
public class ExceptionMiddleware
{private readonly RequestDelegate _next;private readonly ILogger<ExceptionMiddleware> _logger;public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger){_next = next;_logger = logger;}public async Task InvokeAsync(HttpContext context){try{await _next(context);}catch (Exception ex){_logger.LogError(ex, "An unhandled exception has occurred");await HandleExceptionAsync(context, ex);}}private static Task HandleExceptionAsync(HttpContext context, Exception exception){context.Response.ContentType = "application/json";var statusCode = exception switch{ValidationException => StatusCodes.Status400BadRequest,NotFoundException => StatusCodes.Status404NotFound,UnauthorizedAccessException => StatusCodes.Status401Unauthorized,_ => StatusCodes.Status500InternalServerError};context.Response.StatusCode = statusCode;var response = new{status = statusCode,message = exception.Message,details = exception is ValidationException validationException ? validationException.Errors : null};return context.Response.WriteAsync(JsonSerializer.Serialize(response));}
}
4.5.2 性能监控中间件
public class PerformanceMiddleware
{private readonly RequestDelegate _next;private readonly ILogger<PerformanceMiddleware> _logger;private readonly Stopwatch _stopwatch;public PerformanceMiddleware(RequestDelegate next, ILogger<PerformanceMiddleware> logger){_next = next;_logger = logger;_stopwatch = new Stopwatch();}public async Task InvokeAsync(HttpContext context){_stopwatch.Restart();await _next(context);_stopwatch.Stop();if (_stopwatch.ElapsedMilliseconds > 500){_logger.LogWarning("Request {Method} {Path} took {Elapsed}ms",context.Request.Method,context.Request.Path,_stopwatch.ElapsedMilliseconds);}}
}

5. 前端Vue架构设计

5.1 项目结构

fast-production-frontend/
├── public/                  # 静态资源
├── src/
│   ├── api/                 # API请求封装
│   ├── assets/              # 静态资源
│   ├── components/          # 公共组件
│   │   ├── common/          # 通用组件
│   │   ├── layout/          # 布局组件
│   │   └── ui/             # UI组件
│   ├── composables/         # 组合式函数
│   ├── directives/          # 自定义指令
│   ├── router/              # 路由配置
│   ├── stores/              # Pinia状态管理
│   ├── styles/              # 全局样式
│   ├── utils/               # 工具函数
│   ├── views/               # 页面组件
│   │   ├── auth/            # 认证相关页面
│   │   ├── dashboard/       # 仪表盘
│   │   ├── system/          # 系统管理
│   │   └── ...              # 其他业务模块
│   ├── App.vue              # 根组件
│   └── main.ts              # 应用入口
├── .env                     # 环境变量
├── vite.config.ts           # Vite配置
└── tsconfig.json            # TypeScript配置

5.2 核心模块实现

5.2.1 API服务封装
// src/api/http.ts
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
import { useAuthStore } from '@/stores/auth';const http: AxiosInstance = axios.create({baseURL: import.meta.env.VITE_API_BASE_URL,timeout: 10000,
});// 请求拦截器
http.interceptors.request.use((config: AxiosRequestConfig) => {const authStore = useAuthStore();if (authStore.token) {config.headers = config.headers || {};config.headers['Authorization'] = `Bearer ${authStore.token}`;}return config;
});// 响应拦截器
http.interceptors.response.use((response: AxiosResponse) => {return response.data;},(error) => {if (error.response?.status === 401) {const authStore = useAuthStore();authStore.logout();window.location.href = '/login';}const message = error.response?.data?.message || error.message;console.error('API Error:', message);return Promise.reject(error);}
);export default http;// src/api/user.ts
import http from './http';export interface User {id: number;username: string;email: string;roles: string[];
}export const getUser = (id: number): Promise<User> => {return http.get(`/users/${id}`);
};export const getUsers = (params: any): Promise<PaginatedResult<User>> => {return http.get('/users', { params });
};export const createUser = (data: any): Promise<User> => {return http.post('/users', data);
};// 其他API函数...
5.2.2 状态管理(Pinia)
// src/stores/auth.ts
import { defineStore } from 'pinia';
import { login, logout, getUserInfo } from '@/api/auth';
import { User } from '@/api/user';interface AuthState {token: string | null;user: User | null;
}export const useAuthStore = defineStore('auth', {state: (): AuthState => ({token: localStorage.getItem('token'),user: null,}),getters: {isAuthenticated: (state) => !!state.token,roles: (state) => state.user?.roles || [],},actions: {async login(credentials: { username: string; password: string }) {const { token } = await login(credentials);this.token = token;localStorage.setItem('token', token);await this.fetchUser();},async fetchUser() {if (this.token) {this.user = await getUserInfo();}},async logout() {await logout();this.token = null;this.user = null;localStorage.removeItem('token');},},
});
5.2.3 路由配置
// src/router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
import { useAuthStore } from '@/stores/auth';const routes: RouteRecordRaw[] = [{path: '/',component: () => import('@/layouts/MainLayout.vue'),meta: { requiresAuth: true },children: [{path: '',name: 'Dashboard',component: () => import('@/views/dashboard/DashboardView.vue'),},{path: 'users',name: 'Users',component: () => import('@/views/system/user/UserList.vue'),meta: { permission: 'users.view' },},// 其他路由...],},{path: '/login',name: 'Login',component: () => import('@/views/auth/LoginView.vue'),meta: { guestOnly: true },},
];const router = createRouter({history: createWebHistory(),routes,
});router.beforeEach(async (to, from, next) => {const authStore = useAuthStore();// 如果路由需要认证但用户未登录if (to.meta.requiresAuth && !authStore.isAuthenticated) {return next({ name: 'Login', query: { redirect: to.fullPath } });}// 如果路由仅允许游客访问但用户已登录if (to.meta.guestOnly && authStore.isAuthenticated) {return next({ name: 'Dashboard' });}// 检查权限if (to.meta.permission) {const hasPermission = await authStore.checkPermission(to.meta.permission);if (!hasPermission) {return next({ name: 'Forbidden' });}}next();
});export default router;
5.2.4 权限指令
// src/directives/permission.ts
import { Directive } from 'vue';
import { useAuthStore } from '@/stores/auth';export const permissionDirective: Directive = {mounted(el, binding) {const authStore = useAuthStore();const { value } = binding;if (value) {const hasPermission = authStore.roles.some(role => {return role.permissions.some(permission => permission.code === value);});if (!hasPermission) {el.parentNode?.removeChild(el);}}},
};// 在main.ts中注册
import { permissionDirective } from '@/directives/permission';app.directive('permission', permissionDirective);// 使用示例
<button v-permission="'users.create'">创建用户</button>

5.3 组件设计

5.3.1 通用表格组件
<!-- src/components/common/DataTable.vue -->
<template><el-table:data="tableData"v-loading="loading"@sort-change="handleSortChange"@selection-change="handleSelectionChange"><el-table-columnv-if="selectable"type="selection"width="55"/><el-table-columnv-for="column in columns":key="column.prop":prop="column.prop":label="column.label":sortable="column.sortable":width="column.width"><template #default="scope"><slot :name="column.prop" :row="scope.row">{{ scope.row[column.prop] }}</slot></template></el-table-column><el-table-columnv-if="hasActions"label="操作"width="180"><template #default="scope"><slot name="actions" :row="scope.row" /></template></el-table-column></el-table><div class="pagination" v-if="showPagination"><el-paginationv-model:current-page="currentPage"v-model:page-size="pageSize":total="total":page-sizes="[10, 20, 50, 100]"layout="total, sizes, prev, pager, next, jumper"@size-change="handleSizeChange"@current-change="handleCurrentChange"/></div>
</template><script lang="ts" setup>
import { ref, computed } from 'vue';interface Column {prop: string;label: string;sortable?: boolean;width?: string;
}const props = defineProps({columns: {type: Array as () => Column[],required: true,},tableData: {type: Array,default: () => [],},loading: {type: Boolean,default: false,},selectable: {type: Boolean,default: false,},total: {type: Number,default: 0,},showPagination: {type: Boolean,default: true,},
});const emit = defineEmits(['sort-change','selection-change','page-change','size-change',
]);const currentPage = ref(1);
const pageSize = ref(10);const hasActions = computed(() => {return !!slots.actions;
});const handleSortChange = (sort: any) => {emit('sort-change', sort);
};const handleSelectionChange = (selection: any[]) => {emit('selection-change', selection);
};const handleSizeChange = (size: number) => {pageSize.value = size;emit('size-change', size);emit('page-change', { page: currentPage.value, size });
};const handleCurrentChange = (page: number) => {currentPage.value = page;emit('page-change', { page, size: pageSize.value });
};
</script><style scoped>
.pagination {margin-top: 16px;display: flex;justify-content: flex-end;
}
</style>
5.3.2 表单对话框组件
<!-- src/components/common/FormDialog.vue -->
<template><el-dialogv-model="visible":title="title":width="width":close-on-click-modal="false"@closed="handleClosed"><el-formref="formRef":model="formData":rules="rules"label-width="120px"><slot :form="formData" /></el-form><template #footer><el-button @click="visible = false">取消</el-button><el-buttontype="primary":loading="submitting"@click="handleSubmit">确认</el-button></template></el-dialog>
</template><script lang="ts" setup>
import { ref, watch } from 'vue';
import { ElForm, ElMessage } from 'element-plus';interface Props {modelValue: boolean;title: string;formData: Record<string, any>;rules?: Record<string, any>;width?: string;
}const props = withDefaults(defineProps<Props>(), {rules: () => ({}),width: '50%',
});const emit = defineEmits(['update:modelValue','submit','closed',
]);const visible = ref(props.modelValue);
const formRef = ref<InstanceType<typeof ElForm>>();
const submitting = ref(false);watch(() => props.modelValue, (val) => {visible.value = val;
});watch(visible, (val) => {emit('update:modelValue', val);
});const handleSubmit = async () => {try {const valid = await formRef.value?.validate();if (valid) {submitting.value = true;await emit('submit', props.formData);visible.value = false;}} catch (error) {console.error('Form validation failed:', error);} finally {submitting.value = false;}
};const handleClosed = () => {formRef.value?.resetFields();emit('closed');
};
</script>

5.4 页面示例

5.4.1 用户管理页面
<!-- src/views/system/user/UserList.vue -->
<template><div class="user-list"><el-card><template #header><div class="card-header"><span>用户管理</span><div><el-buttontype="primary"v-permission="'users.create'"@click="handleCreate">新增用户</el-button></div></div></template><el-form :model="queryParams" inline><el-form-item label="用户名"><el-inputv-model="queryParams.username"placeholder="请输入用户名"clearable/></el-form-item><el-form-item label="状态"><el-selectv-model="queryParams.status"placeholder="请选择状态"clearable><el-optionv-for="item in statusOptions":key="item.value":label="item.label":value="item.value"/></el-select></el-form-item><el-form-item><el-button type="primary" @click="handleQuery">查询</el-button><el-button @click="resetQuery">重置</el-button></el-form-item></el-form><data-table:columns="columns":table-data="userList":total="total":loading="loading"@page-change="handlePageChange"><template #status="{ row }"><el-tag :type="row.status === 1 ? 'success' : 'danger'">{{ row.status === 1 ? '启用' : '禁用' }}</el-tag></template><template #actions="{ row }"><el-buttonsize="small"v-permission="'users.update'"@click="handleEdit(row)">编辑</el-button><el-buttonsize="small"type="danger"v-permission="'users.delete'"@click="handleDelete(row)">删除</el-button></template></data-table></el-card><user-form-dialogv-model="dialogVisible":title="dialogTitle":form-data="formData"@submit="handleSubmit"/></div>
</template><script lang="ts" setup>
import { ref, reactive, onMounted } from 'vue';
import { getUsers, createUser, updateUser, deleteUser } from '@/api/user';
import DataTable from '@/components/common/DataTable.vue';
import UserFormDialog from './UserFormDialog.vue';interface User {id: number;username: string;email: string;phone: string;status: number;roles: number[];createdAt: string;
}const columns = [{ prop: 'username', label: '用户名' },{ prop: 'email', label: '邮箱' },{ prop: 'phone', label: '手机号' },{ prop: 'status', label: '状态' },{ prop: 'createdAt', label: '创建时间', sortable: true },
];const statusOptions = [{ value: 1, label: '启用' },{ value: 0, label: '禁用' },
];const queryParams = reactive({username: '',status: undefined,page: 1,size: 10,sort: '',
});const userList = ref<User[]>([]);
const total = ref(0);
const loading = ref(false);const dialogVisible = ref(false);
const dialogTitle = ref('');
const formData = reactive({id: undefined,username: '',email: '',phone: '',password: '',status: 1,roles: [],
});const fetchUsers = async () => {try {loading.value = true;const { data, total: totalCount } = await getUsers(queryParams);userList.value = data;total.value = totalCount;} finally {loading.value = false;}
};const handleQuery = () => {queryParams.page = 1;fetchUsers();
};const resetQuery = () => {Object.assign(queryParams, {username: '',status: undefined,page: 1,sort: '',});fetchUsers();
};const handlePageChange = ({ page, size }: { page: number; size: number }) => {queryParams.page = page;queryParams.size = size;fetchUsers();
};const handleCreate = () => {dialogTitle.value = '新增用户';Object.assign(formData, {id: undefined,username: '',email: '',phone: '',password: '',status: 1,roles: [],});dialogVisible.value = true;
};const handleEdit = (row: User) => {dialogTitle.value = '编辑用户';Object.assign(formData, {id: row.id,username: row.username,email: row.email,phone: row.phone,password: '',status: row.status,roles: row.roles,});dialogVisible.value = true;
};const handleDelete = async (row: User) => {try {await ElMessageBox.confirm('确认删除该用户吗?', '提示', {type: 'warning',});await deleteUser(row.id);ElMessage.success('删除成功');fetchUsers();} catch (error) {console.error('Delete user failed:', error);}
};const handleSubmit = async (data: any) => {try {if (data.id) {await updateUser(data.id, data);ElMessage.success('更新成功');} else {await createUser(data);ElMessage.success('创建成功');}dialogVisible.value = false;fetchUsers();} catch (error) {console.error('Submit user failed:', error);}
};onMounted(() => {fetchUsers();
});
</script><style scoped>
.card-header {display: flex;justify-content: space-between;align-items: center;
}
</style>
5.4.2 用户表单对话框
<!-- src/views/system/user/UserFormDialog.vue -->
<template><form-dialogv-model="visible":title="title":form-data="formData":rules="rules"@submit="$emit('submit', formData)"><el-form-item label="用户名" prop="username"><el-input v-model="formData.username" /></el-form-item><el-form-item label="邮箱" prop="email"><el-input v-model="formData.email" /></el-form-item><el-form-item label="手机号" prop="phone"><el-input v-model="formData.phone" /></el-form-item><el-form-item label="密码" prop="password" v-if="!formData.id"><el-input v-model="formData.password" type="password" /></el-form-item><el-form-item label="状态" prop="status"><el-radio-group v-model="formData.status"><el-radio :label="1">启用</el-radio><el-radio :label="0">禁用</el-radio></el-radio-group></el-form-item><el-form-item label="角色" prop="roles"><el-selectv-model="formData.roles"multiplestyle="width: 100%"><el-optionv-for="role in roleOptions":key="role.id":label="role.name":value="role.id"/></el-select></el-form-item></form-dialog>
</template><script lang="ts" setup>
import { ref } from 'vue';
import FormDialog from '@/components/common/FormDialog.vue';
import { getRoles } from '@/api/role';interface Props {modelValue: boolean;title: string;formData: any;
}defineProps<Props>();
defineEmits(['submit']);const visible = defineModel<boolean>('modelValue');const roleOptions = ref<any[]>([]);const rules = {username: [{ required: true, message: '请输入用户名', trigger: 'blur' },{ min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' },],email: [{ required: true, message: '请输入邮箱', trigger: 'blur' },{ type: 'email', message: '请输入正确的邮箱格式', trigger: 'blur' },],password: [{ required: true, message: '请输入密码', trigger: 'blur' },{ min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' },],roles: [{ required: true, message: '请选择角色', trigger: 'change' },],
};const fetchRoles = async () => {try {const data = await getRoles();roleOptions.value = data;} catch (error) {console.error('Fetch roles failed:', error);}
};fetchRoles();
</script>

6. 数据库设计

6.1 核心表结构

6.1.1 用户表(Users)
CREATE TABLE Users (Id INT PRIMARY KEY IDENTITY(1,1),Username NVARCHAR(50) NOT NULL,Email NVARCHAR(100) NOT NULL,PasswordHash NVARCHAR(255) NOT NULL,PhoneNumber NVARCHAR(20),IsActive BIT NOT NULL DEFAULT 1,LastLoginTime DATETIME,CreatedAt DATETIME NOT NULL DEFAULT GETDATE(),UpdatedAt DATETIME NOT NULL DEFAULT GETDATE(),CreatedBy INT,UpdatedBy INT,CONSTRAINT UQ_Users_Username UNIQUE (Username),CONSTRAINT UQ_Users_Email UNIQUE (Email)
);
6.1.2 角色表(Roles)
CREATE TABLE Roles (Id INT PRIMARY KEY IDENTITY(1,1),Name NVARCHAR(50) NOT NULL,Description NVARCHAR(255),IsSystem BIT NOT NULL DEFAULT 0,CreatedAt DATETIME NOT NULL DEFAULT GETDATE(),UpdatedAt DATETIME NOT NULL DEFAULT GETDATE(),CONSTRAINT UQ_Roles_Name UNIQUE (Name)
);
6.1.3 权限表(Permissions)
CREATE TABLE Permissions (Id INT PRIMARY KEY IDENTITY(1,1),Name NVARCHAR(50) NOT NULL,Code NVARCHAR(50) NOT NULL,Module NVARCHAR(50) NOT NULL,Description NVARCHAR(255),CreatedAt DATETIME NOT NULL DEFAULT GETDATE(),CONSTRAINT UQ_Permissions_Code UNIQUE (Code)
);
6.1.4 用户角色关联表(UserRoles)
CREATE TABLE UserRoles (UserId INT NOT NULL,RoleId INT NOT NULL,CreatedAt DATETIME NOT NULL DEFAULT GETDATE(),PRIMARY KEY (UserId, RoleId),FOREIGN KEY (UserId) REFERENCES Users(Id) ON DELETE CASCADE,FOREIGN KEY (RoleId) REFERENCES Roles(Id) ON DELETE CASCADE
);
6.1.5 角色权限关联表(RolePermissions)
CREATE TABLE RolePermissions (RoleId INT NOT NULL,PermissionId INT NOT NULL,CreatedAt DATETIME NOT NULL DEFAULT GETDATE(),PRIMARY KEY (RoleId, PermissionId),FOREIGN KEY (RoleId) REFERENCES Roles(Id) ON DELETE CASCADE,FOREIGN KEY (PermissionId) REFERENCES Permissions(Id) ON DELETE CASCADE
);
6.1.6 系统日志表(SystemLogs)
CREATE TABLE SystemLogs (Id BIGINT PRIMARY KEY IDENTITY(1,1),Level NVARCHAR(20) NOT NULL,Message NVARCHAR(MAX) NOT NULL,Exception NVARCHAR(MAX),Source NVARCHAR(255),UserId INT,RequestPath NVARCHAR(255),RequestMethod NVARCHAR(10),RequestQuery NVARCHAR(MAX),RequestBody NVARCHAR(MAX),IpAddress NVARCHAR(50),UserAgent NVARCHAR(255),CreatedAt DATETIME NOT NULL DEFAULT GETDATE(),FOREIGN KEY (UserId) REFERENCES Users(Id) ON DELETE SET NULL
);

6.2 索引设计

-- 用户表索引
CREATE INDEX IX_Users_Email ON Users(Email);
CREATE INDEX IX_Users_PhoneNumber ON Users(PhoneNumber);
CREATE INDEX IX_Users_IsActive ON Users(IsActive);-- 日志表索引
CREATE INDEX IX_SystemLogs_Level ON SystemLogs(Level);
CREATE INDEX IX_SystemLogs_UserId ON SystemLogs(UserId);
CREATE INDEX IX_SystemLogs_CreatedAt ON SystemLogs(CreatedAt);
CREATE INDEX IX_SystemLogs_RequestPath ON SystemLogs(RequestPath);

6.3 数据初始化脚本

-- 初始化超级管理员角色
INSERT INTO Roles (Name, Description, IsSystem)
VALUES ('SuperAdmin', '超级管理员', 1);-- 初始化基本权限
INSERT INTO Permissions (Name, Code, Module, Description)
VALUES 
('用户管理-查看', 'users.view', 'system', '查看用户列表和详情'),
('用户管理-创建', 'users.create', 'system', '创建新用户'),
('用户管理-编辑', 'users.update', 'system', '编辑用户信息'),
('用户管理-删除', 'users.delete', 'system', '删除用户'),
('角色管理-查看', 'roles.view', 'system', '查看角色列表和详情'),
('角色管理-创建', 'roles.create', 'system', '创建新角色'),
('角色管理-编辑', 'roles.update', 'system', '编辑角色信息'),
('角色管理-删除', 'roles.delete', 'system', '删除角色');-- 为超级管理员角色分配所有权限
INSERT INTO RolePermissions (RoleId, PermissionId)
SELECT r.Id, p.Id
FROM Roles r
CROSS JOIN Permissions p
WHERE r.Name = 'SuperAdmin';-- 初始化超级管理员用户
DECLARE @adminId INT;
INSERT INTO Users (Username, Email, PasswordHash, IsActive)
VALUES ('admin', 'admin@example.com', '$2a$11$WrL5JZ6U3n7GkGKjZ8bVf.3dQY9wLmN1JfzXcVvV6yKt5rHs3s4XW', 1);SET @adminId = SCOPE_IDENTITY();-- 分配超级管理员角色
INSERT INTO UserRoles (UserId, RoleId)
VALUES (@adminId, (SELECT Id FROM Roles WHERE Name = 'SuperAdmin'));

7. API设计规范

7.1 RESTful API设计原则

  1. 资源命名

    • 使用名词复数形式表示资源
    • 例如:/users, /roles, /permissions
  2. HTTP方法

    • GET:获取资源
    • POST:创建资源
    • PUT:完整更新资源
    • PATCH:部分更新资源
    • DELETE:删除资源
  3. 端点示例

    GET    /users          - 获取用户列表
    POST   /users          - 创建新用户
    GET    /users/{id}     - 获取指定用户
    PUT    /users/{id}     - 更新指定用户
    DELETE /users/{id}     - 删除指定用户
    GET    /users/{id}/roles - 获取用户的角色
    

7.2 请求与响应规范

7.2.1 请求头
Authorization: Bearer {token}
Content-Type: application/json
Accept: application/json
7.2.2 查询参数
  • 分页:?page=1&size=10
  • 排序:?sort=name,asc&sort=createdAt,desc
  • 过滤:?name=admin&status=1
7.2.3 响应格式

成功响应:

{"data": {"id": 1,"username": "admin","email": "admin@example.com"},"message": "操作成功","success": true
}

分页响应:

{"data": {"items": [{"id": 1,"username": "admin"}],"total": 1,"page": 1,"size": 10},"message": "操作成功","success": true
}

错误响应:

{"error": {"code": "ValidationError","message": "请求参数无效","details": {"username": ["用户名不能为空"],"email": ["邮箱格式不正确"]}},"success": false
}

7.3 版本控制

在URL中包含API版本:

/api/v1/users
/api/v2/users

7.4 数据格式

  1. 日期时间:ISO 8601格式 "2023-05-20T12:34:56Z"
  2. 枚举:使用字符串值而非数字
  3. 布尔值:使用 true/false
  4. 空值:使用 null

7.5 错误代码

HTTP状态码错误代码描述
400BadRequest请求参数无效
401Unauthorized未认证或token过期
403Forbidden无权限访问
404NotFound资源不存在
409Conflict资源冲突
500InternalServerError服务器内部错误

8. 认证与授权方案

8.1 认证流程

  1. 登录

    • 用户提交用户名和密码
    • 服务器验证凭证
    • 生成JWT token返回给客户端
  2. 访问API

    • 客户端在请求头中添加 Authorization: Bearer {token}
    • 服务器验证token有效性
    • 允许或拒绝请求

8.2 JWT实现

// JWT服务实现
public class JwtService : IJwtService
{private readonly JwtSettings _jwtSettings;public JwtService(IOptions<JwtSettings> jwtSettings){_jwtSettings = jwtSettings.Value;}public string GenerateToken(User user){var claims = new[]{new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()),new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),new Claim(JwtRegisteredClaimNames.UniqueName, user.Username),new Claim(ClaimTypes.Email, user.Email),new Claim("permissions", JsonSerializer.Serialize(user.Permissions))};var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.Secret));var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);var token = new JwtSecurityToken(issuer: _jwtSettings.Issuer,audience: _jwtSettings.Audience,claims: claims,expires: DateTime.UtcNow.AddMinutes(_jwtSettings.ExpiryMinutes),signingCredentials: creds);return new JwtSecurityTokenHandler().WriteToken(token);}public int? ValidateToken(string token){if (string.IsNullOrEmpty(token))return null;var tokenHandler = new JwtSecurityTokenHandler();var key = Encoding.UTF8.GetBytes(_jwtSettings.Secret);try{tokenHandler.ValidateToken(token, new TokenValidationParameters{ValidateIssuerSigningKey = true,IssuerSigningKey = new SymmetricSecurityKey(key),ValidateIssuer = true,ValidIssuer = _jwtSettings.Issuer,ValidateAudience = true,ValidAudience = _jwtSettings.Audience,ValidateLifetime = true,ClockSkew = TimeSpan.Zero}, out var validatedToken);var jwtToken = (JwtSecurityToken)validatedToken;var userId = int.Parse(jwtToken.Claims.First(x => x.Type == "sub").Value);return userId;}catch{return null;}}
}

8.3 授权策略

// 权限策略注册
services.AddAuthorization(options =>
{// 基于角色的策略options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));// 基于声明的策略options.AddPolicy("ManageUsers", policy => policy.RequireClaim("permission", "users.manage"));// 自定义需求策略options.AddPolicy("Over18", policy =>policy.Requirements.Add(new MinimumAgeRequirement(18)));
});// 自定义授权处理器
public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
{protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,PermissionRequirement requirement){if (context.User.HasClaim(c => c.Type == "permissions" && c.Value.Contains(requirement.Permission))){context.Succeed(requirement);}return Task.CompletedTask;}
}// 控制器中使用
[Authorize(Policy = "ManageUsers")]
public class UsersController : ControllerBase
{// ...
}

8.4 刷新令牌机制

public class TokenService : ITokenService
{public async Task<AuthenticateResponse> AuthenticateAsync(User user, string ipAddress){var jwtToken = GenerateJwtToken(user);var refreshToken = GenerateRefreshToken(ipAddress);user.RefreshTokens.Add(refreshToken);await _context.SaveChangesAsync();return new AuthenticateResponse(user, jwtToken, refreshToken.Token);}public async Task<AuthenticateResponse> RefreshTokenAsync(string token, string ipAddress){var user = await _context.Users.Include(u => u.RefreshTokens).FirstOrDefaultAsync(u => u.RefreshTokens.Any(t => t.Token == token));if (user == null)throw new AppException("无效的刷新令牌");var refreshToken = user.RefreshTokens.Single(x => x.Token == token);if (!refreshToken.IsActive)throw new AppException("无效的刷新令牌");// 替换旧刷新令牌var newRefreshToken = GenerateRefreshToken(ipAddress);refreshToken.Revoked = DateTime.UtcNow;refreshToken.RevokedByIp = ipAddress;refreshToken.ReplacedByToken = newRefreshToken.Token;user.RefreshTokens.Add(newRefreshToken);await _context.SaveChangesAsync();var jwtToken = GenerateJwtToken(user);return new AuthenticateResponse(user, jwtToken, newRefreshToken.Token);}public async Task RevokeTokenAsync(string token, string ipAddress){var user = await _context.Users.Include(u => u.RefreshTokens).FirstOrDefaultAsync(u => u.RefreshTokens.Any(t => t.Token == token));if (user == null)throw new AppException("无效的刷新令牌");var refreshToken = user.RefreshTokens.Single(x => x.Token == token);if (!refreshToken.IsActive)throw new AppException("无效的刷新令牌");refreshToken.Revoked = DateTime.UtcNow;refreshToken.RevokedByIp = ipAddress;await _context.SaveChangesAsync();}private RefreshToken GenerateRefreshToken(string ipAddress){return new RefreshToken{Token = RandomTokenString(),Expires = DateTime.UtcNow.AddDays(7),Created = DateTime.UtcNow,CreatedByIp = ipAddress};}private string RandomTokenString(){using var rngCryptoServiceProvider = new RNGCryptoServiceProvider();var randomBytes = new byte[40];rngCryptoServiceProvider.GetBytes(randomBytes);return BitConverter.ToString(randomBytes).Replace("-", "");}
}

9. 异常处理与日志系统

9.1 异常处理策略

9.1.1 自定义异常体系
// 基础异常类
public class AppException : Exception
{public AppException() { }public AppException(string message) : base(message) { }public AppException(string message, Exception inner) : base(message, inner) { }
}// 业务异常
public class BusinessException : AppException
{public string Code { get; }public BusinessException(string code, string message) : base(message){Code = code;}
}// 验证异常
public class ValidationException : AppException
{public IDictionary<string, string[]> Errors { get; }public ValidationException() : base("一个或多个验证失败"){Errors = new Dictionary<string, string[]>();}public ValidationException(IDictionary<string, string[]> errors) : this(){Errors = errors;}
}// 未授权异常
public class UnauthorizedException : AppException
{public UnauthorizedException(string message) : base(message) { }
}// 未找到异常
public class NotFoundException : AppException
{public NotFoundException(string name, object key) : base($"实体 \"{name}\" ({key}) 未找到") { }
}
9.1.2 全局异常处理
// 异常中间件
public class ExceptionMiddleware
{private readonly RequestDelegate _next;private readonly ILogger<ExceptionMiddleware> _logger;public ExceptionMiddleware(RequestDelegate next, ILogger<ExceptionMiddleware> logger){_next = next;_logger = logger;}public async Task InvokeAsync(HttpContext context){try{await _next(context);}catch (Exception ex){_logger.LogError(ex, "An unhandled exception has occurred");await HandleExceptionAsync(context, ex);}}private static Task HandleExceptionAsync(HttpContext context, Exception exception){context.Response.ContentType = "application/json";var statusCode = exception switch{ValidationException => StatusCodes.Status400BadRequest,NotFoundException => StatusCodes.Status404NotFound,UnauthorizedException => StatusCodes.Status401Unauthorized,BusinessException => StatusCodes.Status400BadRequest,_ => StatusCodes.Status500InternalServerError};context.Response.StatusCode = statusCode;var response = new{status = statusCode,message = exception.Message,details = exception is ValidationException validationException ? validationException.Errors : null,code = exception is BusinessException businessException? businessException.Code: null};return context.Response.WriteAsync(JsonSerializer.Serialize(response));}
}

9.2 日志系统设计

9.2.1 日志配置
// 使用Serilog配置
Log.Logger = new LoggerConfiguration().MinimumLevel.Information().MinimumLevel.Override("Microsoft", LogEventLevel.Warning).MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information).MinimumLevel.Override("System", LogEventLevel.Warning).Enrich.FromLogContext().WriteTo.Console().WriteTo.File(path: "Logs/log-.txt",rollingInterval: RollingInterval.Day,retainedFileCountLimit: 7,shared: true).WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://localhost:9200")){AutoRegisterTemplate = true,IndexFormat = "fastproduction-{0:yyyy.MM}"}).CreateLogger();// 在Program.cs中
builder.Host.UseSerilog();
9.2.2 结构化日志
// 控制器中使用结构化日志
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{private readonly ILogger<WeatherForecastController> _logger;public WeatherForecastController(ILogger<WeatherForecastController> logger){_logger = logger;}[HttpGet]public IEnumerable<WeatherForecast> Get(){_logger.LogInformation("Getting weather forecast with {Count} items", 5);try{// 业务逻辑}catch (Exception ex){_logger.LogError(ex, "Failed to get weather forecast");throw;}}
}
9.2.3 请求日志中间件
public class RequestLoggingMiddleware
{private readonly RequestDelegate _next;private readonly ILogger<RequestLoggingMiddleware> _logger;public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger){_next = next;_logger = logger;}public async Task Invoke(HttpContext context){var startTime = DateTime.UtcNow;try{await _next(context);var elapsedMs = (DateTime.UtcNow - startTime).TotalMilliseconds;_logger.LogInformation("Request {Method} {Path} completed in {Elapsed}ms with {StatusCode}",context.Request.Method,context.Request.Path,elapsedMs,context.Response.StatusCode);}catch (Exception ex){var elapsedMs = (DateTime.UtcNow - startTime).TotalMilliseconds;_logger.LogError(ex,"Request {Method} {Path} failed in {Elapsed}ms",context.Request.Method,context.Request.Path,elapsedMs);throw;}}
}

10. 性能优化策略

10.1 数据库优化

10.1.1 查询优化
// 避免N+1查询问题
// 错误方式 - 会导致N+1查询
var users = await _context.Users.ToListAsync();
foreach (var user in users)
{var roles = await _context.Roles.Where(r => r.UserRoles.Any(ur => ur.UserId == user.Id)).ToListAsync();
}// 正确方式 - 使用Include预先加载
var usersWithRoles = await _context.Users.Include(u => u.UserRoles).ThenInclude(ur => ur.Role).ToListAsync();// 使用投影只选择需要的字段
var userDtos = await _context.Users.Where(u => u.IsActive).Select(u => new UserDto{Id = u.Id,Username = u.Username,Email = u.Email}).ToListAsync();
10.1.2 索引优化
-- 为常用查询条件添加索引
CREATE INDEX IX_Users_IsActive ON Users(IsActive);
CREATE INDEX IX_Users_Email ON Users(Email);-- 复合索引
CREATE INDEX IX_Orders_CustomerId_Status ON Orders(CustomerId, Status);
10.1.3 批量操作
// 批量插入
var newUsers = new List<User>();
for (int i = 0; i < 1000; i++)
{newUsers.Add(new User { /* ... */ });
}await _context.BulkInsertAsync(newUsers);// 批量更新
await _context.Users.Where(u => u.LastLoginTime < DateTime.Now.AddYears(-1)).BatchUpdateAsync(u => new User { IsActive = false });// 批量删除
await _context.Users.Where(u => !u.IsActive && u.CreatedAt < DateTime.Now.AddYears(-2)).BatchDeleteAsync();

10.2 缓存策略

10.2.1 多级缓存
// 缓存服务实现
public class CacheService : ICacheService
{private readonly IMemoryCache _memoryCache;private readonly IDistributedCache _distributedCache;public CacheService(IMemoryCache memoryCache,IDistributedCache distributedCache){_memoryCache = memoryCache;_distributedCache = distributedCache;}public async Task<T> GetOrCreateAsync<T>(string key,Func<Task<T>> factory,TimeSpan? memoryCacheExpiration = null,TimeSpan? distributedCacheExpiration = null,CancellationToken cancellationToken = default){// 首先尝试从内存缓存获取if (_memoryCache.TryGetValue(key, out T memoryValue)){return memoryValue;}// 然后尝试从分布式缓存获取var distributedValue = await _distributedCache.GetStringAsync(key, cancellationToken);if (distributedValue != null){var value = JsonSerializer.Deserialize<T>(distributedValue);// 存入内存缓存_memoryCache.Set(key, value, memoryCacheExpiration ?? TimeSpan.FromMinutes(5));return value;}// 如果缓存中都没有,则调用工厂方法获取数据var result = await factory();// 存入分布式缓存await _distributedCache.SetStringAsync(key,JsonSerializer.Serialize(result),new DistributedCacheEntryOptions{AbsoluteExpirationRelativeToNow = distributedCacheExpiration ?? TimeSpan.FromHours(1)},cancellationToken);// 存入内存缓存_memoryCache.Set(key, result, memoryCacheExpiration ?? TimeSpan.FromMinutes(5));return result;}
}
10.2.2 缓存失效策略
// 缓存失效装饰器
public class CachedUserService : IUserService
{private readonly IUserService _inner;private readonly ICacheService _cache;public CachedUserService(IUserService inner, ICacheService cache){_inner = inner;_cache = cache;}public async Task<UserDto> GetByIdAsync(int id){var cacheKey = $"user:{id}";return await _cache.GetOrCreateAsync(cacheKey, () => _inner.GetByIdAsync(id));}public async Task UpdateAsync(int id, UpdateUserDto userDto){await _inner.UpdateAsync(id, userDto);// 更新后使缓存失效var cacheKey = $"user:{id}";await _cache.RemoveAsync(cacheKey);}
}

10.3 响应压缩

// 在Program.cs中配置响应压缩
builder.Services.AddResponseCompression(options =>
{options.Providers.Add<BrotliCompressionProvider>();options.Providers.Add<GzipCompressionProvider>();options.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(new[]{"application/json","text/json","text/plain","application/javascript","text/css","text/html","application/xml","text/xml"});
});app.UseResponseCompression();

10.4 异步编程

// 控制器中使用异步方法
[HttpGet("{id}")]
public async Task<ActionResult<UserDto>> GetUser(int id)
{var user = await _userService.GetByIdAsync(id);if (user == null){return NotFound();}return Ok(user);
}// 服务层异步方法
public async Task<UserDto> GetByIdAsync(int id)
{var user = await _repository.GetByIdAsync(id);if (user == null){throw new NotFoundException(nameof(User), id);}return _mapper.Map<UserDto>(user);
}

10.5 性能监控

// 添加Application Insights
builder.Services.AddApplicationInsightsTelemetry();// 自定义性能计数器
public class PerformanceCounterMiddleware
{private readonly RequestDelegate _next;private readonly ILogger<PerformanceCounterMiddleware> _logger;private readonly Stopwatch _stopwatch;public PerformanceCounterMiddleware(RequestDelegate next,ILogger<PerformanceCounterMiddleware> logger){_next = next;_logger = logger;_stopwatch = new Stopwatch();}public async Task InvokeAsync(HttpContext context){_stopwatch.Restart();await _next(context);_stopwatch.Stop();if (_stopwatch.ElapsedMilliseconds > 500){_logger.LogWarning("Request {Method} {Path} took {Elapsed}ms",context.Request.Method,context.Request.Path,_stopwatch.ElapsedMilliseconds);}}
}

11. 测试策略

11.1 测试金字塔

  1. 单元测试:70% - 测试单个类或方法
  2. 集成测试:20% - 测试多个组件交互
  3. 端到端测试:10% - 测试完整业务流程

11.2 单元测试

11.2.1 测试示例
// 用户服务测试
public class UserServiceTests
{private readonly Mock<IUserRepository> _mockRepo;private readonly Mock<IMapper> _mockMapper;private readonly UserService _userService;public UserServiceTests(){_mockRepo = new Mock<IUserRepository>();_mockMapper = new Mock<IMapper>();_userService = new UserService(_mockRepo.Object, _mockMapper.Object);}[Fact]public async Task GetByIdAsync_ShouldReturnUser_WhenUserExists(){// Arrangevar userId = 1;var user = new User { Id = userId, Username = "test" };var userDto = new UserDto { Id = userId, Username = "test" };_mockRepo.Setup(x => x.GetByIdAsync(userId)).ReturnsAsync(user);_mockMapper.Setup(x => x.Map<UserDto>(user)).Returns(userDto);// Actvar result = await _userService.GetByIdAsync(userId);// AssertAssert.NotNull(result);Assert.Equal(userId, result.Id);_mockRepo.Verify(x => x.GetByIdAsync(userId), Times.Once);}[Fact]public async Task GetByIdAsync_ShouldThrowNotFoundException_WhenUserNotExists(){// Arrangevar userId = 999;_mockRepo.Setup(x => x.GetByIdAsync(userId)).ReturnsAsync((User)null);// Act & Assertawait Assert.ThrowsAsync<NotFoundException>(() => _userService.GetByIdAsync(userId));}
}
11.2.2 测试覆盖率目标
  • 业务逻辑:90%+
  • 控制器:70%+
  • 仓储层:80%+
  • 整体覆盖率:75%+

11.3 集成测试

11.3.1 API测试
public class UsersApiTests : IClassFixture<WebApplicationFactory<Program>>
{private readonly WebApplicationFactory<Program> _factory;private readonly HttpClient _client;public UsersApiTests(WebApplicationFactory<Program> factory){_factory = factory.WithWebHostBuilder(builder =>{builder.ConfigureTestServices(services =>{// 替换真实服务为模拟服务services.AddScoped<IUserService, MockUserService>();});});_client = _factory.CreateClient();}[Fact]public async Task Get_Users_ReturnsSuccessStatusCode(){// Actvar response = await _client.GetAsync("/api/users");// Assertresponse.EnsureSuccessStatusCode();Assert.Equal("application/json; charset=utf-8", response.Content.Headers.ContentType.ToString());}[Fact]public async Task Get_UserById_ReturnsUser(){// Arrangevar userId = 1;// Actvar response = await _client.GetAsync($"/api/users/{userId}");// Assertresponse.EnsureSuccessStatusCode();var user = await response.Content.ReadFromJsonAsync<UserDto>();Assert.NotNull(user);Assert.Equal(userId, user.Id);}
}
11.3.2 数据库测试
public class UserRepositoryTests : IDisposable
{private readonly AppDbContext _context;private readonly UserRepository _repository;public UserRepositoryTests(){var options = new DbContextOptionsBuilder<AppDbContext>().UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString()).Options;_context = new AppDbContext(options);_repository = new UserRepository(_context);// 初始化测试数据_context.Users.AddRange(new User { Id = 1, Username = "user1", Email = "user1@test.com" },new User { Id = 2, Username = "user2", Email = "user2@test.com" });_context.SaveChanges();}[Fact]public async Task GetByIdAsync_ShouldReturnUser_WhenUserExists(){// Arrangevar userId = 1;// Actvar user = await _repository.GetByIdAsync(userId);// AssertAssert.NotNull(user);Assert.Equal(userId, user.Id);}[Fact]public async Task GetByIdAsync_ShouldReturnNull_WhenUserNotExists(){// Arrangevar userId = 999;// Actvar user = await _repository.GetByIdAsync(userId);// AssertAssert.Null(user);}public void Dispose(){_context.Dispose();}
}

11.4 端到端测试

11.4.1 UI测试
// 使用Cypress进行UI测试
describe('User Management', () => {beforeEach(() => {cy.login('admin', 'password');cy.visit('/users');});it('should display user list', () => {cy.get('.user-table').should('exist');cy.get('.user-table tbody tr').should('have.length.at.least', 1);});it('should create new user', () => {cy.get('[data-test="create-user-button"]').click();cy.get('[data-test="username-input"]').type('newuser');cy.get('[data-test="email-input"]').type('newuser@example.com');cy.get('[data-test="password-input"]').type('Password123');cy.get('[data-test="submit-button"]').click();cy.contains('User created successfully').should('exist');cy.get('[data-test="user-table"]').contains('td', 'newuser').should('exist');});
});
11.4.2 API契约测试
// 使用Pact进行契约测试
public class UserApiContractTests : IDisposable
{private readonly IPactBuilderV4 _pactBuilder;private readonly int _port = 9000;public UserApiContractTests(){var config = new PactConfig{PactDir = "../../../pacts/",LogLevel = PactLogLevel.Debug};_pactBuilder = Pact.V4("Frontend", "UserService", config).WithHttpInteractions(_port);}[Fact]public async Task GetUser_WhenUserExists_ReturnsUser(){// Arrangevar userId = 1;_pactBuilder.UponReceiving("A GET request to retrieve a user").Given("A user with ID 1 exists").WithRequest(HttpMethod.Get, $"/api/users/{userId}").WithHeader("Accept", "application/json").WillRespond().WithStatus(HttpStatusCode.OK).WithHeader("Content-Type", "application/json").WithJsonBody(new{id = 1,username = "testuser",email = "test@example.com"});await _pactBuilder.VerifyAsync(async ctx =>{// Actvar client = new HttpClient { BaseAddress = ctx.MockServerUri };var response = await client.GetAsync($"/api/users/{userId}");// AssertAssert.Equal(HttpStatusCode.OK, response.StatusCode);var user = await response.Content.ReadFromJsonAsync<UserDto>();Assert.Equal(userId, user.Id);Assert.NotNull(user.Username);Assert.NotNull(user.Email);});}public void Dispose(){_pactBuilder.Dispose();}
}

11.5 性能测试

// 使用BenchmarkDotNet进行性能测试
[MemoryDiagnoser]
public class UserServiceBenchmarks
{private IUserService _userService;private AppDbContext _context;[GlobalSetup]public void Setup(){var options = new DbContextOptionsBuilder<AppDbContext>().UseInMemoryDatabase(databaseName: "BenchmarkDb").Options;_context = new AppDbContext(options);// 添加测试数据for (int i = 1; i <= 1000; i++){_context.Users.Add(new User { Id = i, Username = $"user{i}", Email = $"user{i}@test.com" });}_context.SaveChanges();_userService = new UserService(new UserRepository(_context),new Mapper(new MapperConfiguration(cfg => cfg.AddProfile<AutoMapperProfile>())));}[Benchmark]public async Task GetUserById(){for (int i = 1; i <= 100; i++){await _userService.GetByIdAsync(i % 1000 + 1);}}[Benchmark]public async Task GetPagedUsers(){for (int i = 1; i <= 10; i++){await _userService.GetAllAsync(i, 10);}}[GlobalCleanup]public void Cleanup(){_context.Dispose();}
}

12. 部署方案

12.1 部署架构

┌─────────────────────────────────────────────────┐
│                    CI/CD Pipeline               │
│  (GitHub Actions/Jenkins)                      │
└───────────────┬───────────────────┬────────────┘│                   │▼                   ▼
┌─────────────────────────────────────────────────┐
│                   容器注册表                    │
│  (Docker Hub/ACR/ECR)                          │
└───────────────┬───────────────────┬────────────┘│                   │▼                   ▼
┌─────────────────────────────────────────────────┐
│                   Kubernetes集群                │
│  (AKS/EKS/GKE)                                 │
└───────────────┬───────────────────┬────────────┘│                   │▼                   ▼
┌─────────────────┐         ┌─────────────────┐
│   后端Pod       │         │   前端Pod       │
│  (.NET Core)    │         │  (Vue)          │
└─────────────────┘         └─────────────────┘

12.2 Docker化

12.2.1 后端Dockerfile
# 构建阶段
FROM mcr.microsoft.com/dotnet/sdk:7.0 AS build
WORKDIR /src# 复制项目文件并恢复NuGet包
COPY ["FastProductionFramework.WebApi/FastProductionFramework.WebApi.csproj", "FastProductionFramework.WebApi/"]
COPY ["FastProductionFramework.Application/FastProductionFramework.Application.csproj", "FastProductionFramework.Application/"]
COPY ["FastProductionFramework.Core/FastProductionFramework.Core.csproj", "FastProductionFramework.Core/"]
COPY ["FastProductionFramework.Infrastructure/FastProductionFramework.Infrastructure.csproj", "FastProductionFramework.Infrastructure/"]
RUN dotnet restore "FastProductionFramework.WebApi/FastProductionFramework.WebApi.csproj"# 复制所有源代码
COPY . .# 构建应用
WORKDIR "/src/FastProductionFramework.WebApi"
RUN dotnet build "FastProductionFramework.WebApi.csproj" -c Release -o /app/build# 发布应用
FROM build AS publish
RUN dotnet publish "FastProductionFramework.WebApi.csproj" -c Release -o /app/publish# 运行时阶段
FROM mcr.microsoft.com/dotnet/aspnet:7.0 AS runtime
WORKDIR /app# 安装Curl用于健康检查
RUN apt-get update && \apt-get install -y --no-install-recommends curl && \rm -rf /var/lib/apt/lists/*# 从发布阶段复制应用
COPY --from=publish /app/publish .# 设置环境变量
ENV ASPNETCORE_ENVIRONMENT=Production
ENV ASPNETCORE_URLS=http://+:80# 暴露端口
EXPOSE 80# 健康检查
HEALTHCHECK --interval=30s --timeout=3s \CMD curl -f http://localhost/health || exit 1# 启动应用
ENTRYPOINT ["dotnet", "FastProductionFramework.WebApi.dll"]
12.2.2 前端Dockerfile
# 构建阶段
FROM node:16 AS build
WORKDIR /app# 复制package文件并安装依赖
COPY package*.json ./
RUN npm install# 复制源代码并构建
COPY . .
RUN npm run build# 运行时阶段
FROM nginx:alpine# 复制构建产物
COPY --from=build /app/dist /usr/share/nginx/html# 复制Nginx配置
COPY nginx.conf /etc/nginx/conf.d/default.conf# 暴露端口
EXPOSE 80# 启动Nginx
CMD ["nginx", "-g", "daemon off;"]
12.2.3 Nginx配置
server {listen 80;server_name localhost;location / {root /usr/share/nginx/html;index index.html;try_files $uri $uri/ /index.html;}location /api/ {proxy_pass http://backend:80/;proxy_set_header Host $host;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}location /health {access_log off;return 200 'healthy';}
}

12.3 Kubernetes部署

12.3.1 后端部署
# backend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: backendlabels:app: backend
spec:replicas: 3selector:matchLabels:app: backendtemplate:metadata:labels:app: backendspec:containers:- name: backendimage: your-registry/fastproduction-backend:latestports:- containerPort: 80resources:requests:cpu: "100m"memory: "256Mi"limits:cpu: "500m"memory: "512Mi"readinessProbe:httpGet:path: /healthport: 80initialDelaySeconds: 10periodSeconds: 5livenessProbe:httpGet:path: /healthport: 80initialDelaySeconds: 30periodSeconds: 10env:- name: ASPNETCORE_ENVIRONMENTvalue: Production- name: ConnectionStrings__DefaultConnectionvalueFrom:secretKeyRef:name: db-secretskey: connection-string
---
apiVersion: v1
kind: Service
metadata:name: backend
spec:selector:app: backendports:- protocol: TCPport: 80targetPort: 80
12.3.2 前端部署
# frontend-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:name: frontendlabels:app: frontend
spec:replicas: 2selector:matchLabels:app: frontendtemplate:metadata:labels:app: frontendspec:containers:- name: frontendimage: your-registry/fastproduction-frontend:latestports:- containerPort: 80resources:requests:cpu: "50m"memory: "128Mi"limits:cpu: "200m"memory: "256Mi"readinessProbe:httpGet:path: /healthport: 80initialDelaySeconds: 5periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:name: frontend
spec:selector:app: frontendports:- protocol: TCPport: 80targetPort: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: frontend-ingressannotations:nginx.ingress.kubernetes.io/rewrite-target: /
spec:rules:- host: fastproduction.example.comhttp:paths:- path: /pathType: Prefixbackend:service:name: frontendport:number: 80- path: /apipathType: Prefixbackend:service:name: backendport:number: 80

12.4 CI/CD流程

12.4.1 GitHub Actions工作流
name: Build and Deployon:push:branches: [ main ]pull_request:branches: [ main ]env:REGISTRY: ghcr.ioIMAGE_NAME: ${{ github.repository }}jobs:build-backend:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v2- name: Setup .NETuses: actions/setup-dotnet@v1with:dotnet-version: '7.0.x'- name: Restore dependenciesrun: dotnet restore- name: Buildrun: dotnet build --configuration Release --no-restore- name: Testrun: dotnet test --no-restore --verbosity normal- name: Publishrun: dotnet publish -c Release -o ./publish- name: Login to Container Registryuses: docker/login-action@v1with:registry: ${{ env.REGISTRY }}username: ${{ github.actor }}password: ${{ secrets.GITHUB_TOKEN }}- name: Build and push Docker imageuses: docker/build-push-action@v2with:context: .file: ./FastProductionFramework.WebApi/Dockerfilepush: ${{ github.ref == 'refs/heads/main' }}tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/backend:latestlabels: |org.opencontainers.image.source=${{ github.repositoryUrl }}build-frontend:runs-on: ubuntu-latestneeds: build-backendsteps:- uses: actions/checkout@v2- name: Setup Node.jsuses: actions/setup-node@v2with:node-version: '16'- name: Install dependenciesrun: npm ci- name: Buildrun: npm run build- name: Run testsrun: npm run test- name: Login to Container Registryuses: docker/login-action@v1with:registry: ${{ env.REGISTRY }}username: ${{ github.actor }}password: ${{ secrets.GITHUB_TOKEN }}- name: Build and push Docker imageuses: docker/build-push-action@v2with:context: .file: ./frontend/Dockerfilepush: ${{ github.ref == 'refs/heads/main' }}tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/frontend:latestlabels: |org.opencontainers.image.source=${{ github.repositoryUrl }}deploy:runs-on: ubuntu-latestneeds: [build-backend, build-frontend]if: github.ref == 'refs/heads/main'steps:- name: Checkoutuses: actions/checkout@v2- name: Install kubectluses: azure/setup-kubectl@v1- name: Configure Kubernetesrun: |mkdir -p ~/.kubeecho "${{ secrets.KUBE_CONFIG }}" > ~/.kube/configkubectl version --client- name: Deploy to Kubernetesrun: |kubectl apply -f k8s/backend-deployment.yamlkubectl apply -f k8s/frontend-deployment.yamlkubectl rollout status deployment/backendkubectl rollout status deployment/frontend

13. 监控与运维

13.1 监控体系

13.1.1 应用性能监控(APM)
// 添加Application Insights
builder.Services.AddApplicationInsightsTelemetry(options =>
{options.ConnectionString = builder.Configuration["ApplicationInsights:ConnectionString"];options.EnableAdaptiveSampling = false;
});// 自定义遥测
public class UsersController : ControllerBase
{private readonly TelemetryClient _telemetryClient;public UsersController(TelemetryClient telemetryClient){_telemetryClient = telemetryClient;}[HttpGet("{id}")]public async Task<ActionResult<UserDto>> GetUser(int id){// 自定义事件_telemetryClient.TrackEvent("GetUser", new Dictionary<string, string>{["userId"] = id.ToString()});try{var user = await _userService.GetByIdAsync(id);// 自定义指标_telemetryClient.GetMetric("UserRetrievalTime").TrackValue(stopwatch.ElapsedMilliseconds);return Ok(user);}catch (Exception ex){// 自定义异常_telemetryClient.TrackException(ex, new Dictionary<string, string>{["userId"] = id.ToString()});throw;}}
}
13.1.2 健康检查
// 添加健康检查服务
builder.Services.AddHealthChecks().AddCheck<DatabaseHealthCheck>("database").AddRedis(builder.Configuration.GetConnectionString("Redis")).AddRabbitMQ(builder.Configuration.GetConnectionString("RabbitMQ")).AddApplicationInsightsPublisher();// 自定义健康检查
public class DatabaseHealthCheck : IHealthCheck
{private readonly AppDbContext _context;public DatabaseHealthCheck(AppDbContext context){_context = context;}public async Task<HealthCheckResult> CheckHealthAsync(HealthCheckContext context,CancellationToken cancellationToken = default){try{await _context.Database.CanConnectAsync(cancellationToken);return HealthCheckResult.Healthy();}catch (Exception ex){return HealthCheckResult.Unhealthy("Database connection failed", ex);}}
}// 在Program.cs中配置健康检查端点
app.MapHealthChecks("/health", new HealthCheckOptions
{ResponseWriter = async (context, report) =>{var result = JsonSerializer.Serialize(new{status = report.Status.ToString(),checks = report.Entries.Select(e => new{name = e.Key,status = e.Value.Status.ToString(),description = e.Value.Description,exception = e.Value.Exception?.Message})});context.Response.ContentType = "application/json";await context.Response.WriteAsync(result);}
});

13.2 日志聚合

13.2.1 ELK Stack配置
# docker-compose.yml for ELK
version: '3'
services:elasticsearch:image: docker.elastic.co/elasticsearch/elasticsearch:8.5.1environment:- discovery.type=single-node- xpack.security.enabled=falseports:- "9200:9200"volumes:- esdata:/usr/share/elasticsearch/datahealthcheck:test: ["CMD", "curl", "-f", "http://localhost:9200"]interval: 10stimeout: 5sretries: 3logstash:image: docker.elastic.co/logstash/logstash:8.5.1ports:- "5000:5000"volumes:- ./logstash.conf:/usr/share/logstash/pipeline/logstash.confdepends_on:- elasticsearchlinks:- elasticsearchkibana:image: docker.elastic.co/kibana/kibana:8.5.1ports:- "5601:5601"depends_on:- elasticsearchlinks:- elasticsearchvolumes:esdata:driver: local
13.2.2 Logstash配置
# logstash.conf
input {tcp {port => 5000codec => json_lines}
}filter {mutate {remove_field => ["@version", "host"]}
}output {elasticsearch {hosts => ["elasticsearch:9200"]index => "fastproduction-%{+YYYY.MM.dd}"}
}

13.3 告警系统

13.3.1 Prometheus + Alertmanager
# prometheus.yml
global:scrape_interval: 15sevaluation_interval: 15srule_files:- 'alert.rules'scrape_configs:- job_name: 'fastproduction'metrics_path: '/metrics'static_configs:- targets: ['backend:80', 'frontend:80']alerting:alertmanagers:- static_configs:- targets: ['alertmanager:9093']
13.3.2 告警规则
# alert.rules
groups:
- name: instancerules:- alert: HighErrorRateexpr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.1for: 10mlabels:severity: criticalannotations:summary: "High error rate on {{ $labels.instance }}"description: "Error rate is {{ $value }} for {{ $labels.path }}"- alert: ServiceDownexpr: up == 0for: 5mlabels:severity: criticalannotations:summary: "Service {{ $labels.instance }} is down"description: "{{ $labels.instance }} has been down for more than 5 minutes"

13.4 运维工具

13.4.1 数据库迁移
// 使用EF Core迁移
dotnet ef migrations add InitialCreate
dotnet ef database update// 生产环境迁移脚本
dotnet ef migrations script --idempotent --output migrations.sql
13.4.2 维护任务
// 使用Hangfire执行后台任务
public class MaintenanceService
{private readonly AppDbContext _context;private readonly ILogger<MaintenanceService> _logger;public MaintenanceService(AppDbContext context,ILogger<MaintenanceService> logger){_context = context;_logger = logger;}[AutomaticRetry(Attempts = 3)]public async Task CleanupOldLogs(){var cutoffDate = DateTime.UtcNow.AddMonths(-3);_logger.LogInformation("Cleaning up logs older than {CutoffDate}", cutoffDate);var count = await _context.Logs.Where(l => l.CreatedAt < cutoffDate).BatchDeleteAsync();_logger.LogInformation("Deleted {Count} old log entries", count);}[AutomaticRetry(Attempts = 3)]public async Task BackupDatabase(){_logger.LogInformation("Starting database backup");// 执行备份逻辑_logger.LogInformation("Database backup completed successfully");}
}

14. 开发流程与规范

14.1 Git工作流

采用Git Flow工作流:

  1. 主分支(main):稳定版本,对应生产环境
  2. 开发分支(develop):集成开发功能,对应测试环境
  3. 功能分支(feature/xxx):开发新功能
  4. 修复分支(hotfix/xxx):紧急修复生产问题
  5. 发布分支(release/xxx):准备发布版本

14.2 代码审查

  1. Pull Request模板

    ## 变更描述## 相关Issue## 测试说明## 截图(如适用)
    
  2. 审查清单

    • 代码是否符合编码规范
    • 是否有适当的单元测试
    • 是否影响现有功能
    • 是否有性能问题
    • 是否有安全风险

14.3 编码规范

14.3.1 C#编码规范
  1. 命名约定

    • 类名:PascalCase
    • 方法名:PascalCase
    • 变量名:camelCase
    • 私有字段:_camelCase
    • 常量:UPPER_CASE
  2. 代码组织

    • 使用region组织代码
    • 成员顺序:字段 > 属性 > 构造函数 > 方法
  3. 最佳实践

    • 使用async/await进行异步编程
    • 避免使用魔法字符串
    • 使用依赖注入
    • 遵循SOLID原则
14.3.2 TypeScript/Vue编码规范
  1. 组件命名

    • 单文件组件:PascalCase (UserList.vue)
    • 组件引用:PascalCase
  2. 代码结构

    <script setup lang="ts">
    // 导入顺序: Vue > 第三方库 > 本地组件 > 类型
    import { ref } from 'vue';
    import axios from 'axios';
    import UserForm from './UserForm.vue';
    import type { User } from '@/types';// 组合式API顺序: props > emits > 响应式状态 > 计算属性 > 方法 > 生命周期
    const props = defineProps<{ userId: number }>();const emit = defineEmits(['update', 'delete']);const user = ref<User | null>(null);const fullName = computed(() => {return `${user.value?.firstName} ${user.value?.lastName}`;
    });async function fetchUser() {// ...
    }onMounted(() => {fetchUser();
    });
    </script>
    
  3. 最佳实践

    • 使用Composition API
    • 为组件props定义类型
    • 避免直接操作DOM
    • 使用Pinia进行状态管理

14.4 提交消息规范

使用Conventional Commits规范:

<类型>[可选 范围]: <描述>[可选 正文][可选 脚注]

示例:

feat(users): add user deletion functionalityAdd the ability to delete users from the admin panel. Includes API endpoint, service method and UI component.Closes #123

常见类型:

  • feat:新功能
  • fix:错误修复
  • docs:文档变更
  • style:代码样式变更
  • refactor:代码重构
  • test:测试相关
  • chore:构建或辅助工具变更

15. 安全考虑

15.1 安全防护措施

15.1.1 输入验证
// 使用FluentValidation进行复杂验证
public class CreateUserDtoValidator : AbstractValidator<CreateUserDto>
{public CreateUserDtoValidator(){RuleFor(x => x.Username).NotEmpty().WithMessage("用户名不能为空").Length(3, 20).WithMessage("用户名长度必须在3到20个字符之间").Matches("^[a-zA-Z0-9_]+$").WithMessage("用户名只能包含字母、数字和下划线");RuleFor(x => x.Email).NotEmpty().WithMessage("邮箱不能为空").EmailAddress().WithMessage("邮箱格式不正确");RuleFor(x => x.Password).NotEmpty().WithMessage("密码不能为空").MinimumLength(8).WithMessage("密码长度至少8个字符").Matches("[A-Z]").WithMessage("密码必须包含至少一个大写字母").Matches("[a-z]").WithMessage("密码必须包含至少一个小写字母").Matches("[0-9]").WithMessage("密码必须包含至少一个数字");}
}// 在控制器中使用
[HttpPost]
public async Task<ActionResult<UserDto>> CreateUser([FromBody] CreateUserDto dto)
{var validationResult = await _validator.ValidateAsync(dto);if (!validationResult.IsValid){throw new ValidationException(validationResult.ToDictionary());}// 处理逻辑...
}
15.1.2 输出编码
// 在Razor视图中自动编码输出
<p>@Model.UserInput</p>// 对于需要原始HTML的情况
<p>@Html.Raw(Model.TrustedHtml)</p>
15.1.3 CSRF防护
// 在Program.cs中配置
builder.Services.AddAntiforgery(options =>
{options.HeaderName = "X-CSRF-TOKEN";options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});// 在控制器中使用
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult UpdateProfile(ProfileViewModel model)
{// ...
}

15.2 安全头配置

// 添加安全中间件
app.Use(async (context, next) =>
{// 设置安全相关的HTTP头context.Response.Headers.Add("X-Content-Type-Options", "nosniff");context.Response.Headers.Add("X-Frame-Options", "DENY");context.Response.Headers.Add("X-XSS-Protection", "1; mode=block");context.Response.Headers.Add("Referrer-Policy", "no-referrer");context.Response.Headers.Add("Content-Security-Policy", "default-src 'self'; script-src 'self' 'unsafe-inline' cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:;");await next();
});

15.3 安全审计

15.3.1 依赖安全检查
# 检查NuGet包漏洞
dotnet list package --vulnerable# 检查npm包漏洞
npm audit
15.3.2 静态代码分析

使用SonarQube进行静态代码分析:

# .github/workflows/sonarcloud.yml
name: SonarCloud Scanon:push:branches: [ main ]pull_request:types: [ opened, synchronize, reopened ]jobs:build:runs-on: ubuntu-lateststeps:- uses: actions/checkout@v2with:fetch-depth: 0- name: Setup .NETuses: actions/setup-dotnet@v1with:dotnet-version: '7.0.x'- name: Install SonarCloud scannerrun: dotnet tool install --global dotnet-sonarscanner- name: SonarCloud Scanrun: |dotnet sonarscanner begin /k:"your-project-key" /d:sonar.login="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io"dotnet builddotnet sonarscanner end /d:sonar.login="${{ secrets.SONAR_TOKEN }}"

16. 扩展性设计

16.1 插件系统设计

// 插件接口定义
public interface IPlugin
{string Name { get; }string Version { get; }void Initialize(IServiceCollection services);void Configure(IApplicationBuilder app);
}// 插件管理器
public class PluginManager
{private readonly List<IPlugin> _plugins = new();public void LoadPlugins(string pluginsPath){foreach (var file in Directory.GetFiles(pluginsPath, "*.dll")){var assembly = Assembly.LoadFrom(file);foreach (var type in assembly.GetTypes().Where(t => typeof(IPlugin).IsAssignableFrom(t) && !t.IsAbstract)){if (Activator.CreateInstance(type) is IPlugin plugin){_plugins.Add(plugin);}}}}public void InitializePlugins(IServiceCollection services){foreach (var plugin in _plugins){plugin.Initialize(services);}}public void ConfigurePlugins(IApplicationBuilder app){foreach (var plugin in _plugins){plugin.Configure(app);}}
}// 示例插件
public class ReportingPlugin : IPlugin
{public string Name => "Reporting Plugin";public string Version => "1.0";public void Initialize(IServiceCollection services){services.AddScoped<IReportService, ReportService>();}public void Configure(IApplicationBuilder app){app.Map("/reporting", builder =>{builder.UseEndpoints(endpoints =>{endpoints.MapControllers();});});}
}

16.2 模块化设计

16.2.1 模块定义
public interface IModule
{IServiceCollection RegisterModule(IServiceCollection services);IEndpointRouteBuilder MapEndpoints(IEndpointRouteBuilder endpoints);
}public static class ModuleExtensions
{public static IServiceCollection AddModules(this IServiceCollection services){var modules = DiscoverModules();foreach (var module in modules){module.RegisterModule(services);}return services;}public static WebApplication MapModules(this WebApplication app){var modules = DiscoverModules();foreach (var module in modules){module.MapEndpoints(app);}return app;}private static IEnumerable<IModule> DiscoverModules(){return typeof(IModule).Assembly.GetTypes().Where(p => p.IsClass && p.IsAssignableTo(typeof(IModule))).Select(Activator.CreateInstance).Cast<IModule>();}
}
16.2.2 用户模块示例
public class UserModule : IModule
{public IServiceCollection RegisterModule(IServiceCollection services){services.AddScoped<IUserService, UserService>();services.AddScoped<IUserRepository, UserRepository>();return services;}public IEndpointRouteBuilder MapEndpoints(IEndpointRouteBuilder endpoints){endpoints.MapGet("/api/users", async (IUserService userService) =>{var users = await userService.GetAllAsync();return Results.Ok(users);});// 其他端点...return endpoints;}
}

16.3 动态功能开关

// 功能标志服务
public interface IFeatureService
{bool IsEnabled(string feature);Task SetFeatureStateAsync(string feature, bool enabled);
}// 基于数据库的实现
public class DatabaseFeatureService : IFeatureService
{private readonly AppDbContext _context;public DatabaseFeatureService(AppDbContext context){_context = context;}public bool IsEnabled(string feature){return _context.FeatureFlags.FirstOrDefault(f => f.Name == feature)?.IsEnabled ?? false;}public async Task SetFeatureStateAsync(string feature, bool enabled){var flag = await _context.FeatureFlags.FirstOrDefaultAsync(f => f.Name == feature);if (flag == null){flag = new FeatureFlag { Name = feature };_context.FeatureFlags.Add(flag);}flag.IsEnabled = enabled;await _context.SaveChangesAsync();}
}// 使用特性标记
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class FeatureFlagAttribute : Attribute
{public string Feature { get; }public FeatureFlagAttribute(string feature){Feature = feature;}
}// 特性过滤器
public class FeatureFlagFilter : IAsyncActionFilter
{private readonly IFeatureService _featureService;public FeatureFlagFilter(IFeatureService featureService){_featureService = featureService;}public async Task OnActionExecutionAsync(ActionExecutingContext context,ActionExecutionDelegate next){var featureAttributes = context.ActionDescriptor.EndpointMetadata.OfType<FeatureFlagAttribute>().ToList();if (featureAttributes.Any()){foreach (var attr in featureAttributes){if (!_featureService.IsEnabled(attr.Feature)){context.Result = new NotFoundResult();return;}}}await next();}
}// 在控制器中使用
[FeatureFlag("new-user-interface")]
[HttpGet("new-ui")]
public IActionResult NewUserInterface()
{return View();
}

17. 项目里程碑

17.1 开发阶段

里程碑目标预计时间交付物
1. 基础框架搭建完成项目结构、基础架构和核心模块设计2周项目骨架、核心模块原型
2. 用户管理系统实现用户认证、授权和权限管理3周完整的用户权限系统
3. 代码生成器开发基础CRUD代码生成功能2周代码生成工具和文档
4. 前端框架搭建前端基础架构和核心组件3周前端框架和UI组件库
5. 系统集成前后端集成和联调2周可运行的全栈应用
6. 测试与优化完成系统测试和性能优化2周测试报告、优化方案
7. 文档编写完成技术文档和用户手册1周完整项目文档

17.2 发布计划

版本目标预计时间
v0.1 Alpha内部测试版,核心功能可用第6周
v0.5 Beta公开测试版,主要功能完成第10周
v1.0 RC候选发布版,功能完整第12周
v1.0 GA正式发布版第14周

17.3 迭代计划

迭代重点周期
迭代1核心架构、用户管理2周
迭代2权限系统、代码生成2周
迭代3前端框架、基础UI2周
迭代4系统集成、API完善2周
迭代5测试优化、文档编写2周

18. 团队分工

18.1 角色与职责

角色人数职责
项目经理1项目管理、进度控制、风险管理
架构师1系统架构设计、技术决策
后端开发2-3后端功能开发、API设计
前端开发2前端功能开发、UI实现
测试工程师1-2测试用例设计、质量保证
DevOps工程师1部署流水线、运维支持
技术文档工程师1文档编写、维护

18.2 协作流程

  1. 每日站会:15分钟同步进度和问题
  2. 迭代计划会:每个迭代开始前规划任务
  3. 代码审查:所有代码必须经过PR审查
  4. 演示会议:迭代结束时展示成果
  5. 回顾会议:总结改进点

18.3 沟通工具

  1. 项目管理:Jira/禅道
  2. 文档协作:Confluence/Wiki
  3. 即时通讯:Slack/Teams
  4. 代码托管:GitHub/GitLab
  5. 视频会议:Zoom/Teams

19. 风险评估

19.1 技术风险

风险可能性影响缓解措施
新技术学习曲线提前培训、技术预研
性能瓶颈早期性能测试、优化设计
第三方依赖问题评估备选方案、锁定版本
安全漏洞安全审计、定期更新

19.2 项目风险

风险可能性影响缓解措施
需求变更迭代开发、频繁反馈
进度延误定期检查、缓冲时间
资源不足合理规划、及时调整
沟通不畅明确流程、定期同步

19.3 运营风险

风险可能性影响缓解措施
用户增长过快可扩展架构、自动伸缩
数据安全加密、备份、访问控制
合规问题提前研究相关法规
技术支持完善文档、培训计划

20. 附录

20.1 技术参考资料

  1. ASP.NET Core官方文档
  2. Vue 3官方文档
  3. Entity Framework Core官方文档
  4. Microsoft架构指南
  5. RESTful API设计最佳实践

20.2 工具和资源

  1. Visual Studio 2022
  2. VS Code
  3. Postman
  4. Swagger UI
  5. Docker Desktop
  6. Kubernetes
  7. Azure/AWS服务

20.3 术语表

术语解释
RESTful一种API设计风格
JWTJSON Web Token,用于认证
ORM对象关系映射
DI依赖注入
CI/CD持续集成/持续交付
RBAC基于角色的访问控制
http://www.lryc.cn/news/576117.html

相关文章:

  • Chrome浏览器访问https提示“您的连接不是私密连接”问题解决方案
  • 已对接Shopee、Lazada、亚马逊等知名海外电商平台!商派DigiOS-OMS业务中台助力品牌扩展全球业务
  • 《Opto-Electronic Advances》热点论文速览(2025)
  • linux中python虚拟环境和版本的选择
  • 【Linux手册】进程终止:进程退出和信号的响应机制
  • VB.NET,C#字典对象来保存用户数据,支持大小写
  • Selenium 多窗口截图(窗口切换)二次封装方法详解 —— Python 实践
  • 【Python】实现对LGBT+ rights worldwide (2025)数据集的可视化展示
  • MySQL在C中常用的API接口
  • TiDB AUTO_RANDOM 超大主键前端精度丢失排查:JavaScript Number 限制与解决方案
  • 玩转Linux CAN/CAN FD—SocketCAN的使用
  • opensuse安装rabbitmq
  • 【编译原理】期末复习知识总结
  • 【大数据】大数据产品基础篇
  • 【开源项目】「安卓原生3D开源渲染引擎」:Sceneform‑EQR
  • ArcGIS Pro利用擦除工具,矢量要素消除另一矢量部分区域
  • 【网络安全】密码学知识普及
  • 高可用与低成本兼得:全面解析 TDengine 时序数据库双活与双副本
  • OkHttp 简单配置
  • pandas---使用教程
  • 解构SAP RISE与Cloud ERP授权新政:从许可模式到迁移策略的深度指南
  • (一)miniconda安装配置
  • Dubbo服务调用超时问题解决方案
  • Hyperledger Fabric 入门笔记(二十)Fabric V2.5 测试网络进阶之Tape性能测试
  • Linux tcp_info:监控TCP连接的秘密武器
  • 【RAG面试题】如何获取准确的语义表示
  • MCP-安全(代码实例)
  • ubuntu安装达梦数据库
  • Java8方法引用:简洁高效的编程利器
  • algorithm ——————》双指针(移动0 复写0 快乐数 装水问题 以及数组中找几个数和为指定的元组)