# ai_survey **Repository Path**: chen613528/ai_survey ## Basic Information - **Project Name**: ai_survey - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-06 - **Last Updated**: 2026-08-08 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # AI 智能问卷生成器 基于一句话需求 → AI 自动生成带逻辑跳转的结构化问卷平台,支持 PC 管理后台、H5、小程序三种访问方式。采用 Monorepo 工程结构,NestJS 后端 + Prisma ORM + MySQL,uni-app (Vue3 + Vant) 用户端,Vue3 + ElementPlus 管理后台。SSE 流式调用 OpenAI 兼容后端 API 计算,逻辑跳转全后端实现,前后端共享 TS 类型契约。 ## 目录结构 ``` ai_survey/ ├── apps/ # 前端应用 │ ├── admin-app/ # PC 管理后台 (Vue3+ElementPlus+ECharts) │ │ ├── src/ │ │ │ ├── api/ # Axios 封装 + 接口声明 │ │ │ ├── components/ # 公共组件 │ │ │ ├── layout/ # 主布局 (侧栏+顶栏+面包屑) │ │ │ ├── router/ # 路由守卫 + 权限校验 │ │ │ ├── stores/ # Pinia 状态 (user) │ │ │ ├── views/ │ │ │ │ ├── login/ # 登录页 │ │ │ │ ├── dashboard/ # 数据看板 │ │ │ │ ├── questionnaire/ # 问卷列表 + 编辑 │ │ │ │ ├── ai-settings/ # AI Key 配置 │ │ │ │ ├── system/ # 系统管理 (Admin) │ │ │ │ ├── unauthorized/ # 403 页 │ │ │ │ └── Placeholder.vue │ │ │ ├── App.vue │ │ │ ├── main.ts # 入口 (ElementPlus + Pinia) │ │ │ └── styles/main.css │ │ ├── index.html │ │ ├── package.json │ │ ├── tsconfig.json │ │ └── vite.config.ts │ │ │ ├── user-app/ # 用户端 (uni-app+Vant4) │ │ ├── src/ │ │ │ ├── api/ │ │ │ │ └── http.ts # uni.request 封装 │ │ │ ├── components/ │ │ │ │ ├── QuestionCard.vue # 单选/多选/评分/NPS/文本 │ │ │ │ ├── ProgressBar.vue # 进度条 │ │ │ │ └── TypeWriter.vue # 逐字动画 │ │ │ ├── composables/ │ │ │ │ ├── useLLMStream.ts # SSE 流式消费 │ │ │ │ └── useQuestionJump.ts # 跳转引擎 │ │ │ ├── components/GlobalData.vue │ │ │ ├── pages/ │ │ │ │ ├── index/index.vue # 首页 (需求输入) │ │ │ │ ├── generator/index.vue # AI 生成预览 │ │ │ │ ├── questionnaire/filling.vue# 填答跳转 │ │ │ │ └── user/profile.vue # 个人中心 │ │ │ ├── stores/user.ts # Pinia token/user state │ │ │ ├── App.vue │ │ │ ├── main.ts # uni-app 入口 │ │ │ ├── manifest.json # 小程序 manifest │ │ │ ├── pages.json # 页面路由 + tabBar │ │ │ └── uni.scss # uni-app 主题 │ │ ├── package.json │ │ └── vite.config.ts │ │ │ └── shared/ # 跨端 TS 类型包 │ └── src/ │ ├── types/ # User / Questionnaire / LogicRule / ApiResponse │ ├── schemas/ # Zod 校验 schema (QuestionnaireConfig) │ ├── constants/index.ts # 题目类型 / 状态枚举 │ ├── index.ts # 统一导出 │ └── utils/ # 纯函数工具 │ ├── server/ # NestJS 后端 │ ├── prisma/ │ │ ├── schema.prisma # 7 张表 (User/Team/TeamMember/Questionnaire/QuestionnaireResponse/Generation/AIApiCallLog) │ │ └── seed.ts # 开发种子数据 │ │ │ ├── src/ │ │ ├── main.ts # 全局入口 (helmet + ValidationPipe + Swagger + prefix=api) │ │ ├── app.module.ts # 顶层模块注册 │ │ ├── prisma/ │ │ │ ├── prisma.module.ts # 全局 PrismaModule │ │ │ └── prisma.service.ts # PrismaClient 单例 │ │ │ │ │ ├── common/ │ │ │ ├── exception.ts # DomainException 基类 │ │ │ ├── guards/ │ │ │ │ └── admin.guard.ts # SUPER_ADMIN 校验 │ │ │ ├── middleware/ │ │ │ │ ├── prisma-tenant.middleware.ts # 自动注入 ownerId │ │ │ │ └── tenant-context.ts │ │ │ ├── decorators/ # 自定义装饰器目录 │ │ │ ├── filters/ │ │ │ │ └── http-exception.filter.ts # 统一错误 JSON 响应 │ │ │ ├── pipes/ # 自定义管道目录 │ │ │ └── interceptors/ │ │ │ └── logging.interceptor.ts # HTTP 请求日志 │ │ │ │ │ └── modules/ # 业务模块 │ │ ├── auth/ # 鉴权模块 (JWT+Refresh) │ │ │ ├── auth.controller.ts # POST register/login, GET me │ │ │ ├── auth.service.ts │ │ │ ├── auth.module.ts │ │ │ ├── guards/jwt-auth.guard.ts │ │ │ ├── decorators/current-user.decorator.ts │ │ │ └── dto/ │ │ │ ├── login.dto.ts │ │ │ └── register.dto.ts │ │ │ │ │ ├── user/ # 用户模块 (AI Key 加密 + 配额) │ │ │ ├── user.controller.ts # GET /me, PUT /ai-key, GET /quota │ │ │ ├── user.service.ts │ │ │ ├── user.module.ts │ │ │ ├── ai-crypto.util.ts # AES-256-GCM 加密 │ │ │ ├── dto/update-ai-key.dto.ts │ │ │ └── tests/ai-crypto.spec.ts │ │ │ │ │ ├── ai/ # AI 生成模块 (SSE) │ │ │ ├── ai.controller.ts # @Sse POST /generate │ │ │ ├── ai.service.ts │ │ │ ├── ai.module.ts │ │ │ ├── llm-gateway.service.ts # OpenAI 兼容流式 │ │ │ ├── prompts/questionnaire-v1.prompt.ts │ │ │ └── tests/ │ │ │ │ │ ├── questionnaire/ # 问卷模块 (CRUD + 发布) │ │ │ ├── questionnaire.controller.ts # POST/PUT/GET/PATCH/DELETE │ │ │ ├── questionnaire.service.ts │ │ │ ├── questionnaire.module.ts │ │ │ ├── questionnaire-config.schema.ts # Zod 校验 │ │ │ ├── dto/ │ │ │ │ ├── create-questionnaire.dto.ts │ │ │ │ └── update-questionnaire.dto.ts │ │ │ └── tests/ │ │ │ │ │ ├── response/ # 填答与跳转引擎 │ │ │ ├── response.controller.ts # POST submit/submit-complete │ │ │ ├── response.service.ts │ │ │ ├── response.module.ts │ │ │ ├── jump-engine.util.ts # 单命中跳转逻辑 │ │ │ ├── dto/ │ │ │ │ ├── submit-answer.dto.ts │ │ │ │ └── submit-complete.dto.ts │ │ │ └── tests/ │ │ │ │ │ ├── analytics/ # 统计 + 导出 │ │ │ ├── analytics.controller.ts # GET stats/export │ │ │ ├── analytics.service.ts │ │ │ ├── analytics.module.ts │ │ │ └── export-excel.util.ts │ │ │ │ │ └── tenant/ # 团队空间 (Phase 2 预留) │ │ ├── tenant.module.ts │ │ └── tenant.service.ts │ │ │ ├── .env.example # 完整环境变量清单 │ ├── jest.config.js │ ├── nest-cli.json │ ├── package.json │ └── tsconfig.json │ ├── deploy/ # 部署脚本 │ ├── Dockerfile # 多阶段构建 (builder + production + PM2) │ ├── pm2.config.js # PM2 四进程集群配置 │ ├── nginx.conf # 反向代理 (SSL + SPA + 限流 + gzip) │ ├── docker-compose.yml # 全栈编排 (server + MySQL + MinIO) │ └── setup.sh # 一键服务器部署脚本 │ ├── .github/workflows/ # CI/CD │ ├── ci.yml # PR 触发 lint + build + test │ └── deploy.yml # push main 构建 + SSH 部署 + PM2 reload │ ├── docs/ │ ├── architecture.md # 整体架构参考文档 │ ├── dev-guide.md # 开发者指南 │ └── superpowers/plans/ # 实施计划与设计文档 │ ├── package.json # 根 workspace 配置 ├── pnpm-workspace.yaml # pnpm 工作区声明 ├── tsconfig.base.json # 共享 TS 配置 ├── .gitignore ├── .editorconfig └── README.md # 本文件 ``` ## 快速启动 | 步骤 | 命令 | 说明 | |------|------|------| | 安装依赖 | `pnpm install` | Monorepo 全量安装 (Node>=20, pnpm>=9) | | 配置环境变量 | `cp server/.env.example server/.env` | 编辑 DATABASE_URL / JWT_SECRET / ENCRYPTION_KEY | | 数据库迁移 | `cd server && npx prisma migrate dev` | 生成 PrismaClient + 建表 | | 种子数据 | `cd server && npx prisma db seed` | 插入开发用 admin/user 账号 | | 本地开发 | `pnpm dev` | 并行启动 admin/user/server | | 构建产物 | `pnpm build` | 编译 admin + user + server | | 单元测试 | `pnpm test` | 后端 jest 覆盖率 >70% | | 代码检查 | `pnpm lint` | ESLint 全仓检查 | ## 开发服务器端口 | 服务 | 地址 | 备注 | |------|------|------| | 后端 API | `http://localhost:3000` | Swagger 文档 `/api/docs` | | PC 管理后台 | `http://localhost:5174` | Vue3+EP, 需要 SUPER_ADMIN | | 用户端 H5 | `http://localhost:5173` | uni-app, Vite dev | | MinIO Console | `http://localhost:9001` | 对象存储 S3 兼容 | ## 如何新建一个 Module 参照现有 Auth Module 模板,三步走: ### 第一步:创建 Module 骨架 ``` server/src/modules/my-feature/ ├── my-feature.controller.ts # 路由 + DTO 接收 + 鉴权 guard ├── my-feature.service.ts # 业务逻辑 ├── my-feature.module.ts # 模块声明 ├── dto/ │ ├── create.dto.ts # class-validator 校验 │ └── update.dto.ts └── tests/ └── my-feature.service.spec.ts # 单元测试 ``` ### 第二步:Module 声明示例 ```typescript // my-feature.module.ts import { Module } from '@nestjs/common'; import { MyFeatureController } from './my-feature.controller'; import { MyFeatureService } from './my-feature.service'; @Module({ controllers: [MyFeatureController], providers: [MyFeatureService], exports: [MyFeatureService], }) export class MyFeatureModule {} ``` ### 第三步:注册到 AppModule ```typescript // app.module.ts imports: 中加入 MyFeatureModule ``` ### 关键约束 1. 所有端点必须使用 `@UseGuards(JwtAuthGuard)` (admin 接口额外用 `AdminGuard`) 2. Service 方法从 `@CurrentUser()` 取 `user.id` 做租户隔离 3. 写 Prisma 操作时强制带 `ownerId` 过滤;查询同理使用 `prisma-tenant.middleware` 4. DTO 用 `class-validator` 必填项装饰器 5. 新增错误枚举在 `common/exception.ts` 拓展 `DomainException` ## 贡献指南 ### 编码规范 - **文件命名**:`kebab-case.ts` (multi-word),Vue 组件 `PascalCase.vue` - **No any**:TypeScript strict mode 下不允许 any,仅 Prisma 生成类型例外 - **导入顺序**:nest/angular 内置 → 第三方 → 本地 (`@shared` 别名) - **提交前检查**:`pnpm lint && pnpm build && pnpm test` ### 提交规范 (commitlint + husky) | 类型 | 作用 | |------|------| | `feat` | 新功能模块 (如 `feat(auth): ...`) | | `fix` | Bug 修复 | | `docs` | 文档/注释变更 | | `refactor` | 重构 (不影响行为) | | `test` | 测试用例补充 | | `ci` | CI/CD 脚本 | `pre-commit` 钩子执行 `lint-staged` (仅检查暂存文件),`commit-msg` 钩子校验 commit message 格式。 ### 分支策略 - `main`:生产部署分支,受保护 - `develop`:集成测试分支 - `feature/*`:功能开发分支(PR → develop → main) - `hotfix/*`:紧急修复分支(PR → main) ## 部署到生产 参照 `.github/workflows/deploy.yml` 的自动化流程,手动部署步骤如下: 1. **SSH 登录服务器** → 拉最新代码 2. **构建产物**: ```bash pnpm install --frozen-lockfile pnpm build ``` 3. **数据库迁移**: ```bash cd server && npx prisma migrate deploy ``` 4. **同步前端静态资源**: ```bash rsync -az apps/admin-app/dist/ /var/www/admin-app/dist/ rsync -az apps/user-app/dist/h5/ /var/www/user-app/dist/h5/ ``` 5. **PM2 reload (零停机)**: ```bash cd /opt/ai-questionnaire && pm2 reload pm2.config.js --env production ``` 6. **健康检查**:`curl http://127.0.0.1:3000/api/health` ### Docker 部署(推荐) ```bash docker compose -f deploy/docker-compose.yml up -d --build ``` ### 一键新服部署 ```bash chmod +x deploy/setup.sh sudo ./deploy/setup.sh # 自动安装 Docker / Let's Encrypt / Nginx ``` ## 环境变量完整表 | 变量 | 默认值 | 说明 | |------|--------|------| | `DATABASE_URL` | `mysql://root:password@localhost:3306/ai_questionnaire` | Prisma 连接串 | | `JWT_SECRET` | `change-this-in-production-use-random-64-char-string` | Access Token 签发密钥 | | `JWT_ACCESS_TTL` | `3600` | Access Token 有效期 (秒) | | `JWT_REFRESH_TTL` | `604800` | Refresh Token 有效期 (秒, 默认 7 天) | | `ENCRYPTION_KEY` | `0123456...` (32 字节 hex) | AES-256-GCM 主密钥,生产必须更换 | | `PORT` | `3000` | NestJS 监听端口 | | `NODE_ENV` | `development` | `development` / `production` / `test` | | `DOMAIN` | `http://localhost:3000` | 绝对 URL 生成 (分享链接等) | | `CORS_ORIGINS` | `http://localhost:5173,http://localhost:5174` | 跨域白名单 (逗号分隔) | | `THROTTLE_TTL` | `60` | 限流时间窗口 (秒) | | `THROTTLE_LIMIT` | `60` | 单 IP 每窗口最大请求数 | | `S3_ENDPOINT` | `http://localhost:9000` | S3 兼容对象存储地址 | | `S3_REGION` | `us-east-1` | S3 Region | | `S3_BUCKET` | `ai-questionnaire` | 存储桶名 | | `S3_ACCESS_KEY` | `minioadmin` | S3 Access Key | | `S3_SECRET_KEY` | `minioadmin` | S3 Secret Key | ## 技术栈版本表 | 层级 | 技术 | 版本 | |------|------|------| | 运行时 | Node.js | >= 20.10.0 | | 包管理 | pnpm | >= 9.0.0 | | 后端框架 | NestJS | 10 | | ORM | Prisma | 5.x | | 数据库 | MySQL | 8.0 | | 后端校验 | class-validator + class-transformer | latest | | 后端文档 | Swagger (OpenAPI 3) | via @nestjs/swagger | | 用户端框架 | uni-app (Vue3 + TS) | latest | | 用户端 UI | Vant | 4.x | | 管理后台框架 | Vue | 3.4+ | | 管理后台 UI | Element Plus | 2.7+ | | 图表 | ECharts | 5.x | | 状态管理 | Pinia | 2.x | | 构建工具 | Vite | 5.x | | 请求库 | axios (admin) + uni.request (user) | latest | | 进程管理 | PM2 | latest | | 反向代理 | Nginx | latest | | CI/CD | GitHub Actions | — | | 对象存储 | MinIO (dev) / OSS (prod) | S3 API | ## API 端点快速索引 ### 认证模块 (AuthModule) — `/api/auth` | 方法 | 路径 | 鉴权 | 说明 | |------|------|------|------| | POST | `/api/auth/register` | — | 注册返回 `{ accessToken, refreshToken }` | | POST | `/api/auth/login` | — | 登录返回 Token pair | | GET | `/api/auth/me` | JWT | 当前用户信息 | ### 用户模块 (UserModule) — `/api/users` | 方法 | 路径 | 鉴权 | 说明 | |------|------|------|------| | GET | `/api/users/me` | JWT | 用户信息 (不含 aiApiKeyEnc) | | PUT | `/api/users/ai-key` | JWT | 更新加密 AI Key | | GET | `/api/users/quota` | JWT | 剩余生成配额 | ### AI 生成模块 (AIModule) — `/api/ai` | 方法 | 路径 | 鉴权 | 说明 | |------|------|------|------| | SSE | `/api/ai/generate` | JWT | 流式生成问卷 (返回 `question/jump/done/error` 事件) | ### 问卷模块 (QuestionnaireModule) — `/api/questionnaires` | 方法 | 路径 | 鉴权 | 说明 | |------|------|------|------| | POST | `/api/questionnaires/` | JWT | 新建问卷 (draft) | | GET | `/api/questionnaires/` | JWT | 当前用户问卷列表 | | GET | `/api/questionnaires/:id` | JWT | 单份问卷详情 | | PUT | `/api/questionnaires/:id` | JWT | 更新问卷字段 | | PATCH| `/api/questionnaires/:id/publish` | JWT | 发布问卷 (校验 config schema) | | PATCH| `/api/questionnaires/:id/close` | JWT | 关闭回收 | | PATCH| `/api/questionnaires/:id/archive` | JWT | 归档 | | DELETE | `/api/questionnaires/:id` | JWT | 删除 | ### 填答模块 (ResponseModule) — `/api/questionnaires/:id/responses` | 方法 | 路径 | 鉴权 | 说明 | |------|------|------|------| | POST | `/api/questionnaires/:id/responses` | — | 提交单题答案 (`{ qId, answer }`) → 返回 `nextQId/isEnd` | | POST | `/api/questionnaires/:id/responses/complete` | — | 完成填答 (标记 `isComplete=true`) | ### 统计模块 (AnalyticsModule) — `/api/questionnaires/:id` | 方法 | 路径 | 鉴权 | 说明 | |------|------|------|------| | GET | `/api/questionnaires/:id/stats` | JWT | 回收统计 (`total/daily[]/pie[]/funnel/avgDurationMs`) | | GET | `/api/questionnaires/:id/export?format=xlsx` | JWT | 导出 Excel (Pro 用户) | ## 文档 - [开发者指南](docs/dev-guide.md) — 环境准备、module 开发流程、调试命令 - [架构参考](docs/architecture.md) — 部署架构图、模块关系、业务流程、安全设计 ## License TBD (暂定)