# 大作业·工程结构设计 > 基于《思维导图》与《大作业项目要求》,前端技术栈统一采用 **React + Vite**,后端为 Go(Gin + gRPC)。 ## 一、仓库根目录结构(`final_homework/`) ``` final_homework/ ├── hr-frontend/ # HR管理端前端(React + Vite) ├── user-frontend/ # 候选人用户端前端(React + Vite) ├── web-gin-service/ # Gin Web网关服务 ├── logic-grpc-service/ # gRPC核心业务服务 ├── proto/ # Protobuf 定义 │ └── v1/ │ ├── auth.proto # 认证相关 │ ├── job.proto # 岗位相关 │ ├── application.proto # 投递相关 │ ├── profile.proto # 候选人档案 │ ├── resume.proto # 简历/OSS上传 │ └── chat.proto # AI对话 ├── api.md # 前后端接口说明文档 ├── db.md # 数据库设计文档 ├── README.md # 启动部署指南 + 项目亮点 └── answer.md # 拓展设计方案(OpenClaw/Hermes集成思路) ``` --- ## 二、HR管理端前端(`hr-frontend/`) ``` hr-frontend/ ├── index.html ├── package.json ├── vite.config.ts # Vite 配置,配置代理到 web-gin-service ├── tsconfig.json ├── .env # 前端环境变量(API_BASE_URL 等,不进 Git) ├── .gitignore ├── public/ │ └── favicon.ico ├── src/ │ ├── main.tsx # 入口文件 │ ├── App.tsx # 路由入口 + 权限守卫 │ ├── vite-env.d.ts │ │ │ ├── styles/ # 全局样式 │ │ ├── global.css │ │ └── variables.css │ │ │ ├── router/ # 路由配置 │ │ └── index.tsx # 独立路由页面:/hr/* │ │ │ ├── api/ # Axios 请求封装 │ │ ├── client.ts # axios 实例 + baseURL / interceptor │ │ ├── auth.ts # 登录/注册接口 │ │ ├── job.ts # 岗位 CRUD 接口 │ │ ├── application.ts # 投递列表接口 │ │ ├── profile.ts # 候选人档案接口 │ │ ├── resume.ts # 简历信息接口 │ │ └── chat.ts # AI对话接口 │ │ │ ├── stores/ # 状态管理(useState + Context 即可) │ │ ├── AuthContext.tsx # 登录态 + JWT Token 上下文 │ │ └── ChatContext.tsx # AI对话消息历史上下文 │ │ │ ├── pages/ # 页面组件 │ │ ├── LoginPage.tsx # HR登录页 │ │ ├── RegisterPage.tsx # HR注册页 │ │ └── dashboard/ # 后台主页面(登录后可见) │ │ ├── DashboardLayout.tsx # 布局框架(侧边栏 + 顶栏) │ │ ├── HomePage.tsx # 工作台概览 │ │ ├── JobListPage.tsx # 岗位列表(分页、搜索) │ │ ├── JobEditPage.tsx # 新建/编辑岗位表单 │ │ ├── CandidatePage.tsx # 岗位下候选人列表(含投递状态) │ │ ├── ProfileViewPage.tsx # 候选人结构化档案详情页 │ │ └── ChatPage.tsx # AI智能对话窗口(常驻) │ │ │ ├── components/ # 公共组件 │ │ ├── Layout/ │ │ │ ├── Sidebar.tsx # 左侧导航栏 │ │ │ └── Header.tsx # 顶部栏(显示当前HR头像/退出) │ │ ├── Common/ │ │ │ ├── LoadingSpinner.tsx │ │ │ ├── EmptyState.tsx │ │ │ └── PageHeader.tsx │ │ └── UI/ # 基础 UI 组件(可用 Ant Design / Shadcn) │ │ ├── Table.tsx │ │ ├── Form.tsx │ │ ├── Modal.tsx │ │ └── Tag.tsx │ │ │ ├── types/ # TypeScript 类型定义 │ │ ├── auth.ts │ │ ├── job.ts │ │ ├── application.ts │ │ ├── profile.ts │ │ ├── resume.ts │ │ └── chat.ts │ │ │ └── utils/ # 工具函数 │ ├── request.ts # 通用请求拦截(自动带 Token) │ └── format.ts # 日期/数字格式化工具 ``` ### HR端关键路由 | 路径 | 页面 | 说明 | 鉴权 | |------|------|------|------| | `/hr/login` | 登录页 | HR账号密码登录 | 访客可访问 | | `/hr/register` | 注册页 | HR账号注册 | 访客可访问 | | `/hr/dashboard` | 工作台 | 数据概览 | 必须JWT有效 | | `/hr/dashboard/jobs` | 岗位列表 | 本人发布的岗位 | 必须JWT有效 | | `/hr/dashboard/jobs/create` | 新建岗位 | 表单页 | 必须JWT有效 | | `/hr/dashboard/jobs/:id/edit` | 编辑岗位 | 回显+修改 | 必须是创建者 | | `/hr/dashboard/candidates` | 候选人列表 | 岗位下投递者 | 必须JWT有效 | | `/hr/dashboard/candidates/:id/profile` | 档案详情 | 结构化资料 | 必须JWT有效 | | `/hr/dashboard/chat` | AI对话 | 智能问答窗口 | 必须JWT有效 | --- ## 三、候选人用户端前端(`user-frontend/`) ``` user-frontend/ ├── index.html ├── package.json ├── vite.config.ts ├── tsconfig.json ├── .env ├── .gitignore ├── public/ │ └── favicon.ico ├── src/ │ ├── main.tsx │ ├── App.tsx # 路由入口 + 游客/已登录态切换 │ ├── vite-env.d.ts │ │ │ ├── styles/ │ │ ├── global.css │ │ └── variables.css │ │ │ ├── router/ │ │ └── index.tsx # 独立路由页面:/user/* │ │ │ ├── api/ # Axios 请求封装(同hr-frontend模式) │ │ ├── client.ts │ │ ├── auth.ts │ │ ├── job.ts │ │ ├── profile.ts │ │ ├── resume.ts │ │ └── application.ts │ │ │ ├── stores/ │ │ └── AuthContext.tsx # 登录态 + Token 上下文 │ │ │ ├── pages/ │ │ ├── HomePage.tsx # 首页:公开岗位列表(游客可见) │ │ ├── LoginPage.tsx # 候选人登录页 │ │ ├── RegisterPage.tsx # 候选人注册页(强校验) │ │ ├── JobDetailPage.tsx # 岗位详情(含投递按钮) │ │ ├── SetupProfilePage.tsx # 引导完善档案 │ │ ├── ResumeUploadPage.tsx # 简历上传页(OSS签名URL直传) │ │ └── ApplicationPage.tsx # 我的投递记录 │ │ │ ├── components/ │ │ ├── Layout/ │ │ │ ├── Navbar.tsx # 顶栏导航(游客/已登录不同UI) │ │ │ └── Footer.tsx │ │ ├── JobCard.tsx # 岗位卡片组件(复用) │ │ ├── JobList.tsx # 岗位列表组件 │ │ ├── UploadProgress.tsx # 简历上传进度条 │ │ ├── WarningModal.tsx # 拦截弹窗(未完善资料/无简历) │ │ └── Common/ │ │ ├── EmptyState.tsx │ │ └── LoadingSpinner.tsx │ │ │ ├── types/ │ │ ├── auth.ts │ │ ├── job.ts │ │ ├── profile.ts │ │ ├── resume.ts │ │ └── application.ts │ │ │ └── utils/ │ ├── request.ts │ └── format.ts ``` ### 候选人端关键路由 | 路径 | 页面 | 说明 | 鉴权 | |------|------|------|------| | `/user/` | 首页 | 公开岗位列表 | 免登录 | | `/user/login` | 登录页 | 候选人登录 | 访客可访问 | | `/user/register` | 注册页 | 候选人注册(强校验) | 访客可访问 | | `/user/job/:id` | 岗位详情 | 查看岗位 + 投递 | 投递需登录 | | `/user/profile/setup` | 完善档案 | 必填字段表单 | 必须登录 | | `/user/resume/upload` | 简历上传 | OSS签名URL直传 | 必须登录 | | `/user/applications` | 我的投递 | 投递记录列表 | 必须登录 | --- ## 四、Web 网关服务(`web-gin-service/`) ``` web-gin-service/ ├── go.mod ├── go.sum ├── main.go # 服务入口,初始化 Gin Router + gRPC dial ├── config/ │ ├── config.go # 配置加载(Viper / env) │ └── .env.example # 环境变量模板 ├── internal/ │ ├── handler/ # HTTP Handler(仅做参数解析 + gRPC调用转发) │ │ ├── auth_handler.go # POST /api/auth/login, /api/auth/register │ │ ├── job_handler.go # POST/GET/PUT/DELETE /api/jobs │ │ ├── application_handler.go │ │ ├── profile_handler.go │ │ ├── resume_handler.go # POST /api/resume/upload → 调gRPC取签名URL │ │ └── chat_handler.go # POST /api/chat/messages │ │ │ ├── middleware/ # 中间件 │ │ ├── cors.go # 全局跨域处理 │ │ ├── jwt_auth.go # JWT 统一鉴权(角色校验) │ │ └── validate.go # 请求参数合法性校验 │ │ │ ├── transport/ # gRPC 客户端(连接 logic-grpc-service) │ │ ├── grpc_client.go # 统一 gRPC client 池 │ │ └── codec.go # proto message ↔ HTTP JSON 转换 │ │ │ └── router/ # 路由注册 │ └── router.go # gin.Engine 路由绑定 ├── proto/ # 引用的 proto 文件只读拷贝 │ └── gen/ │ └── v1/ └── README.md # Web服务自述 ``` ### Web端 API 路由清单 | 方法 | 路径 | 说明 | 鉴权 | |------|------|------|------| | POST | `/api/auth/login` | 登录,返回 JWT | 无 | | POST | `/api/auth/register` | 注册 | 无 | | GET | `/api/jobs` | 岗位列表(分页) | 无 | | GET | `/api/jobs/:id` | 岗位详情 | 无 | | POST | `/api/jobs` | 发布岗位 | HR | | PUT | `/api/jobs/:id` | 编辑岗位 | HR(本人) | | DELETE | `/api/jobs/:id` | 下架岗位 | HR(本人) | | GET | `/api/jobs/:id/applications` | 岗位下候选人列表 | HR | | GET | `/api/profile/:userId` | 查看候选人档案 | HR | | POST | `/api/resume/upload` | 获取OSS签名URL | 候选人 | | POST | `/api/profile` | 更新个人档案 | 候选人 | | POST | `/api/applications` | 投递简历 | 候选人 | | POST | `/api/chat/messages` | AI对话发送 | HR | | GET | `/api/chat/history` | 加载对话历史 | HR | --- ## 五、Logic 核心业务服务(`logic-grpc-service/`) ``` logic-grpc-service/ ├── go.mod ├── go.sum ├── main.go # 服务入口,注册 gRPC Server ├── config/ │ ├── config.go │ └── .env.example ├── internal/ │ ├── server/ # gRPC Server 实现 │ │ ├── auth_service.go # 用户注册/登录/鉴权 │ │ ├── job_service.go # 岗位 CRUD(含创建者权限校验) │ │ ├── application_service.go # 投递逻辑 │ │ ├── profile_service.go # 候选人档案增改查 │ │ ├── resume_service.go # OSS签名URL生成 │ │ └── chat_service.go # AI对话 + 历史持久化 │ │ │ ├── model/ # 数据模型(ORM struct) │ │ ├── user.go │ │ ├── job.go │ │ ├── application.go │ │ ├── profile.go │ │ └── chat_record.go │ │ │ ├── repository/ # 数据访问层(MySQL操作) │ │ ├── user_repo.go │ │ ├── job_repo.go │ │ ├── application_repo.go │ │ ├── profile_repo.go │ │ ├── resume_repo.go │ │ └── chat_record_repo.go │ │ │ ├── oss/ # OSS客户端封装 │ │ ├── client.go # MinIO / 阿里云 OSS SDK 初始化 │ │ └── signer.go # 签名URL生成 + 文件头白名单校验 │ │ │ ├── ai/ # Eino AI 封装 │ │ ├── engine.go # Eino Chat 引擎初始化 │ │ ├── prompt.go # 提示词模板 + 业务上下文拼接 │ │ └── query_parser.go # 自然语言意图解析 → SQL查询条件 │ │ │ ├── jwt/ # JWT 工具 │ │ └── jwt.go # 签发 + 验证 + 角色提取 │ │ │ └── converter/ # DTO 转换 │ └── proto_converter.go # proto message ↔ model struct ├── proto/ │ └── gen/ │ └── v1/ └── README.md ``` --- ## 六、Protobuf 定义(`proto/v1/`) 所有 proto 文件统一放在 `proto/v1/`,供 `web-gin-service` 和 `logic-grpc-service` 共同引用。 ``` proto/v1/ ├── common.proto # 公共定义(Response code/msg, Pagination) ├── auth.proto # AuthService: Login, Register, VerifyToken ├── job.proto # JobService: CreateJob, UpdateJob, ListJobs, DeleteJob ├── application.proto # ApplicationService: SubmitApplication, ListApplications ├── profile.proto # ProfileService: GetProfile, UpdateProfile ├── resume.proto # ResumeService: GenOssSign, GetResumeInfo └── chat.proto # ChatService: SendChat, GetHistory ``` --- ## 七、数据流总览 ### 7.1 核心架构调用链 ```mermaid flowchart LR subgraph FE ["前端层(React + Vite)"] HR["HR管理端
hr-frontend"] User["候选人用户端
user-frontend"] end subgraph GW ["Web网关层(Gin)
web-gin-service"] RH["Auth Handler"] JH["Job Handler"] AH["Application Handler"] PH["Profile Handler"] RRH["Resume Handler"] CH["Chat Handler"] JWT["JWT 鉴权中间件"] CORS["CORS 中间件"] end subgraph RPC ["Logic业务层(gRPC)
logic-grpc-service"] US["AuthService"] JS["JobService"] AS["ApplicationService"] PS["ProfileService"] RSS["ResumeService"] CS["ChatService"] JWT_L["JWT 验证"] DB["MySQL Repository"] OSS_S["OSS Signer"] AI["Eino Chat Engine"] end subgraph Storage ["存储 & 外部服务"] M[(MySQL)] O[私有 OSS Bucket] LLM[大语言模型] end %% HR 请求链路 HR -->|"HTTP /api/*"| CORS CORS --> JWT JWT --> RH JWT --> JH JWT --> CH %% 候选人 请求链路 User -->|"HTTP /api/*"| CORS %% gRPC 跨服务调用 RH -->|"gRPC Login/RPC"| US JH -->|"gRPC Job CRUD"| JS CH -->|"gRPC SendChat/History"| CS RRH -->|"gRPC GenOssSign"| RSS %% Handler 转发候选人请求 JWT --> RRH JWT --> PH JWT --> AH PH -->|"gRPC Get/UpdateProfile"| PS AH -->|"gRPC SubmitApplication"| AS %% Backend → Storage US --> M JS --> M AS --> M PS --> M DB --> M CS --> DB RSS --> O CS --> AI AI --> LLM classDef frontNode fill:#e3f2fd,stroke:#1565c0; classDef gwNode fill:#fff3e0,stroke:#e65100; classDef logicNode fill:#e8f5e9,stroke:#2e7d32; classDef storeNode fill:#fce4ec,stroke:#c62828; class HR,User frontNode; class CORS,RH,JH,AH,PH,RRH,CH gwNode; class US,JS,AS,PS,RSS,CS,JWT_L,DB,OSS_S,AI logicNode; class M,O,LLM storeNode; ``` ### 7.2 简历直传流程(签名 URL 方案) ```mermaid sequenceDiagram participant C as CandidateFrontend participant W as Web-Gin participant R as ResumeService (gRPC) participant S as Storage (Private OSS) Note over C,S: 第一阶段:获取上传凭证 C->>W: POST /api/resume/upload
(PDF/DOC/DOCX 文件元信息) W->>W: JWT校验 + 后缀白名单校验 W->>R: gRPC GenOssSign(key, extension) R->>R: 校验文件头 Magic Number R-->>W: {UploadURL, DownloadURL, Expiry} W-->>C: 返回签名URL信息 Note over C,S: 第二阶段:客户端直传 OSS(服务端零缓存) C->>S: PUT 直接上传到 OSS Bucket S-->>C: 200 OK,上传完成 Note over C,W: 第三阶段:记录简历链接到数据库 C->>W: POST /api/profile/save-resume
(fileKey) W->>R: gRPC SaveResume(fileKey) R->>R: 持久化 fileKey 到 MySQL R-->>W: 操作成功 W-->>C: 保存成功 ``` ### 7.3 AI 对话数据流 ```mermaid sequenceDiagram participant H as HR Frontend participant W as Web-Gin participant C as ChatService (gRPC) participant Q as Query Parser participant M as MySQL participant P as Prompt Builder participant E as Eino Chat participant L as Large Language Model participant DB as ChatRecordRepo Note over H,M: Step 1 — 发起提问 H->>W: POST /api/chat/messages {question} W->>C: gRPC SendChat(hrId, question) Note over C,Q: Step 2 — 意图解析 + 数据查询 C->>Q: 解析自然语言意图 Q->>M: 执行业务SQL查询 M-->>Q: 真实统计数据 Note over C,P: Step 3 — 上下文拼接 + 推送模型 Q-->>C: 结构化数据 C->>P: 拼接 Prompt = 模板 + 业务数据 + 原始问题 P-->>C: 完整Prompt C->>E: Eino.Chat.Send(context, messages) E->>L: HTTP 请求至大模型 L-->>E: 生成回答 E-->>C: 自然语言回答 Note over C,DB: Step 4 — 持久化 + 返回 C->>DB: INSERT chat_record(hr_id, question, reply) DB-->>C: 写入成功 C-->>W: gRPC Response {reply, records[]} W-->>H: 渲染AI回复 + 历史消息列表 ``` ### 7.4 模块依赖关系图 ```mermaid graph TB subgraph HRFE ["hr-frontend (React + Vite)"] Routes["路由 /hr/*"] AuthCtx["AuthContext (JWT)"] ChatCtx["ChatContext (对话状态)"] API["Axios API 层"] end subgraph UFE ["user-frontend (React + Vite)"] URoutes["路由 /user/*"] UAuthCtx["AuthContext (JWT)"] UAPI["Axios API 层"] end subgraph GWI ["web-gin-service (Go)"] Router["Router"] Middleware["Middleware: CORS + JWT + Validate"] Handlers["Handlers: auth / job / resume / chat ..."] GrpcClient["gRPC Client → Logic"] end subgraph LO ["logic-grpc-service (Go)"] GrpcServer["gRPC Server"] Services["Services: auth / job / application / profile / resume / chat"] Repos["Repositories → MySQL"] OSSMod["OSS Client → Private OSS"] AIMod["Eino Engine → LLM"] end subgraph DATA ["数据 & 存储"] MySQL[(MySQL)] OSS["私有 OSS"] LLM["大语言模型 API"] end HRFE -->|HTTP JSON| GWI UFE -->|HTTP JSON| GWI GWI -->|gRPC| LO LO --> MySQL LO --> OSS LO --> LLM classDef fe fill:#e3f2fd,stroke:#1565c0; classDef gw fill:#fff3e0,stroke:#e65100; classDef logic fill:#e8f5e9,stroke:#2e7d32; classDef data fill:#fce4ec,stroke:#c62828; class HRFE,UFE fe; class GWI gw; class LO logic; class DATA data; ``` --- ## 八、开发建议 1. **前后端各自独立运行**:每个 `frontend/` 用 `npm run dev` 启动 Vite 开发服务器,通过 `vite.config.ts` 中的 `server.proxy` 将 `/api/*` 代理到 `localhost:8080`(Web-Gin)。 2. **Proto 先行**:先写好 `proto/v1/` 下的所有 `.proto` 文件,再各自 `protoc` 生成代码,避免来回改接口。 3. **前端共享模式**:HR端和用户端都遵循「API层 → Store层 → 页面层」三层分离,可抽取公共的 Axios client 和类型定义作为参考。 4. **环境变量隔离**:两个前端的 `.env`、后端的 `.env` 全部加 `.gitignore`,使用 `.env.example` 作为模板提交。