快速开始
**本文引用的文件** - Program.cs(file://App/WebApi/Program.cs) - appsettings.json(file://App/WebApi/appsettings.json) - appsettings.Development.json(file://App/WebApi/appsettings.Development.json) - 环境变量设置.txt(file://Doc/项目相关/环境变量设置.txt) - KevinDbContext.cs(file://Kevin/Kevin.EntityFrameworkCore/Database/KevinDbContext.cs) - TUserBaseData.cs(file://Kevin/Domain/BaseDatas/TUserBaseData.cs) - TRoleBaseData.cs(file://Kevin/Domain/BaseDatas/TRoleBaseData.cs) - Dockerfile(file://App/WebApi/Dockerfile) - docker说明.txt(file://Doc/项目相关/docker说明.txt) - SYSTEM_DOCUMENTATION.md(file://SYSTEM_DOCUMENTATION.md)
目录
简介
本指南面向初次接触 NetCoreKevin 的开发者,目标是帮助你在最短时间内完成本地开发环境的搭建、数据库初始化、服务启动与基础功能验证。你将了解:
- 需要安装的环境与版本
- 如何配置 MySQL、Redis、Qdrant
- 如何通过命令行或容器方式启动后端服务
- 如何使用默认管理员账号登录并体验核心功能
环境要求与依赖
- .NET SDK 9.0+(用于构建与运行 WebApi)
- MySQL 8.0+(数据存储,EF Core 使用 MySQL 提供程序)
- Redis 7.0+(缓存、Hangfire、SignalR 等)
- Qdrant 1.7+(向量检索,AI/RAG 能力)
- 可选:Docker(用于容器化运行与编排)
说明:
- 应用通过配置文件读取连接串与外部服务地址,默认监听端口由 Kestrel 决定;容器镜像中强制暴露 8080 端口。
- 日志框架使用 log4net,便于定位问题。
章节来源
- Dockerfile:4-9(file://App/WebApi/Dockerfile#L4-L9)
- KevinDbContext.cs:108-112(file://Kevin/Kevin.EntityFrameworkCore/Database/KevinDbContext.cs#L108-L112)
- appsettings.json:12-15(file://App/WebApi/appsettings.json#L12-L15)
数据库与缓存配置
- 数据库连接串
- 在 appsettings.json 或 appsettings.Development.json 的 ConnectionStrings.dbConnection 中配置 MySQL 连接信息(服务器、端口、数据库名、用户名、密码等)。
- EF Core 使用 MySQL 提供程序,并在 OnModelCreating 中自动注册种子数据配置。
- 缓存连接串
- ConnectionStrings.redisConnection 用于缓存、Hangfire、SignalR 等模块。
- Hangfire 与 SignalR 的 Redis 配置
- HangfireSetting.HangfireRedisSetting 与 SignalrRdisSetting 分别配置 Hangfire 与 SignalR 使用的 Redis 实例与键前缀等。
- Qdrant 客户端
- QdrantClientSetting.Url 指向 Qdrant 服务地址,供 AI/RAG 模块使用。
提示:
- 若本地使用 Docker 运行 MySQL/Redis/Qdrant,请确保端口映射正确,并在配置中填写宿主机可达的地址。
- 若使用容器部署,ASP.NET 进程会监听 8080 端口(可通过 ASPNETCORE_URLS 覆盖)。
章节来源
- appsettings.json:12-20(file://App/WebApi/appsettings.json#L12-L20)
- appsettings.json:37-45(file://App/WebApi/appsettings.json#L37-L45)
- appsettings.json:133-135(file://App/WebApi/appsettings.json#L133-L135)
- KevinDbContext.cs:108-112(file://Kevin/Kevin.EntityFrameworkCore/Database/KevinDbContext.cs#L108-L112)
环境变量设置
- 环境变量 ASPNETCORE_ENVIRONMENT 控制加载的配置集(Development/Test/Production)。
- Windows PowerShell 示例:$env:ASPNETCORE_ENVIRONMENT = "Development"
- Linux/macOS 示例:export ASPNETCORE_ENVIRONMENT=Development
- 应用启动时会读取该环境变量以选择对应环境的配置文件。
章节来源
- 环境变量设置.txt:1-16(file://Doc/项目相关/环境变量设置.txt#L1-L16)
- Program.cs:27-29(file://App/WebApi/Program.cs#L27-L29)
首次运行(从零到可访问)
步骤总览:
- 准备依赖服务
- 启动 MySQL 8.0+,创建数据库 kevin_app(或使用你配置的数据库名),确保用户具备读写权限。
- 启动 Redis 7.0+,确认端口与密码与配置一致。
- 启动 Qdrant 1.7+,确认地址与 QdrantClientSetting 一致。
- 配置连接串与环境
- 在 appsettings.Development.json 中设置 dbConnection、redisConnection、QdrantClientSetting 等。
- 设置环境变量 ASPNETCORE_ENVIRONMENT=Development。
- 执行数据库迁移与种子数据
- 将默认项目设置为 Kevin.EntityFrameworkCore。
- 执行添加迁移与应用迁移命令,使表结构与种子数据生效。
- 启动后端服务
- 命令行:进入 App/WebApi 目录,执行 dotnet run --environment Development。
- 或在 Visual Studio 中将 App.WebApi 设为启动项目并按 F5。
- 访问服务
- API 文档:http://localhost:9901/swagger(如未修改端口)
- Hangfire 面板:http://localhost:9901/pchangfire(如已启用)
- 前端(可选)
- 如需体验前端界面,可在 vue/kevin.web.vue 目录下按前端工程方式启动,并确保跨域与后端地址配置正确。
注意:
- 若使用 Docker 运行后端,镜像默认暴露 8080 端口,可通过 -p 映射到宿主机端口。
- 若出现 CORS 限制,检查 CorsSetting 中的 IP 列表是否包含前端地址。
章节来源
- SYSTEM_DOCUMENTATION.md:116-185(file://SYSTEM_DOCUMENTATION.md#L116-L185)
- Program.cs:23-85(file://App/WebApi/Program.cs#L23-L85)
- Dockerfile:4-9(file://App/WebApi/Dockerfile#L4-L9)
默认账户与功能验证
- 默认管理员账户
- 用户名:admin
- 密码:123456
- 租户:1000(默认初始化租户)
- 验证方法
- 打开 Swagger 文档,调用鉴权相关接口获取令牌后,再调用受保护接口进行验证。
- 或通过前端登录页面输入默认账号密码,登录后访问系统管理菜单(如用户、角色、字典、日志等)。
- 种子数据
- 应用会在 EF Core 模型构建阶段注册种子数据配置,包括用户、角色、租户、字典等。
章节来源
- TUserBaseData.cs:12-15(file://Kevin/Domain/BaseDatas/TUserBaseData.cs#L12-L15)
- TRoleBaseData.cs:7-11(file://Kevin/Domain/BaseDatas/TRoleBaseData.cs#L7-L11)
- KevinDbContext.cs:290-303(file://Kevin/Kevin.EntityFrameworkCore/Database/KevinDbContext.cs#L290-L303)
- SYSTEM_DOCUMENTATION.md:181-185(file://SYSTEM_DOCUMENTATION.md#L181-L185)
架构概览
下图展示了从浏览器/客户端到后端 API、再到数据库与缓存的关键交互路径。
#mermaid-svg-VQxmWqHsSt25Aw5H{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-VQxmWqHsSt25Aw5H .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-VQxmWqHsSt25Aw5H .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-VQxmWqHsSt25Aw5H .error-icon{fill:#552222;}#mermaid-svg-VQxmWqHsSt25Aw5H .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-VQxmWqHsSt25Aw5H .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-VQxmWqHsSt25Aw5H .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-VQxmWqHsSt25Aw5H .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-VQxmWqHsSt25Aw5H .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-VQxmWqHsSt25Aw5H .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-VQxmWqHsSt25Aw5H .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-VQxmWqHsSt25Aw5H .marker{fill:#333333;stroke:#333333;}#mermaid-svg-VQxmWqHsSt25Aw5H .marker.cross{stroke:#333333;}#mermaid-svg-VQxmWqHsSt25Aw5H svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-VQxmWqHsSt25Aw5H p{margin:0;}#mermaid-svg-VQxmWqHsSt25Aw5H .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-VQxmWqHsSt25Aw5H .cluster-label text{fill:#333;}#mermaid-svg-VQxmWqHsSt25Aw5H .cluster-label span{color:#333;}#mermaid-svg-VQxmWqHsSt25Aw5H .cluster-label span p{background-color:transparent;}#mermaid-svg-VQxmWqHsSt25Aw5H .label text,#mermaid-svg-VQxmWqHsSt25Aw5H span{fill:#333;color:#333;}#mermaid-svg-VQxmWqHsSt25Aw5H .node rect,#mermaid-svg-VQxmWqHsSt25Aw5H .node circle,#mermaid-svg-VQxmWqHsSt25Aw5H .node ellipse,#mermaid-svg-VQxmWqHsSt25Aw5H .node polygon,#mermaid-svg-VQxmWqHsSt25Aw5H .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-VQxmWqHsSt25Aw5H .rough-node .label text,#mermaid-svg-VQxmWqHsSt25Aw5H .node .label text,#mermaid-svg-VQxmWqHsSt25Aw5H .image-shape .label,#mermaid-svg-VQxmWqHsSt25Aw5H .icon-shape .label{text-anchor:middle;}#mermaid-svg-VQxmWqHsSt25Aw5H .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-VQxmWqHsSt25Aw5H .rough-node .label,#mermaid-svg-VQxmWqHsSt25Aw5H .node .label,#mermaid-svg-VQxmWqHsSt25Aw5H .image-shape .label,#mermaid-svg-VQxmWqHsSt25Aw5H .icon-shape .label{text-align:center;}#mermaid-svg-VQxmWqHsSt25Aw5H .node.clickable{cursor:pointer;}#mermaid-svg-VQxmWqHsSt25Aw5H .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-VQxmWqHsSt25Aw5H .arrowheadPath{fill:#333333;}#mermaid-svg-VQxmWqHsSt25Aw5H .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-VQxmWqHsSt25Aw5H .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-VQxmWqHsSt25Aw5H .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VQxmWqHsSt25Aw5H .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-VQxmWqHsSt25Aw5H .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VQxmWqHsSt25Aw5H .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-VQxmWqHsSt25Aw5H .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-VQxmWqHsSt25Aw5H .cluster text{fill:#333;}#mermaid-svg-VQxmWqHsSt25Aw5H .cluster span{color:#333;}#mermaid-svg-VQxmWqHsSt25Aw5H div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-VQxmWqHsSt25Aw5H .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-VQxmWqHsSt25Aw5H rect.text{fill:none;stroke-width:0;}#mermaid-svg-VQxmWqHsSt25Aw5H .icon-shape,#mermaid-svg-VQxmWqHsSt25Aw5H .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-VQxmWqHsSt25Aw5H .icon-shape p,#mermaid-svg-VQxmWqHsSt25Aw5H .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-VQxmWqHsSt25Aw5H .icon-shape .label rect,#mermaid-svg-VQxmWqHsSt25Aw5H .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-VQxmWqHsSt25Aw5H .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-VQxmWqHsSt25Aw5H .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-VQxmWqHsSt25Aw5H :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 浏览器/客户端
WebApi (Kestrel)
认证/授权模块
Redis(缓存/Hangfire/SignalR)
MySQL(持久化)
Qdrant(向量检索)
图表来源
- Program.cs:23-85(file://App/WebApi/Program.cs#L23-L85)
- appsettings.json:12-20(file://App/WebApi/appsettings.json#L12-L20)
- appsettings.json:133-135(file://App/WebApi/appsettings.json#L133-L135)
- KevinDbContext.cs:108-112(file://Kevin/Kevin.EntityFrameworkCore/Database/KevinDbContext.cs#L108-L112)
详细组件分析
启动流程(Program 主入口)
- 设置环境变量并读取配置
- 注册日志、控制器、全局中间件
- 构建应用并运行
- 开发环境启用开发者异常页,生产环境启用统一异常处理
"WebApplication" "服务注册" "WebApplication.CreateBuilder" "Program.Main" "操作系统" "WebApplication" "服务注册" "WebApplication.CreateBuilder" "Program.Main" "操作系统" #mermaid-svg-cx1bXnjiiS4g1d70{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-cx1bXnjiiS4g1d70 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-cx1bXnjiiS4g1d70 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-cx1bXnjiiS4g1d70 .error-icon{fill:#552222;}#mermaid-svg-cx1bXnjiiS4g1d70 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-cx1bXnjiiS4g1d70 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-cx1bXnjiiS4g1d70 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-cx1bXnjiiS4g1d70 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-cx1bXnjiiS4g1d70 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-cx1bXnjiiS4g1d70 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-cx1bXnjiiS4g1d70 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-cx1bXnjiiS4g1d70 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-cx1bXnjiiS4g1d70 .marker.cross{stroke:#333333;}#mermaid-svg-cx1bXnjiiS4g1d70 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-cx1bXnjiiS4g1d70 p{margin:0;}#mermaid-svg-cx1bXnjiiS4g1d70 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-cx1bXnjiiS4g1d70 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-cx1bXnjiiS4g1d70 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-cx1bXnjiiS4g1d70 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-cx1bXnjiiS4g1d70 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-cx1bXnjiiS4g1d70 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-cx1bXnjiiS4g1d70 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-cx1bXnjiiS4g1d70 .sequenceNumber{fill:white;}#mermaid-svg-cx1bXnjiiS4g1d70 #sequencenumber{fill:#333;}#mermaid-svg-cx1bXnjiiS4g1d70 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-cx1bXnjiiS4g1d70 .messageText{fill:#333;stroke:none;}#mermaid-svg-cx1bXnjiiS4g1d70 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-cx1bXnjiiS4g1d70 .labelText,#mermaid-svg-cx1bXnjiiS4g1d70 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-cx1bXnjiiS4g1d70 .loopText,#mermaid-svg-cx1bXnjiiS4g1d70 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-cx1bXnjiiS4g1d70 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-cx1bXnjiiS4g1d70 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-cx1bXnjiiS4g1d70 .noteText,#mermaid-svg-cx1bXnjiiS4g1d70 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-cx1bXnjiiS4g1d70 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-cx1bXnjiiS4g1d70 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-cx1bXnjiiS4g1d70 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-cx1bXnjiiS4g1d70 .actorPopupMenu{position:absolute;}#mermaid-svg-cx1bXnjiiS4g1d70 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-cx1bXnjiiS4g1d70 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-cx1bXnjiiS4g1d70 .actor-man circle,#mermaid-svg-cx1bXnjiiS4g1d70 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-cx1bXnjiiS4g1d70 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 启动进程 设置环境变量 创建构建器 注册日志/控制器/扩展服务 构建应用 启用中间件/异常处理 Run()
图表来源
- Program.cs:23-85(file://App/WebApi/Program.cs#L23-L85)
章节来源
- Program.cs:23-85(file://App/WebApi/Program.cs#L23-L85)
数据库上下文与种子数据
- DbContext 使用 MySQL 提供程序,并自动扫描实体类型、设置表名前缀与字段注释
- 在 OnModelCreating 中注册种子数据配置(用户、角色、租户、字典等)
- SaveChanges 中处理领域事件发布、乐观并发、多租户字段注入
#mermaid-svg-xSwGyMBMddEuJaPt{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-xSwGyMBMddEuJaPt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-xSwGyMBMddEuJaPt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-xSwGyMBMddEuJaPt .error-icon{fill:#552222;}#mermaid-svg-xSwGyMBMddEuJaPt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-xSwGyMBMddEuJaPt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-xSwGyMBMddEuJaPt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-xSwGyMBMddEuJaPt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-xSwGyMBMddEuJaPt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-xSwGyMBMddEuJaPt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-xSwGyMBMddEuJaPt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-xSwGyMBMddEuJaPt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-xSwGyMBMddEuJaPt .marker.cross{stroke:#333333;}#mermaid-svg-xSwGyMBMddEuJaPt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-xSwGyMBMddEuJaPt p{margin:0;}#mermaid-svg-xSwGyMBMddEuJaPt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-xSwGyMBMddEuJaPt .cluster-label text{fill:#333;}#mermaid-svg-xSwGyMBMddEuJaPt .cluster-label span{color:#333;}#mermaid-svg-xSwGyMBMddEuJaPt .cluster-label span p{background-color:transparent;}#mermaid-svg-xSwGyMBMddEuJaPt .label text,#mermaid-svg-xSwGyMBMddEuJaPt span{fill:#333;color:#333;}#mermaid-svg-xSwGyMBMddEuJaPt .node rect,#mermaid-svg-xSwGyMBMddEuJaPt .node circle,#mermaid-svg-xSwGyMBMddEuJaPt .node ellipse,#mermaid-svg-xSwGyMBMddEuJaPt .node polygon,#mermaid-svg-xSwGyMBMddEuJaPt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-xSwGyMBMddEuJaPt .rough-node .label text,#mermaid-svg-xSwGyMBMddEuJaPt .node .label text,#mermaid-svg-xSwGyMBMddEuJaPt .image-shape .label,#mermaid-svg-xSwGyMBMddEuJaPt .icon-shape .label{text-anchor:middle;}#mermaid-svg-xSwGyMBMddEuJaPt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-xSwGyMBMddEuJaPt .rough-node .label,#mermaid-svg-xSwGyMBMddEuJaPt .node .label,#mermaid-svg-xSwGyMBMddEuJaPt .image-shape .label,#mermaid-svg-xSwGyMBMddEuJaPt .icon-shape .label{text-align:center;}#mermaid-svg-xSwGyMBMddEuJaPt .node.clickable{cursor:pointer;}#mermaid-svg-xSwGyMBMddEuJaPt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-xSwGyMBMddEuJaPt .arrowheadPath{fill:#333333;}#mermaid-svg-xSwGyMBMddEuJaPt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-xSwGyMBMddEuJaPt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-xSwGyMBMddEuJaPt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xSwGyMBMddEuJaPt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-xSwGyMBMddEuJaPt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xSwGyMBMddEuJaPt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-xSwGyMBMddEuJaPt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-xSwGyMBMddEuJaPt .cluster text{fill:#333;}#mermaid-svg-xSwGyMBMddEuJaPt .cluster span{color:#333;}#mermaid-svg-xSwGyMBMddEuJaPt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-xSwGyMBMddEuJaPt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-xSwGyMBMddEuJaPt rect.text{fill:none;stroke-width:0;}#mermaid-svg-xSwGyMBMddEuJaPt .icon-shape,#mermaid-svg-xSwGyMBMddEuJaPt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-xSwGyMBMddEuJaPt .icon-shape p,#mermaid-svg-xSwGyMBMddEuJaPt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-xSwGyMBMddEuJaPt .icon-shape .label rect,#mermaid-svg-xSwGyMBMddEuJaPt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-xSwGyMBMddEuJaPt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-xSwGyMBMddEuJaPt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-xSwGyMBMddEuJaPt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} SaveChanges 入口
发布领域事件
更新 RowVersion(乐观并发)
写入 TenantId(新增记录)
提交到数据库
完成
图表来源
- KevinDbContext.cs:405-483(file://Kevin/Kevin.EntityFrameworkCore/Database/KevinDbContext.cs#L405-L483)
- KevinDbContext.cs:290-303(file://Kevin/Kevin.EntityFrameworkCore/Database/KevinDbContext.cs#L290-L303)
章节来源
- KevinDbContext.cs:83-133(file://Kevin/Kevin.EntityFrameworkCore/Database/KevinDbContext.cs#L83-L133)
- KevinDbContext.cs:165-303(file://Kevin/Kevin.EntityFrameworkCore/Database/KevinDbContext.cs#L165-L303)
- KevinDbContext.cs:405-483(file://Kevin/Kevin.EntityFrameworkCore/Database/KevinDbContext.cs#L405-L483)
容器化运行(Docker)
- 基础镜像使用 aspnet:9.0,工作目录 /app,暴露 8080 端口
- 构建阶段恢复依赖、编译并发布
- 最终镜像仅包含发布产物,入口为 dotnet App.WebApi.dll
#mermaid-svg-IPwby3eUQ4PBcJOZ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-IPwby3eUQ4PBcJOZ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-IPwby3eUQ4PBcJOZ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-IPwby3eUQ4PBcJOZ .error-icon{fill:#552222;}#mermaid-svg-IPwby3eUQ4PBcJOZ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-IPwby3eUQ4PBcJOZ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-IPwby3eUQ4PBcJOZ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-IPwby3eUQ4PBcJOZ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-IPwby3eUQ4PBcJOZ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-IPwby3eUQ4PBcJOZ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-IPwby3eUQ4PBcJOZ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-IPwby3eUQ4PBcJOZ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-IPwby3eUQ4PBcJOZ .marker.cross{stroke:#333333;}#mermaid-svg-IPwby3eUQ4PBcJOZ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-IPwby3eUQ4PBcJOZ p{margin:0;}#mermaid-svg-IPwby3eUQ4PBcJOZ .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-IPwby3eUQ4PBcJOZ .cluster-label text{fill:#333;}#mermaid-svg-IPwby3eUQ4PBcJOZ .cluster-label span{color:#333;}#mermaid-svg-IPwby3eUQ4PBcJOZ .cluster-label span p{background-color:transparent;}#mermaid-svg-IPwby3eUQ4PBcJOZ .label text,#mermaid-svg-IPwby3eUQ4PBcJOZ span{fill:#333;color:#333;}#mermaid-svg-IPwby3eUQ4PBcJOZ .node rect,#mermaid-svg-IPwby3eUQ4PBcJOZ .node circle,#mermaid-svg-IPwby3eUQ4PBcJOZ .node ellipse,#mermaid-svg-IPwby3eUQ4PBcJOZ .node polygon,#mermaid-svg-IPwby3eUQ4PBcJOZ .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-IPwby3eUQ4PBcJOZ .rough-node .label text,#mermaid-svg-IPwby3eUQ4PBcJOZ .node .label text,#mermaid-svg-IPwby3eUQ4PBcJOZ .image-shape .label,#mermaid-svg-IPwby3eUQ4PBcJOZ .icon-shape .label{text-anchor:middle;}#mermaid-svg-IPwby3eUQ4PBcJOZ .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-IPwby3eUQ4PBcJOZ .rough-node .label,#mermaid-svg-IPwby3eUQ4PBcJOZ .node .label,#mermaid-svg-IPwby3eUQ4PBcJOZ .image-shape .label,#mermaid-svg-IPwby3eUQ4PBcJOZ .icon-shape .label{text-align:center;}#mermaid-svg-IPwby3eUQ4PBcJOZ .node.clickable{cursor:pointer;}#mermaid-svg-IPwby3eUQ4PBcJOZ .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-IPwby3eUQ4PBcJOZ .arrowheadPath{fill:#333333;}#mermaid-svg-IPwby3eUQ4PBcJOZ .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-IPwby3eUQ4PBcJOZ .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-IPwby3eUQ4PBcJOZ .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IPwby3eUQ4PBcJOZ .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-IPwby3eUQ4PBcJOZ .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IPwby3eUQ4PBcJOZ .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-IPwby3eUQ4PBcJOZ .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-IPwby3eUQ4PBcJOZ .cluster text{fill:#333;}#mermaid-svg-IPwby3eUQ4PBcJOZ .cluster span{color:#333;}#mermaid-svg-IPwby3eUQ4PBcJOZ div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-IPwby3eUQ4PBcJOZ .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-IPwby3eUQ4PBcJOZ rect.text{fill:none;stroke-width:0;}#mermaid-svg-IPwby3eUQ4PBcJOZ .icon-shape,#mermaid-svg-IPwby3eUQ4PBcJOZ .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-IPwby3eUQ4PBcJOZ .icon-shape p,#mermaid-svg-IPwby3eUQ4PBcJOZ .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-IPwby3eUQ4PBcJOZ .icon-shape .label rect,#mermaid-svg-IPwby3eUQ4PBcJOZ .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-IPwby3eUQ4PBcJOZ .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-IPwby3eUQ4PBcJOZ .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-IPwby3eUQ4PBcJOZ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} dotnet build
dotnet publish
复制到 final 镜像
ENTRYPOINT: dotnet App.WebApi.dll
图表来源
- Dockerfile:13-66(file://App/WebApi/Dockerfile#L13-L66)
- Dockerfile:69-74(file://App/WebApi/Dockerfile#L69-L74)
章节来源
- Dockerfile:4-9(file://App/WebApi/Dockerfile#L4-L9)
- Dockerfile:13-66(file://App/WebApi/Dockerfile#L13-L66)
- Dockerfile:69-74(file://App/WebApi/Dockerfile#L69-L74)
依赖关系分析
- 运行时依赖
- .NET Runtime(由镜像提供)
- MySQL(持久化)
- Redis(缓存、任务队列、实时通信)
- Qdrant(向量检索)
- 配置依赖
- appsettings.* 与环境变量共同决定行为
- MigrationsAssembly 指定 EF 迁移程序集
- 模块依赖
- WebApi 依赖 Kevin.Module 下的多个子模块(认证、缓存、日志、权限、SignalR、RAG 等)
#mermaid-svg-Bu4FDlpfpGJ4SXJv{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Bu4FDlpfpGJ4SXJv .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .error-icon{fill:#552222;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .marker.cross{stroke:#333333;}#mermaid-svg-Bu4FDlpfpGJ4SXJv svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Bu4FDlpfpGJ4SXJv p{margin:0;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .cluster-label text{fill:#333;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .cluster-label span{color:#333;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .cluster-label span p{background-color:transparent;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .label text,#mermaid-svg-Bu4FDlpfpGJ4SXJv span{fill:#333;color:#333;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .node rect,#mermaid-svg-Bu4FDlpfpGJ4SXJv .node circle,#mermaid-svg-Bu4FDlpfpGJ4SXJv .node ellipse,#mermaid-svg-Bu4FDlpfpGJ4SXJv .node polygon,#mermaid-svg-Bu4FDlpfpGJ4SXJv .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .rough-node .label text,#mermaid-svg-Bu4FDlpfpGJ4SXJv .node .label text,#mermaid-svg-Bu4FDlpfpGJ4SXJv .image-shape .label,#mermaid-svg-Bu4FDlpfpGJ4SXJv .icon-shape .label{text-anchor:middle;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .rough-node .label,#mermaid-svg-Bu4FDlpfpGJ4SXJv .node .label,#mermaid-svg-Bu4FDlpfpGJ4SXJv .image-shape .label,#mermaid-svg-Bu4FDlpfpGJ4SXJv .icon-shape .label{text-align:center;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .node.clickable{cursor:pointer;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .arrowheadPath{fill:#333333;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Bu4FDlpfpGJ4SXJv .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bu4FDlpfpGJ4SXJv .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Bu4FDlpfpGJ4SXJv .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .cluster text{fill:#333;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .cluster span{color:#333;}#mermaid-svg-Bu4FDlpfpGJ4SXJv div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Bu4FDlpfpGJ4SXJv rect.text{fill:none;stroke-width:0;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .icon-shape,#mermaid-svg-Bu4FDlpfpGJ4SXJv .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .icon-shape p,#mermaid-svg-Bu4FDlpfpGJ4SXJv .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .icon-shape .label rect,#mermaid-svg-Bu4FDlpfpGJ4SXJv .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Bu4FDlpfpGJ4SXJv .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Bu4FDlpfpGJ4SXJv .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Bu4FDlpfpGJ4SXJv :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} App.WebApi
Kevin.Module.*
MySQL
Redis
Qdrant
图表来源
- appsettings.json:9-15(file://App/WebApi/appsettings.json#L9-L15)
- appsettings.json:133-135(file://App/WebApi/appsettings.json#L133-L135)
- KevinDbContext.cs:108-112(file://Kevin/Kevin.EntityFrameworkCore/Database/KevinDbContext.cs#L108-L112)
章节来源
- appsettings.json:9-15(file://App/WebApi/appsettings.json#L9-L15)
- appsettings.json:133-135(file://App/WebApi/appsettings.json#L133-L135)
性能与启动优化建议
- 合理设置连接池与超时参数(MySQL 连接串中的 Command Timeout 等)
- 使用 Redis 作为缓存与会话存储,降低数据库压力
- 在生产环境关闭不必要的调试中间件,启用统一异常处理
- 根据业务量调整 Hangfire 与 SignalR 的 Redis 分库与键前缀,避免冲突
- 如需 HTTPS,可在 Kestrel 中绑定证书并启用默认 HTTPS 配置
本节为通用建议,不直接分析具体代码
故障排除指南
常见问题与排查要点:
- 无法连接数据库
- 检查 MySQL 是否启动、端口是否正确、用户名密码与数据库名是否与配置一致
- 查看 appsettings.* 中 ConnectionStrings.dbConnection
- 确认防火墙与安全组放行端口
- 无法连接 Redis
- 检查 Redis 是否启动、端口与密码是否与 redisConnection 一致
- Hangfire/SignalR 相关配置是否指向正确的 Redis 实例
- Qdrant 不可用导致 AI/RAG 功能异常
- 检查 QdrantClientSetting.Url 是否可访问
- 确认网络连通性与防火墙策略
- 启动时报错或端口占用
- 使用 ASPNETCORE_URLS 指定监听地址与端口
- 在容器中通过 -p 映射端口,确保宿主机端口未被占用
- 权限或登录失败
- 确认已执行数据库迁移并生成种子数据
- 使用默认管理员账号登录(admin/123456,租户 1000)
章节来源
- appsettings.json:12-20(file://App/WebApi/appsettings.json#L12-L20)
- appsettings.json:133-135(file://App/WebApi/appsettings.json#L133-L135)
- KevinDbContext.cs:290-303(file://Kevin/Kevin.EntityFrameworkCore/Database/KevinDbContext.cs#L290-L303)
- docker说明.txt:1-44(file://Doc/项目相关/docker说明.txt#L1-L44)
结论
通过以上步骤,你可以在本地快速搭建 NetCoreKevin 的开发环境并完成首次运行。建议后续:
- 完善各环境(Development/Test/Production)的配置差异
- 按需启用或配置第三方服务(短信、邮件、云存储等)
- 结合前端工程进行联调与功能验证
- 在生产环境做好安全加固、监控与备份策略