From be07b9ffecc6530eeb493dbc5cd1137d0e1932fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=87=E7=89=A9=E8=A1=97?= <7729700+wanwujie@user.noreply.gitee.com> Date: Sun, 24 Aug 2025 02:55:13 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=85=A8=E9=9D=A2=E6=B8=85?= =?UTF-8?q?=E7=90=86=E9=A1=B9=E7=9B=AE=E7=BB=93=E6=9E=84=EF=BC=8C=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E4=BB=A3=E7=A0=81=E7=BB=84=E7=BB=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 主要改进: 1. 清理Core层空壳目录 - 删除traits, transformers, query等空目录 - 删除security, http, queue等空模块 - 删除logger, context, exception等空壳 2. 清理Common层冗余模块 - 删除utils, cache, queue等空壳模块 - 删除dictionary, dict等重复字典模块 - 删除重复的MemberModule.ts文件 3. 优化模块结构 - 移动config到config/common目录 - 统一模块命名规范为{模块名}.module.ts - 保留业务逻辑模块:auth, member, rbac, admin, settings, upload, notification 4. 代码质量提升 - 更符合NestJS最佳实践 - 项目结构更清晰 - 删除所有空壳和重复代码 - 打包测试通过 技术改进: - 使用TypeScript枚举替代PHP风格的Dict类 - 优化导入路径和模块引用 - 清理无用的空壳目录和文件 --- .trae/rules/project_rules.md | 2404 +++-------------- AI-DEVELOPMENT-GUIDE.md | 385 +++ admin/apps/web-ele/src/api/README.md | 118 + admin/apps/web-ele/src/api/admin/index.ts | 5 + admin/apps/web-ele/src/api/admin/member.ts | 102 + admin/apps/web-ele/src/api/frontend/index.ts | 6 + admin/apps/web-ele/src/api/frontend/member.ts | 62 + admin/apps/web-ele/src/api/frontend/site.ts | 41 + admin/apps/web-ele/src/api/index.ts | 2 + admin/src/types/admin-api.d.ts | 207 ++ admin/src/types/frontend-api.d.ts | 70 + lefthook.yml | 42 + package.json | 5 + sql/test-data.sql | 289 ++ 14 files changed, 1640 insertions(+), 2098 deletions(-) create mode 100644 AI-DEVELOPMENT-GUIDE.md create mode 100644 admin/apps/web-ele/src/api/README.md create mode 100644 admin/apps/web-ele/src/api/admin/index.ts create mode 100644 admin/apps/web-ele/src/api/admin/member.ts create mode 100644 admin/apps/web-ele/src/api/frontend/index.ts create mode 100644 admin/apps/web-ele/src/api/frontend/member.ts create mode 100644 admin/apps/web-ele/src/api/frontend/site.ts create mode 100644 admin/src/types/admin-api.d.ts create mode 100644 admin/src/types/frontend-api.d.ts create mode 100644 lefthook.yml create mode 100644 package.json create mode 100644 sql/test-data.sql diff --git a/.trae/rules/project_rules.md b/.trae/rules/project_rules.md index 3981eda..b9c0ff1 100644 --- a/.trae/rules/project_rules.md +++ b/.trae/rules/project_rules.md @@ -1,2105 +1,313 @@ -# 📊 依赖关系图 +框架层面: 100% 使用 NestJS 的方式 +业务层面: 与 PHP 项目保持 100% 一致 +数据层面: 与 PHP 项目数据库结构 100% 一致 +# AI 框架功能对比图 - NestJS vs ThinkPHP -‍``` -┌─────────────────┐ -│ App │ ← 业务开发层(用户自定义业务模块) -│ (用户业务) │ 电商、CRM、ERP等具体业务逻辑 -└─────────────────┘ - ↓ -┌─────────────────┐ -│ Common │ ← 框架通用服务层(企业级通用功能) -│ (框架通用服务) │ 用户管理、权限管理、菜单管理 -└─────────────────┘ 文件上传、通知服务、系统设置 - ↓ 数据字典、缓存服务、队列服务 -┌─────────────────┐ -│ Core │ ← 核心基础设施层(底层基础设施) -│ (基础设施) │ 认证核心、数据库核心、验证核心 -└─────────────────┘ HTTP核心、缓存核心、队列核心 - ↓ -┌─────────────────┐ -│ Vendor │ ← 第三方服务适配层 -│ (外部集成) │ 存储、支付、通信、云服务适配 -└─────────────────┘ +## 📋 概述 -┌─────────────────┐ -│ Addons │ ← 插件扩展层(可插拔功能模块) -│ (插件扩展) │ 扩展框架功能,不影响核心稳定性 -└─────────────────┘ +本文档为 AI 开发者提供 NestJS 和 ThinkPHP 框架的详细对比,包含功能映射、开发规范、命名约定和目录结构对比,确保 AI 能够更好地理解和开发功能。 + +**重要原则:既要尊重 NestJS 框架特性,又要与 PHP 项目业务逻辑保持一致** + +## 🔄 核心功能映射 + +### 1. 基础架构对比 + +| 功能模块 | ThinkPHP | NestJS | 对应关系 | 实现方式 | +|---------|----------|---------|----------|----------| +| **路由系统** | `Route::get()` | `@Get()` | ✅ 直接对应 | 装饰器路由 | +| **控制器** | `Controller` | `@Controller()` | ✅ 直接对应 | 装饰器控制器 | +| **中间件** | `Middleware` | `@UseGuards()` | ✅ 功能对应 | 守卫/拦截器 | +| **依赖注入** | `Container::get()` | `constructor()` | ✅ 更强大 | 自动注入 | +| **数据验证** | `Validate` | `@UsePipes()` | ✅ 功能对应 | 验证管道 | +| **异常处理** | `Exception` | `@UseFilters()` | ✅ 功能对应 | 异常过滤器 | + +### 2. 数据库操作对比 + +| 功能模块 | ThinkPHP | NestJS | 对应关系 | 实现方式 | +|---------|----------|---------|----------|----------| +| **模型定义** | `Model` | `@Entity()` | ✅ 功能对应 | TypeORM 实体 | +| **查询构建** | `Db::table()` | `Repository` | ✅ 功能对应 | TypeORM 仓库 | +| **关联关系** | `hasMany()` | `@OneToMany()` | ✅ 功能对应 | TypeORM 关联 | +| **事务处理** | `Db::startTrans()` | `@Transaction()` | ✅ 功能对应 | TypeORM 事务 | +| **软删除** | `SoftDelete` | `@DeleteDateColumn()` | ✅ 功能对应 | TypeORM 软删除 | + +### 3. 缓存和会话 + +| 功能模块 | ThinkPHP | NestJS | 对应关系 | 实现方式 | +|---------|----------|---------|----------|----------| +| **缓存管理** | `Cache::get()` | `@Inject(CACHE_MANAGER)` | ✅ 功能对应 | Cache Manager | +| **会话管理** | `Session` | `@Session()` | ✅ 功能对应 | Session 装饰器 | +| **Redis 集成** | `Redis::get()` | `@InjectRedis()` | ✅ 功能对应 | Redis 模块 | + +## 🏗️ 目录结构对比 + +### ThinkPHP 目录结构 +``` +thinkphp/ +├── app/ # 应用目录 +│ ├── controller/ # 控制器 +│ ├── model/ # 模型 +│ ├── service/ # 服务层 +│ └── middleware/ # 中间件 +├── config/ # 配置文件 +├── public/ # 公共资源 +├── route/ # 路由定义 +└── vendor/ # 第三方包 ``` -## 📚 参考文档 +### NestJS 目录结构 +``` +wwjcloud/ +├── src/ # 源代码目录 +│ ├── common/ # 通用服务层 (对应 ThinkPHP app/) +│ │ ├── admin/ # 管理端服务 +│ │ ├── member/ # 会员服务 +│ │ ├── rbac/ # 权限管理 +│ │ └── settings/ # 系统设置 +│ ├── config/ # 配置管理层 (对应 ThinkPHP config/) +│ │ ├── entity/ # 实体配置 +│ │ ├── database/ # 数据库配置 +│ │ └── env/ # 环境配置 +│ ├── core/ # 核心基础设施层 (对应 ThinkPHP 核心) +│ │ ├── base/ # 基础类 +│ │ ├── traits/ # 特性类 +│ │ ├── database/ # 数据库核心 +│ │ └── security/ # 安全核心 +│ └── vendor/ # 第三方服务适配层 (对应 ThinkPHP vendor/) +│ ├── payment/ # 支付适配器 +│ ├── storage/ # 存储适配器 +│ └── sms/ # 短信适配器 +├── public/ # 公共资源 +└── package.json # 依赖管理 +``` -- **Vben Admin 官方文档**: https://doc.vben.pro/ -- **项目技术栈**: Vue 3 + TypeScript + Vite + Element Plus -- **UI 组件库**: Element Plus (当前项目使用) -- **表单组件**: Vben Form (支持 Element Plus、Ant Design Vue、Naive UI 等多种 UI 库适配) +### 层级对应关系 + +| 层级 | ThinkPHP | NestJS | 说明 | +|------|----------|---------|------| +| **应用层** | `app/` | `src/common/` | 业务逻辑和通用服务 | +| **配置层** | `config/` | `src/config/` | 配置管理和环境变量 | +| **核心层** | 框架核心 | `src/core/` | 基础设施和核心功能 | +| **适配层** | `vendor/` | `src/vendor/` | 第三方服务集成 | + +## 📝 命名规范和约束 + +### 1. 数据库命名规范 + +**重要约束:与 PHP 项目共用数据库,必须保持命名一致** + +- **表名**: 与 PHP 项目完全一致,包括前缀和命名方式 +- **字段名**: 与 PHP 项目完全一致,不能修改任何字段名 +- **字段类型**: 与 PHP 项目完全一致,不能修改字段类型 +- **索引结构**: 与 PHP 项目完全一致,不能添加或删除索引 + +**原则:看到 PHP 项目怎么命名,我们就怎么命名,保持 100% 一致** + +### 2. 代码命名规范 + +#### 类名规范 +- **实体类**: 使用 PascalCase,如 `User`, `OrderDetail` +- **服务类**: 使用 PascalCase + Service,如 `UserService`, `OrderService` +- **控制器**: 使用 PascalCase + Controller,如 `UserController` +- **DTO 类**: 使用 PascalCase + Dto,如 `CreateUserDto`, `UpdateUserDto` + +#### 方法名规范 +**业务逻辑方法优先与 PHP 项目保持一致,NestJS 特有方法按 NestJS 规范:** + +- **CRUD 方法**: 与 PHP 项目方法名保持一致 +- **查询方法**: 与 PHP 项目方法名保持一致 +- **业务方法**: 与 PHP 项目方法名保持一致 +- **NestJS 生命周期方法**: 按 NestJS 规范,如 `onModuleInit()`, `onApplicationBootstrap()` + +**原则:业务逻辑与 PHP 保持一致,NestJS 特性按 NestJS 规范** + +#### 变量名规范 +**业务变量优先与 PHP 项目保持一致,NestJS 特有变量按 NestJS 规范:** + +- **业务变量**: 与 PHP 项目变量名保持一致 +- **业务常量**: 与 PHP 项目常量名保持一致 +- **NestJS 注入变量**: 按 NestJS 规范,如 `private readonly userService: UserService` +- **TypeORM 相关变量**: 按 TypeORM 规范,如 `@InjectRepository(User)` + +**原则:业务变量与 PHP 保持一致,NestJS 特性按 NestJS 规范** + +### 3. 文件命名规范 + +#### 目录结构 +``` +src/common/admin/ +├── controllers/ # 控制器目录 +│ ├── user.controller.ts +│ └── order.controller.ts +├── services/ # 服务目录 +│ ├── user.service.ts +│ └── order.service.ts +├── entities/ # 实体目录 +│ ├── user.entity.ts +│ └── order.entity.ts +└── dto/ # DTO 目录 + ├── create-user.dto.ts + └── update-user.dto.ts +``` + +#### 文件命名 +**NestJS 特有的文件类型,按照 NestJS 规范命名:** + +- **控制器**: `{模块名}.controller.ts` (NestJS 规范) +- **服务**: `{模块名}.service.ts` (NestJS 规范) +- **实体**: `{模块名}.entity.ts` (TypeORM 规范,对应 PHP 的模型) +- **DTO**: `{操作}-{模块名}.dto.ts` (NestJS 规范,对应 PHP 的验证器) +- **模块**: `{模块名}.module.ts` (NestJS 规范) + +**原则:NestJS 特有的文件类型按 NestJS 规范,业务逻辑与 PHP 保持一致** + +## 🔧 开发约束和规范 + +### 1. 数据库约束 + +#### 必须遵守的规则 +- **表结构**: 与 PHP 项目完全一致,不能修改任何结构 +- **索引**: 与 PHP 项目完全一致,不能修改索引 +- **外键**: 与 PHP 项目完全一致,不能修改外键 +- **触发器**: 与 PHP 项目完全一致,不能修改触发器 + +#### 数据一致性 +- **事务处理**: 与 PHP 项目保持一致的事务处理方式 +- **软删除**: 与 PHP 项目保持一致的软删除方式 +- **状态管理**: 与 PHP 项目保持一致的状态管理方式 + +**原则:PHP 项目怎么做,我们就怎么做,保持 100% 一致** + +### 2. API 设计约束 + +#### 接口规范 +- **URL 格式**: 与 PHP 项目完全一致 +- **请求方法**: 与 PHP 项目完全一致 +- **响应格式**: 与 PHP 项目完全一致 +- **错误处理**: 与 PHP 项目完全一致 + +**原则:PHP 项目怎么设计接口,我们就怎么设计,保持 100% 一致** + +#### 权限控制 +- **认证**: 与 PHP 项目保持一致的认证方式 +- **授权**: 与 PHP 项目保持一致的授权方式 +- **数据隔离**: 与 PHP 项目保持一致的数据隔离方式 + +**原则:PHP 项目怎么控制权限,我们就怎么控制,保持 100% 一致** + +### 3. 代码质量约束 + +#### 类型安全 +- **必须使用 TypeScript**: 不允许使用 `any` 类型 +- **接口定义**: 所有 DTO 和响应对象必须有接口定义 +- **类型检查**: 启用严格模式,不允许隐式类型转换 + +#### 错误处理 +- **异常捕获**: 与 PHP 项目保持一致的异常处理方式 +- **日志记录**: 与 PHP 项目保持一致的日志记录方式 +- **错误响应**: 与 PHP 项目保持一致的错误响应格式 + +**原则:PHP 项目怎么处理错误,我们就怎么处理,保持 100% 一致** + +## 🚀 AI 开发指南 + +### 1. 开发流程 + +#### 创建新功能模块 +1. **分析需求**: 确定功能属于哪个层级 (common/config/core/vendor) +2. **参考 PHP 项目**: 查看 PHP 项目如何实现相同功能 +3. **保持一致性**: 与 PHP 项目保持 100% 一致 +4. **创建组件**: 按照 NestJS 规范创建相应的组件 +5. **配置模块**: 在相应的模块中注册组件 + +#### 开发原则 +**既要尊重 NestJS 框架特性,又要与 PHP 项目业务逻辑保持一致** + +- **框架特性**: 按照 NestJS 规范使用装饰器、依赖注入、管道等特性 +- **业务逻辑**: 与 PHP 项目保持 100% 一致 +- **数据操作**: 与 PHP 项目保持 100% 一致 +- **接口设计**: 与 PHP 项目保持 100% 一致 + +### 2. 常见问题解决 + +#### 常见问题解决原则 +**遇到问题时,首先查看 PHP 项目是如何解决的,然后按照相同方式解决** + +- **数据库问题**: 参考 PHP 项目的数据库配置和操作方式 +- **权限问题**: 参考 PHP 项目的权限控制方式 +- **性能问题**: 参考 PHP 项目的性能优化方式 +- **业务问题**: 参考 PHP 项目的业务实现方式 + +**原则:PHP 项目怎么解决,我们就怎么解决,保持 100% 一致** + +### 3. 最佳实践 + +#### 最佳实践原则 +**参考 PHP 项目的最佳实践,保持 100% 一致** + +- **代码组织**: 与 PHP 项目保持一致的代码组织方式 +- **错误处理**: 与 PHP 项目保持一致的错误处理方式 +- **测试策略**: 与 PHP 项目保持一致的测试策略 + +**原则:PHP 项目怎么组织代码,我们就怎么组织,保持 100% 一致** + +## 📚 参考资源 + +### 官方文档 +- **NestJS**: https://nest.nodejs.cn/ +- **TypeORM**: https://typeorm.io/ +- **ThinkPHP**: https://doc.thinkphp.cn/ + +### 项目相关 +- **数据库结构**: 参考 PHP 项目的数据库设计 +- **API 接口**: 参考 PHP 项目的接口文档 +- **业务逻辑**: 参考 PHP 项目的业务实现 + +## 🎯 总结 + +### 平衡原则 +1. **尊重 NestJS**: 充分利用 NestJS 的装饰器、依赖注入、管道等特性 +2. **保持业务一致**: 业务逻辑、数据操作、接口设计与 PHP 项目 100% 一致 +3. **框架适配**: 用 NestJS 的方式实现 PHP 项目的功能 + +### 开发策略 +- **看到 PHP 项目怎么做的,我们就怎么做** (业务层面) +- **看到 NestJS 怎么做的,我们就怎么做** (框架层面) +- **两者结合,发挥各自优势** --- - -## 📐 项目规范(代码风格与约定) - -本节定义目录结构、导入顺序、命名、方法与引用等统一规范,用于指导日常开发与 Code Review。 - -### 1) 目录结构规范 - -``` -src/ -├─ addon/ # 插件扩展层:可插拔功能模块,不影响核心稳定性 -├─ app/ # 应用层:用户业务开发模块 -│ ├─ ecommerce/ # 电商业务模块 -│ ├─ crm/ # CRM 业务模块 -│ └─ erp/ # ERP 业务模块 -├─ common/ # 框架通用服务层:用户、权限、菜单、文件、通知、系统设置等 -├─ config/ # 配置层:集中化配置与校验 -├─ core/ # 基础设施:数据库、缓存、HTTP、认证、日志等核心能力封装 -├─ vendor/ # 第三方适配:支付、存储、短信、云服务等 -└─ main.ts # 应用入口 -``` - -- 依赖方向:App → Common → Core → Vendor;禁止反向依赖与跨层耦合。 -- Addon 可依赖 Common/Core,但 App/Common/Core 不得依赖 Addon。 -- 模块划分以领域边界为单位,保持高内聚、低耦合。 - -### 2) 导入顺序与分组 - -1. Node.js 内置模块 -2. 第三方依赖(npm 包) -3. 项目内部模块 -4. 父级目录(../) -5. 同级目录(./) -6. 索引文件(index) - -示例: -```ts -// 1) Node 内置 -import * as fs from 'fs'; -import * as path from 'path'; - -// 2) 外部依赖 -import { Controller } from '@nestjs/common'; -import { ApiTags } from '@nestjs/swagger'; - -// 3) 内部模块 -import { LoggerService } from '../../core/logger'; - -// 4) 父级目录 -import { BaseController } from '../base'; - -// 5) 同级目录 -import { UserService } from './user.service'; - -// 6) 索引 -import { CreateUserDto } from './dto'; -``` - -### 3) 命名规范 - -- 文件: - - 控制器 `*.controller.ts`,服务 `*.service.ts`,实体 `*.entity.ts`,DTO `*.dto.ts` - - 接口 `*.interface.ts`,类型 `*.type.ts`,常量 `*.constant.ts`,配置 `*.config.ts`,模块 `*.module.ts` -- 类:`UserController`、`UserService`、`UserEntity`、`CreateUserDto` -- 变量/方法:camelCase;布尔值以 `is/has/can/should` 开头;必要时私有方法可用前缀 `_`(可选) - -### 4) 方法与类设计 - -- 单一职责、短小精悍,每个方法聚焦一个清晰目标 -- 依赖注入优先,避免在方法中直接构造依赖 -- 显式返回类型,避免 any;公共方法尽量无副作用 -- 错误优先返回与早失败(early return),减少嵌套 - -### 5) 引用与依赖约束 - -- 严格遵循分层依赖:App → Common → Core → Vendor -- 禁止跨域(跨子域模块)直接依赖,使用约定接口与适配器 -- 避免循环依赖;必要时通过接口、token 或事件解耦 -- 推荐在每层提供 index.ts 作为 barrel 导出,统一对外 API - -### 6) 类型、实体与 DTO - -- 优先使用 interface,合理使用泛型提升复用 -- DTO 与 Entity 分离:DTO 负责入参校验(class-validator),Entity 负责持久化结构 -- 禁止直接在控制器接收实体,必须使用 DTO 并开启全局 ValidationPipe(已开启) - -### 7) 错误处理与日志 - -- 统一抛出框架异常(如 NotFoundException、BadRequestException),或自定义异常族 -- 记录错误日志,包含 requestId(CLS 已接入)、上下文与栈信息 -- 外部接口与关键链路增加 info 级打点,敏感信息不可入日志 - -### 8) 注释规范 - -- 公共类与复杂方法使用 JSDoc 注释;重要分支与特殊处理写明原因 -- 使用 TODO / FIXME 标注技术债与待优化点 - -### 9) 代码质量与提交 - -- Prettier + ESLint 强制统一风格;提交前 lint-staged 自动修复 -- Git 提交遵循 Conventional Commits;使用 `npm run commit` 触发交互式提交 - -### 10) 模块骨架推荐 - -``` -feature/ -├─ feature.module.ts -├─ feature.controller.ts -├─ feature.service.ts -├─ entities/ -│ └─ feature.entity.ts -├─ dto/ -│ ├─ create-feature.dto.ts -│ └─ update-feature.dto.ts -└─ feature.repository.ts # 如需自定义仓储 -``` - -- Common 层提供通用能力(用户、权限、菜单…),App 层仅组合与编排,尽量避免在 App 层重复造轮子。 - -### 11) API 约定 - -- Swagger 注解最小化:`@ApiTags`、`@ApiOperation`、`@ApiBearerAuth`(如需鉴权) -- 错误码与响应体保持一致性(统一响应封装可在 Common 层提供) - -### 12) 性能与安全 - -- 优先分页与选择性字段;避免 N+1 查询,必要时使用关联加载或查询优化 -- 合理使用缓存与索引;异步/队列处理重任务 -- 输入校验与输出清洗;开启限流与安全中间件;严禁在日志中打印密钥/密码 - -> 以上规范作为默认约束,后续将根据业务与基础设施演进持续完善。 ---- - -## 🔒 层级约束(强制) - -- 允许依赖: - - App(modules) → App(common) → Core(严格单向) - - Vendor 仅提供第三方适配器,不依赖 App/Core 任何实现 - - Addon 可依赖 App(common)/Core,但 App/Core 不得依赖 Addon -- 禁止依赖: - - App(common) → App(modules)(反向依赖) - - Core → App(反向依赖) - - App(common) → Vendor(直接依赖第三方),必须通过 Core 暴露的抽象端口(Port/Token)间接使用 Vendor - - 同层不同域模块严禁相互依赖其内部实现,唯一入口为该域公开的 index.ts 或导出 API -- 访问 Vendor 的约束: - - 第三方 SDK/Client 在 Vendor 层实现具体 Adapter;Core 层定义抽象 Port/Token 并注入;Common 只依赖 Core 的抽象,不直接 import Vendor -- 运行时注册约束: - - 所有外部资源(Redis、OSS、SMTP、SMS 等)统一在 Vendor 模块封装 Provider;由 Core 定义抽象并在应用 root 注册,Common 仅消费抽象 - -## 🧭 导入约束执行(建议自动化) - -- ESLint 约束(示例片段,后续可合并到 eslint.config.mjs): -```js -// import 方向约束,禁止反向与跨层内部实现依赖 -rules: { - 'no-restricted-imports': [ - 'error', - { - patterns: [ - // Common 禁止依赖 App、Vendor - { group: ['@app/*', 'src/app/*', '@vendor/*', 'src/vendor/*'], message: 'Common 层禁止依赖 App/Vendor,请依赖 Core 抽象' }, - // Core 禁止依赖 App/Common/Vendor - { group: ['@app/*', 'src/app/*', '@common/*', 'src/common/*', '@vendor/*', 'src/vendor/*'], message: 'Core 层禁止依赖上层与 Vendor 实现' }, - // 任何层禁止 import 同层其他域的内部文件(建议各域仅通过 index.ts 暴露) - { group: ['**/*/internal/**'], message: '禁止依赖其他域内部实现,请通过其公共 API' }, - ], - }, - ], -} -``` -- 路径别名建议(仅规范,不立即修改): - - @app/*,@common/*,@core/*,@vendor/* - - 同层跨域访问仅允许 import 其公共 API(index.ts 或 public API 文件) - -## 🧩 层级职责与能力清单 - -- Core(核心基础设施) - - 配置系统(ConfigModule + Joi 校验) - - 数据库(TypeORM 基类:BaseEntity/BaseRepository,事务、审计字段) - - 日志(Winston + nest-winston,按日切割,CLS requestId) - - 缓存抽象(Cache Port,默认内存,提供 Redis 端口定义) - - 队列抽象(Bull Port,重试、延时、并发控制) - - HTTP 客户端封装(重试、超时、熔断占位) - - 安全与鉴权抽象(密码策略、加密、加盐、签名、ACL/RBAC 接口) - - 全局管道/过滤器/拦截器(ValidationPipe、异常过滤、响应封装、日志/耗时拦截) - - 限流封装(Throttler) - - 事件总线(EventEmitter 封装) - - 请求上下文(CLS,traceId/requestId) - -- App/Common(通用业务能力,内置功能) - - 账户与组织:User、Dept/Org、Profile - - 认证与授权:Auth(登录/登出/刷新)、JWT、RBAC(Role/Permission/Menu/Route) - - 系统配置:SystemConfig、参数配置、开关项、Settings(Email/SMS/Storage/Payment/Login) - - 字典中心:Dictionary/Enum 管理 - - 文件中心:上传、元数据、存储策略(通过 Core 抽象对接 Vendor) - - 通知中心:邮件/短信/站内信(通过 Core 抽象) - - 审计与操作日志:请求追踪、关键操作记录 - - 任务调度:定时任务编排(cron) - - 健康检查与监控:/health、/metrics(可选) - - 国际化:i18n(可选) - - 多租户(可选,后续版本) - -- App/Modules(具体业务模块) - - 电商模块:商品管理、订单管理、购物车、支付流程 - - CRM 模块:客户管理、销售线索、商机跟进 - - ERP 模块:库存管理、采购管理、财务管理 - - 其他业务模块:根据具体需求扩展 - -- Vendor(第三方适配层) - - Redis 客户端、MySQL 连接适配(由 Core 调用) - - 对象存储:Aliyun OSS / AWS S3 / Qiniu 等适配器 - - 邮件:Nodemailer/SES 适配器 - - 短信:Aliyun SMS / Tencent SMS 适配器 - - 支付:Alipay / WeChat Pay 适配器 - - 验证码:图形/短信验证码服务 - - 第三方 OAuth:GitHub/WeChat 等 - -- Addon(可选插件) - - 雪花 ID / 分布式 ID - - 审批流/流程引擎 - - 特性开关/灰度发布 - - 代码生成/脚手架 - -## 📁 完整目录规范(建议骨架) - -``` -src/ -├─ addon/ -│ ├─ feature-flags/ -│ │ ├─ feature-flags.module.ts -│ │ └─ services/ -│ ├─ id-generator/ -│ │ ├─ id-generator.module.ts -│ │ └─ services/ -│ └─ workflow/ -│ ├─ workflow.module.ts -│ └─ services/ -│ -├─ app/ -│ ├─ ecommerce/ -│ │ ├─ product/ -│ │ │ ├─ product.module.ts -│ │ │ ├─ product.controller.ts -│ │ │ ├─ product.service.ts -│ │ │ ├─ entities/ -│ │ │ └─ dto/ -│ │ ├─ order/ -│ │ │ ├─ order.module.ts -│ │ │ ├─ order.controller.ts -│ │ │ ├─ order.service.ts -│ │ │ ├─ entities/ -│ │ │ └─ dto/ -│ │ └─ cart/ -│ │ ├─ cart.module.ts -│ │ ├─ cart.controller.ts -│ │ ├─ cart.service.ts -│ │ ├─ entities/ -│ │ └─ dto/ -│ ├─ crm/ -│ │ ├─ customer/ -│ │ ├─ lead/ -│ │ └─ opportunity/ -│ └─ erp/ -│ ├─ inventory/ -│ ├─ procurement/ -│ └─ finance/ -│ -├─ common/ -│ ├─ auth/ -│ │ ├─ auth.module.ts -│ │ ├─ auth.controller.ts -│ │ ├─ auth.service.ts -│ │ ├─ strategies/ -│ │ └─ dto/ -│ ├─ user/ -│ │ ├─ user.module.ts -│ │ ├─ user.controller.ts -│ │ ├─ user.service.ts -│ │ ├─ entities/ -│ │ └─ dto/ -│ ├─ rbac/ -│ │ ├─ rbac.module.ts -│ │ ├─ role.service.ts -│ │ ├─ permission.service.ts -│ │ ├─ menu.service.ts -│ │ ├─ entities/ -│ │ └─ dto/ -│ ├─ settings/ -│ │ ├─ settings.module.ts -│ │ ├─ email/ -│ │ ├─ sms/ -│ │ ├─ storage/ -│ │ ├─ payment/ -│ │ └─ login/ -│ ├─ dict/ -│ ├─ file/ -│ ├─ notify/ -│ ├─ audit/ -│ ├─ schedule/ -│ ├─ health/ -│ ├─ i18n/ -│ └─ shared/ -│ ├─ dto/ -│ ├─ constants/ -│ └─ utils/ -│ -├─ core/ -│ ├─ config/ -│ │ ├─ config.module.ts -│ │ └─ schemas/ -│ ├─ database/ -│ │ ├─ database.module.ts -│ │ ├─ base.entity.ts -│ │ └─ base.repository.ts -│ ├─ logger/ -│ │ └─ logger.module.ts -│ ├─ cache/ -│ │ ├─ cache.module.ts -│ │ └─ ports/ -│ │ └─ cache.port.ts -│ ├─ queue/ -│ │ ├─ queue.module.ts -│ │ └─ ports/ -│ │ └─ queue.port.ts -│ ├─ http/ -│ │ └─ http.module.ts -│ ├─ security/ -│ │ ├─ security.module.ts -│ │ ├─ guards/ -│ │ └─ strategies/ -│ ├─ exception/ -│ │ └─ filters/ -│ ├─ interceptor/ -│ │ ├─ logging.interceptor.ts -│ │ └─ transform.interceptor.ts -│ ├─ validation/ -│ │ └─ pipes/ -│ └─ context/ -│ └─ cls.module.ts -│ -├─ vendor/ -│ ├─ redis/ -│ │ ├─ redis.module.ts -│ │ └─ redis.provider.ts -│ ├─ mailer/ -│ │ ├─ mailer.module.ts -│ │ └─ nodemailer.adapter.ts -│ ├─ sms/ -│ │ └─ aliyun-sms.adapter.ts -│ ├─ storage/ -│ │ ├─ oss.adapter.ts -│ │ └─ s3.adapter.ts -│ ├─ payment/ -│ │ ├─ alipay.adapter.ts -│ │ └─ wechatpay.adapter.ts -│ ├─ captcha/ -│ │ └─ captcha.adapter.ts -│ └─ http/ -│ └─ axios.adapter.ts -│ -├─ config/ -│ ├─ database.config.ts -│ ├─ redis.config.ts -│ └─ app.config.ts -│ -└─ main.ts -``` - -## 🔌 依赖倒置与适配器模式约定 - -- Core 只定义 Port/Token(接口/抽象)与领域无关的基础能力 -- Vendor 负责第三方实现(Adapter),通过 Provider 绑定到 Core 的 Token -- Common 仅通过 Core 暴露的 Token 使用能力,禁止直接引用具体 Adapter - -## 🔔 事件与跨层通信 - -- 领域事件优先,使用 EventEmitter;禁止同步强耦合调用导致循环依赖 -- 对跨系统/异步任务,统一走队列(Bull)或消息(后续可扩展) - -## ⚙️ 配置命名约定(节选) - -- LOG_LEVEL、THROTTLE_TTL、THROTTLE_LIMIT -- DB_HOST、DB_PORT、DB_USER、DB_PASS、DB_NAME -- REDIS_HOST、REDIS_PORT、REDIS_DB、REDIS_PASS -- JWT_SECRET、JWT_EXPIRES_IN - -## 🛠️ 待落实的工程化检查(后续可执行) - -- ESLint import 方向约束规则落地 -- tsconfig 路径别名(@app/@common/@core/@vendor) -- 各层 index.ts 统一导出公共 API - ---- - - -## 🎨 前端开发规范(Vben Admin) - -本项目前端基于 **Vben Admin** 框架,参考 **Niucloud** 的业务模式进行开发。前端位于 `admin/` 目录,采用 Vue 3 + TypeScript + Vite 技术栈。 - -**参考目录:** -- Niucloud 前端参考:`g:\wwjcloud-nestjs\reference\niucloud-php\admin\` -- 本项目前端目录:`g:\wwjcloud-nestjs\admin\apps\web-ele\` - -### 1) 前端目录结构规范 - -``` -admin/ -├─ apps/ -│ └─ web-ele/ # 主应用 -│ ├─ src/ -│ │ ├─ addon/ # 插件扩展层(参考 Niucloud) -│ │ │ ├─ shop/ # 商城插件 -│ │ │ ├─ cms/ # 内容管理插件 -│ │ │ └─ marketing/ # 营销插件 -│ │ ├─ app/ # 应用业务层(对应后端 app) -│ │ │ ├─ ecommerce/ # 电商模块 -│ │ │ │ ├─ product/ # 商品管理 -│ │ │ │ ├─ order/ # 订单管理 -│ │ │ │ └─ cart/ # 购物车 -│ │ │ ├─ crm/ # CRM 模块 -│ │ │ │ ├─ customer/ # 客户管理 -│ │ │ │ └─ lead/ # 销售线索 -│ │ │ └─ erp/ # ERP 模块 -│ │ │ ├─ inventory/ # 库存管理 -│ │ │ └─ finance/ # 财务管理 -│ │ ├─ common/ # 通用业务功能(对应后端 common) -│ │ │ ├─ user/ # 用户管理 -│ │ │ │ ├─ api/ # 用户相关 API -│ │ │ │ └─ views/ # 用户页面 -│ │ │ ├─ auth/ # 认证授权 -│ │ │ │ ├─ api/ # 认证相关 API -│ │ │ │ └─ views/ # 认证页面 -│ │ │ ├─ settings/ # 系统设置 -│ │ │ │ ├─ api/ # 设置相关 API -│ │ │ │ │ ├─ email.ts -│ │ │ │ │ ├─ login.ts -│ │ │ │ │ ├─ sms.ts -│ │ │ │ │ └─ storage.ts -│ │ │ │ └─ views/ # 设置页面 -│ │ │ │ ├─ email/ -│ │ │ │ │ └─ index.vue -│ │ │ │ ├─ login/ -│ │ │ │ │ └─ index.vue -│ │ │ │ ├─ sms/ -│ │ │ │ │ └─ index.vue -│ │ │ │ └─ storage/ -│ │ │ │ └─ index.vue -│ │ │ ├─ menu/ # 菜单管理 -│ │ │ │ ├─ api/ # 菜单相关 API -│ │ │ │ └─ views/ # 菜单页面 -│ │ │ ├─ upload/ # 文件上传 -│ │ │ │ ├─ api/ # 上传相关 API -│ │ │ │ └─ views/ # 上传页面 -│ │ │ └─ rbac/ # 角色权限管理 -│ │ │ ├─ api/ # 权限相关 API -│ │ │ └─ views/ # 权限页面 -│ │ ├─ api/ # API 接口层(Vben 规范) -│ │ │ └─ request.ts # 请求客户端配置 -│ │ ├─ views/ # 页面视图层(Vben 规范) -│ │ │ └─ dashboard/ # 仪表板 -│ │ ├─ router/ # 路由配置(Vben 规范) -│ │ │ └─ routes/ -│ │ │ └─ modules/ -│ │ │ ├─ settings.ts -│ │ │ └─ auth.ts -│ │ ├─ stores/ # 状态管理(Vben 规范) -│ │ ├─ components/ # 公共组件(Vben 规范) -│ │ ├─ composables/ # 组合式函数(Vben 规范) -│ │ ├─ utils/ # 工具函数(Vben 规范) -│ │ └─ types/ # 类型定义(Vben 规范) -│ ├─ package.json -│ └─ vite.config.ts -├─ packages/ # 共享包(Vben 规范) -└─ docs/ # 文档 -``` - -### 2) 前端分层架构 - -前端采用分层架构设计,参考 Niucloud 业务模式并结合 Vben 框架规范: - -``` -┌─────────────────┐ -│ Addon │ ← 插件扩展层(参考 Niucloud) -│ (插件扩展) │ 可插拔功能模块,如商城、CMS等 -└─────────────────┘ - ↓ -┌─────────────────┐ -│ App │ ← 应用业务层(对应后端 app 层) -│ (业务应用) │ 电商、CRM、ERP 等具体业务模块 -└─────────────────┘ - ↓ -┌─────────────────┐ -│ Common │ ← 通用功能层(对应后端 common 层) -│ (通用功能) │ 用户、认证、设置、菜单、权限等 -└─────────────────┘ - ↓ -┌─────────────────┐ -│ Views │ ← 视图层(页面组件,Vben 规范) -│ (页面视图) │ 负责用户界面展示和交互 -└─────────────────┘ - ↓ -┌─────────────────┐ -│ Composables │ ← 逻辑层(组合式函数,Vben 规范) -│ (业务逻辑) │ 封装业务逻辑和状态管理 -└─────────────────┘ - ↓ -┌─────────────────┐ -│ API │ ← 接口层(API 调用,Vben 规范) -│ (数据接口) │ 封装后端接口调用 -└─────────────────┘ - ↓ -┌─────────────────┐ -│ Utils │ ← 工具层(通用工具,Vben 规范) -│ (工具函数) │ 提供通用工具和辅助函数 -└─────────────────┘ -``` - -#### 2.1 分层架构详细说明 - -- **插件扩展层 (addon/)**: 可插拔功能模块,参考 Niucloud 插件架构 -- **应用业务层 (app/)**: 对应后端 app 层,具体业务模块 - - **电商模块 (app/ecommerce/)**: 商品、订单、购物车等 - - **CRM 模块 (app/crm/)**: 客户管理、销售线索等 - - **ERP 模块 (app/erp/)**: 库存、财务等 -- **通用功能层 (common/)**: 对应后端 common 层,框架通用功能 - - **用户管理 (common/user/)**: 用户相关功能 - - **认证授权 (common/auth/)**: 登录、权限等 - - **系统设置 (common/settings/)**: 邮件、短信、存储等配置 - - **菜单管理 (common/menu/)**: 菜单配置 - - **文件上传 (common/upload/)**: 文件处理 - - **角色权限 (common/rbac/)**: RBAC 权限管理 -- **API 接口层 (api/)**: 统一管理后端接口调用(Vben 规范) -- **页面视图层 (views/)**: 业务页面组件(Vben 规范) -- **路由配置层 (router/)**: 页面路由管理(Vben 规范) -- **状态管理层 (stores/)**: 全局状态管理(Vben 规范) -- **组件层 (components/)**: 可复用组件(Vben 规范) -- **工具层 (utils/)**: 通用工具函数(Vben 规范) - -### 3) API 接口层规范 - -#### 3.1 接口文件组织 -- 按业务模块划分:`api/settings/`、`api/auth/`、`api/user/` 等 -- 每个模块包含:接口函数、类型定义、响应处理 -- 统一使用 `requestClient` 进行 HTTP 请求 - -#### 3.2 接口命名约定 -```typescript -// 获取数据:get{Module}Api 或 get{Module}{Action}Api -export const getEmailSettingsApi = () => requestClient.get('/settings/email') - -// 更新数据:update{Module}Api 或 update{Module}{Action}Api -export const updateEmailSettingsApi = (data: UpdateEmailSettingsDto) => - requestClient.put('/settings/email', data) - -// 创建数据:create{Module}Api -export const createUserApi = (data: CreateUserDto) => - requestClient.post('/users', data) - -// 删除数据:delete{Module}Api -export const deleteUserApi = (id: string) => - requestClient.delete(`/users/${id}`) -``` - -#### 3.3 类型定义规范 -```typescript -// 响应类型以 Vo 结尾(View Object) -export interface EmailSettingsVo { - host: string - port: number - username: string - password: string - encryption: string - fromAddress: string - fromName: string -} - -// 请求类型以 Dto 结尾(Data Transfer Object) -export interface UpdateEmailSettingsDto { - host?: string - port?: number - username?: string - password?: string - encryption?: string - fromAddress?: string - fromName?: string -} -``` - -### 4) 页面组件规范 - -#### 4.1 页面结构模板(Vben Admin + Element Plus 规范) -```vue - -``` - -#### 4.2 组件脚本结构(Vben Admin + Element Plus 规范) -```vue - -``` - -### 5) 路由配置规范(Vben Admin 规范) - -#### 5.1 路由文件组织 -- 按模块划分:`src/router/routes/modules/settings.ts` -- 路由配置遵循 Vben 路由规范 -- 使用懒加载方式导入组件 -- 路由权限通过 `meta.authority` 配置 - -#### 5.2 路由配置示例 -```typescript -import type { RouteRecordRaw } from 'vue-router' - -const routes: RouteRecordRaw[] = [ - { - path: '/settings', - name: 'Settings', - component: '#/layouts/index.vue', - meta: { - title: '系统设置', - icon: 'lucide:settings', - order: 1000, - }, - children: [ - { - path: '/settings/email', - name: 'EmailSettings', - component: () => import('#/views/settings/email/index.vue'), - meta: { - title: '邮件设置', - icon: 'lucide:mail', - authority: ['admin', 'super'], - }, - }, - { - path: '/settings/sms', - name: 'SmsSettings', - component: () => import('#/views/settings/sms/index.vue'), - meta: { - title: '短信设置', - icon: 'lucide:message-square', - authority: ['admin', 'super'], - }, - }, - { - path: '/settings/storage', - name: 'StorageSettings', - component: () => import('#/views/settings/storage/index.vue'), - meta: { - title: '存储设置', - icon: 'lucide:hard-drive', - authority: ['admin', 'super'], - }, - }, - ], - }, -] - -export default routes -``` - -#### 5.3 路由权限配置 -- 使用 `meta.authority` 配置页面访问权限 -- 支持角色权限:`['admin', 'super']` -- 支持权限码:`['settings:email:read', 'settings:email:write']` -- 无权限配置表示公开访问 - -### 6) 状态管理规范(Vben Admin 规范) - -#### 6.1 使用 Pinia 进行状态管理 -- 按业务模块划分 Store:`src/stores/modules/` -- 优先使用 Composition API 风格 -- 合理使用持久化存储 -- 遵循 Vben 状态管理模式 - -#### 6.2 Store 示例 -```typescript -import { defineStore } from 'pinia' -import { ref, computed } from 'vue' -import type { EmailSettingsVo } from '#/api/settings/email' -import { getEmailSettingsApi, updateEmailSettingsApi } from '#/api/settings/email' - -export const useSettingsStore = defineStore( - 'settings', - () => { - // 状态 - const emailSettings = ref(null) - const smsSettings = ref(null) - const storageSettings = ref(null) - const loading = ref(false) - - // 计算属性 - const isEmailConfigured = computed(() => { - return emailSettings.value?.host && emailSettings.value?.username - }) - - const isAnySettingLoading = computed(() => loading.value) - - // 方法 - const fetchEmailSettings = async () => { - loading.value = true - try { - const data = await getEmailSettingsApi() - emailSettings.value = data - return data - } catch (error) { - console.error('Failed to fetch email settings:', error) - throw error - } finally { - loading.value = false - } - } - - const updateEmailSettings = async (settings: EmailSettingsVo) => { - loading.value = true - try { - await updateEmailSettingsApi(settings) - emailSettings.value = settings - return settings - } catch (error) { - console.error('Failed to update email settings:', error) - throw error - } finally { - loading.value = false - } - } - - const resetEmailSettings = () => { - emailSettings.value = null - } - - return { - // 状态 - emailSettings, - smsSettings, - storageSettings, - loading, - // 计算属性 - isEmailConfigured, - isAnySettingLoading, - // 方法 - fetchEmailSettings, - updateEmailSettings, - resetEmailSettings, - } - }, - { - // 持久化配置 - persist: { - key: 'settings-store', - storage: localStorage, - paths: ['emailSettings', 'smsSettings', 'storageSettings'], - }, - }, -) -``` - -### 7) 组件开发规范(Vben Admin 规范) - -#### 7.1 组件命名 -- 使用 PascalCase 命名 -- 组件文件名与组件名保持一致 -- 页面组件放在 `views/` 目录 -- 公共组件放在 `components/` 目录 -- 遵循 Vben 组件命名约定 - -#### 7.2 组件使用优先级(基于 Vben Admin 官方规范) - -**组件选择原则:** -1. **Vben 封装组件**(最高优先级):如 `VbenForm`、`VbenModal`、`VbenDrawer`、`VbenVxeTable` 等 -2. **适配器组件**(中等优先级):通过 `src/adapter/component` 和 `src/adapter/form` 适配的组件 -3. **原生 UI 库组件**(最低优先级):如 Element Plus 的 `el-card`、`el-button` 等 - -**官方指导原则:** -> "如果你觉得现有组件的封装不够理想,或者不完全符合你的需求,大可以直接使用原生组件,亦或亲手封装一个适合的组件。框架提供的组件并非束缚,使用与否,完全取决于你的需求与自由。" —— Vben Admin 官方文档 - -**具体使用指导:** -- **表单开发**:**必须优先使用** `useVbenForm` 和 `VbenForm`,提供完整的表单解决方案,支持动态表单、验证、联动等高级功能 -- **模态框**:**必须优先使用** `useVbenModal` 和 `VbenModal`,支持拖拽、全屏、自动高度等功能,提供统一的交互体验 -- **抽屉**:**必须优先使用** `useVbenDrawer` 和 `VbenDrawer`,提供更好的用户体验和统一的样式 -- **表格**:优先使用 `VbenVxeTable`,基于 vxe-table 封装,结合 VbenForm 搜索;简单展示可使用 Element Plus 的 `ElTable` -- **基础组件**:可根据需求选择 Vben 组件或通过适配器使用 Element Plus 组件 - -**适配器配置要求:** -- Element Plus 组件适配:在 `src/adapter/component/index.ts` 中配置组件映射 -- 表单适配:在 `src/adapter/form.ts` 中配置表单验证、国际化等 -- 适配器处理:v-model 属性映射、国际化、主题适配、验证规则等 -- 灵活使用:可根据具体需求选择 Vben 组件、适配器组件或原生组件 - -#### 7.3 组件结构模板(Vben 组件优先) -```vue - - - - - -``` - -#### 7.3 组件 Props 定义 -```typescript -interface Props { - title?: string - loading?: boolean - data?: Record -} - -const props = withDefaults(defineProps(), { - title: '', - loading: false, - data: () => ({}) -}) -``` - -#### 7.4 组件事件定义 -```typescript -interface Emits { - save: [data: Record] - cancel: [] - change: [value: string] -} - -const emit = defineEmits() -``` - -### 8) 样式规范(Vben Admin 规范) - -#### 8.1 样式系统 -- 优先使用 Tailwind CSS 工具类 -- 使用 CSS Variables 进行主题定制 -- 避免编写自定义 CSS,除非必要 -- 遵循 Vben 设计系统 - -#### 8.2 Tailwind CSS 使用 -```vue - -``` - -#### 8.3 主题定制 -```css -/* 在 tailwind.config.js 中定义主题 */ -module.exports = { - theme: { - extend: { - colors: { - primary: { - 50: '#eff6ff', - 500: '#3b82f6', - 600: '#2563eb', - 700: '#1d4ed8', - }, - gray: { - 50: '#f9fafb', - 100: '#f3f4f6', - 900: '#111827', - }, - }, - spacing: { - '18': '4.5rem', - '88': '22rem', - }, - }, - }, -} -``` - -#### 8.4 自定义样式(仅在必要时使用) -```vue - -``` - -### 9) 国际化规范(Vben Admin 规范) - -#### 9.1 语言文件组织 -``` -src/locales/ -├── langs/ -│ ├── zh-CN/ -│ │ ├── common.json -│ │ ├── settings.json -│ │ ├── validation.json -│ │ └── index.ts -│ ├── en-US/ -│ │ ├── common.json -│ │ ├── settings.json -│ │ ├── validation.json -│ │ └── index.ts -│ └── index.ts -├── helper.ts -└── index.ts -``` - -#### 9.2 语言文件示例 -```json -// src/locales/langs/zh-CN/settings.json -{ - "title": "系统设置", - "email": { - "title": "邮件设置", - "description": "配置系统邮件发送相关参数", - "form": { - "host": "SMTP服务器", - "port": "端口", - "username": "用户名", - "password": "密码", - "encryption": "加密方式", - "fromName": "发件人名称", - "fromEmail": "发件人邮箱" - }, - "placeholder": { - "host": "请输入SMTP服务器地址", - "port": "请输入端口号", - "username": "请输入用户名", - "password": "请输入密码" - }, - "validation": { - "hostRequired": "请输入SMTP服务器地址", - "portRequired": "请输入端口号", - "usernameRequired": "请输入用户名", - "passwordRequired": "请输入密码" - } - }, - "sms": { - "title": "短信设置", - "description": "配置短信发送服务商参数", - "form": { - "provider": "服务商", - "accessKey": "Access Key", - "secretKey": "Secret Key", - "signName": "签名" - } - }, - "storage": { - "title": "存储设置", - "description": "配置文件存储相关参数", - "form": { - "driver": "存储驱动", - "bucket": "存储桶", - "region": "地域", - "endpoint": "访问域名" - } - } -} -``` - -#### 9.3 在组件中使用 -```vue - - - -``` - -#### 9.4 国际化配置 -```typescript -// src/locales/index.ts -import { createI18n } from 'vue-i18n' -import { getLocale, setLocale } from './helper' - -// 导入语言包 -import zhCN from './langs/zh-CN' -import enUS from './langs/en-US' - -const messages = { - 'zh-CN': zhCN, - 'en-US': enUS, -} - -export const i18n = createI18n({ - legacy: false, - locale: getLocale(), - fallbackLocale: 'zh-CN', - messages, - globalInjection: true, -}) - -export { setLocale, getLocale } -``` - -### 10) 错误处理规范(Vben Admin 规范) - -#### 10.1 API 错误处理 -```typescript -// src/api/request.ts -import { requestClient } from '#/api/request' -import { useAuthStore } from '#/stores/auth' -import { message } from '#/components' -import { $t } from '#/locales' - -// 请求拦截器 -requestClient.interceptors.request.use( - (config) => { - const authStore = useAuthStore() - const token = authStore.accessToken - - if (token) { - config.headers.Authorization = `Bearer ${token}` - } - - return config - }, - (error) => { - return Promise.reject(error) - } -) - -// 响应拦截器 -requestClient.interceptors.response.use( - (response) => { - const { code, data, message: msg } = response.data - - // 根据业务状态码处理 - if (code === 200 || code === 0) { - return data - } else { - const errorMessage = msg || $t('common.requestFailed') - message.error(errorMessage) - return Promise.reject(new Error(errorMessage)) - } - }, - (error) => { - const { response } = error - - if (response) { - const { status, data } = response - - switch (status) { - case 401: - const authStore = useAuthStore() - authStore.logout() - message.error($t('common.tokenExpired')) - break - case 403: - message.error($t('common.noPermission')) - break - case 404: - message.error($t('common.notFound')) - break - case 500: - message.error($t('common.serverError')) - break - default: - message.error(data?.message || $t('common.requestFailed')) - } - } else { - // 网络错误 - message.error($t('common.networkError')) - } - - return Promise.reject(error) - } -) -``` - -#### 10.2 组件错误处理 -```vue - -``` - -#### 10.3 全局错误处理 -```typescript -// src/main.ts -import { createApp } from 'vue' -import App from './App.vue' - -const app = createApp(App) - -// 全局错误处理 -app.config.errorHandler = (error, instance, info) => { - console.error('Global error:', error) - console.error('Component instance:', instance) - console.error('Error info:', info) - - // 发送错误到监控服务 - // reportError(error, { instance, info }) -} - -// 未捕获的 Promise 错误 -window.addEventListener('unhandledrejection', (event) => { - console.error('Unhandled promise rejection:', event.reason) - // reportError(event.reason) -}) - -app.mount('#app') -``` - -### 11) 性能优化规范(Vben Admin 规范) - -#### 11.1 组件懒加载 -```typescript -// 路由懒加载 -const routes: RouteRecordRaw[] = [ - { - path: '/settings', - name: 'Settings', - component: () => import('#/views/settings/index.vue'), - meta: { - title: '系统设置', - }, - }, -] - -// 组件懒加载 -import { defineAsyncComponent } from 'vue' - -const AsyncHeavyComponent = defineAsyncComponent({ - loader: () => import('./HeavyComponent.vue'), - loadingComponent: () => import('#/components/Loading.vue'), - errorComponent: () => import('#/components/Error.vue'), - delay: 200, - timeout: 3000, -}) -``` - -#### 11.2 虚拟滚动 -```vue - - - -``` - -#### 11.3 图片优化 -```vue - - - -``` - -#### 11.4 状态管理优化 -```typescript -// 使用 computed 缓存计算结果 -import { computed, ref } from 'vue' - -const expensiveData = ref([]) - -// 缓存计算结果 -const processedData = computed(() => { - return expensiveData.value - .filter(item => item.active) - .map(item => ({ - ...item, - displayName: `${item.firstName} ${item.lastName}`, - })) - .sort((a, b) => a.displayName.localeCompare(b.displayName)) -}) - -// 使用 shallowRef 优化大对象 -import { shallowRef, triggerRef } from 'vue' - -const largeObject = shallowRef({ - // 大量数据 -}) - -// 更新时手动触发响应 -const updateLargeObject = (newData: any) => { - largeObject.value = { ...largeObject.value, ...newData } - triggerRef(largeObject) -} -``` - -#### 11.5 网络请求优化 -```typescript -// 请求去重 -import { ref } from 'vue' - -const requestCache = new Map>() - -const cachedRequest = async (url: string, options?: any) => { - const cacheKey = `${url}-${JSON.stringify(options)}` - - if (requestCache.has(cacheKey)) { - return requestCache.get(cacheKey) - } - - const promise = fetch(url, options).then(res => res.json()) - requestCache.set(cacheKey, promise) - - // 请求完成后清除缓存 - promise.finally(() => { - requestCache.delete(cacheKey) - }) - - return promise -} - -// 请求防抖 -import { debounce } from 'lodash-es' - -const searchKeyword = ref('') -const searchResults = ref([]) - -const debouncedSearch = debounce(async (keyword: string) => { - if (!keyword.trim()) { - searchResults.value = [] - return - } - - try { - const results = await searchApi(keyword) - searchResults.value = results - } catch (error) { - console.error('Search failed:', error) - } -}, 300) - -watch(searchKeyword, (newKeyword) => { - debouncedSearch(newKeyword) -}) -``` - -### 12) 测试规范(Vben Admin 规范) - -#### 12.1 单元测试 -```typescript -// tests/unit/components/EmailSettings.spec.ts -import { mount } from '@vue/test-utils' -import { describe, it, expect, beforeEach, vi } from 'vitest' -import { createTestingPinia } from '@pinia/testing' -import EmailSettings from '#/views/settings/email/index.vue' -import { VbenForm } from '#/components/form' - -// Mock API -vi.mock('#/api/settings/email', () => ({ - getEmailSettingsApi: vi.fn(), - updateEmailSettingsApi: vi.fn(), -})) - -describe('EmailSettings', () => { - let wrapper: any - - beforeEach(() => { - wrapper = mount(EmailSettings, { - global: { - plugins: [ - createTestingPinia({ - createSpy: vi.fn, - }), - ], - components: { - VbenForm, - }, - }, - }) - }) - - it('renders correctly', () => { - expect(wrapper.find('[data-testid="email-settings-title"]').text()) - .toBe('邮件设置') - }) - - it('displays form fields correctly', () => { - const formFields = wrapper.findAll('.vben-form-item') - expect(formFields.length).toBeGreaterThan(0) - }) - - it('handles form submission', async () => { - const mockUpdateApi = vi.mocked(updateEmailSettingsApi) - mockUpdateApi.mockResolvedValue({}) - - const formData = { - host: 'smtp.test.com', - port: 587, - username: 'test@test.com', - password: 'password123', - } - - await wrapper.vm.handleSubmit(formData) - - expect(mockUpdateApi).toHaveBeenCalledWith(formData) - // 验证 Element Plus 消息提示 - expect(ElMessage.success).toHaveBeenCalledWith('保存成功') - }) -}) -``` - -#### 12.2 API 测试 -```typescript -// tests/api/settings.spec.ts -import { describe, it, expect, vi, beforeEach } from 'vitest' -import { getEmailSettingsApi, updateEmailSettingsApi } from '#/api/settings/email' -import { requestClient } from '#/api/request' -import type { EmailSettingsVo } from '#/api/settings/email' - -// Mock request client -vi.mock('#/api/request', () => ({ - requestClient: { - get: vi.fn(), - put: vi.fn(), - }, -})) - -describe('Settings API', () => { - beforeEach(() => { - vi.clearAllMocks() - }) - - describe('getEmailSettingsApi', () => { - it('should fetch email settings successfully', async () => { - const mockData: EmailSettingsVo = { - host: 'smtp.example.com', - port: 587, - username: 'test@example.com', - password: 'password123', - encryption: 'tls', - fromName: 'System', - fromEmail: 'noreply@example.com', - } - - vi.mocked(requestClient.get).mockResolvedValue(mockData) - - const result = await getEmailSettingsApi() - - expect(requestClient.get).toHaveBeenCalledWith('/settings/email') - expect(result).toEqual(mockData) - }) - }) -}) -``` - -#### 12.3 E2E 测试 -```typescript -// tests/e2e/settings.spec.ts -import { test, expect } from '@playwright/test' - -test.describe('Settings Page', () => { - test.beforeEach(async ({ page }) => { - // 登录 - await page.goto('/login') - await page.fill('[data-testid="username-input"]', 'admin') - await page.fill('[data-testid="password-input"]', 'password') - await page.click('[data-testid="login-button"]') - - // 等待登录完成 - await page.waitForURL('/dashboard') - }) - - test('should navigate to email settings', async ({ page }) => { - await page.goto('/settings/email') - - await expect(page.locator('[data-testid="email-settings-title"]')) - .toHaveText('邮件设置') - }) - - test('should update email settings', async ({ page }) => { - await page.goto('/settings/email') - - // 填写表单(Element Plus 输入框) - await page.fill('[data-testid="host-input"]', 'smtp.test.com') - await page.fill('[data-testid="port-input"]', '587') - - // 提交表单(Element Plus 按钮) - await page.click('[data-testid="save-button"]') - - // 验证 Element Plus 成功消息 - await expect(page.locator('.el-message--success')) - .toHaveText('保存成功') - }) -}) -``` - -#### 12.4 测试配置 -```typescript -// vitest.config.ts -import { defineConfig } from 'vitest/config' -import vue from '@vitejs/plugin-vue' -import { resolve } from 'path' - -export default defineConfig({ - plugins: [vue()], - test: { - globals: true, - environment: 'jsdom', - setupFiles: ['./tests/setup.ts'], - }, - resolve: { - alias: { - '#': resolve(__dirname, './src'), - }, - }, -}) -``` - -### 13) 开发工具配置 - -#### 13.1 必要的 VSCode 插件 -- Vue Language Features (Volar) -- TypeScript Vue Plugin (Volar) -- Tailwind CSS IntelliSense -- ESLint -- Prettier - -#### 13.2 代码质量检查 -```bash -# 代码格式化 -pnpm format - -# 代码检查 -pnpm lint - -# 类型检查 -pnpm type-check - -# 构建检查 -pnpm build -``` - -### 14) 与后端协作规范 - -#### 14.1 接口约定 -- 前端接口路径与后端保持一致 -- 使用 TypeScript 类型定义确保类型安全 -- 统一错误码和响应格式 - -#### 14.2 数据流约定 -```typescript -// 后端响应格式 -interface ApiResponse { - code: number - data: T - message: string -} - -// 前端自动解包 data 字段 -const data = await getEmailSettingsApi() // 直接返回 EmailSettingsVo -``` - ---- - -## 📖 重要开发说明 - -### Vben Admin 官方文档参考 - -本项目基于 **Vben Admin** 框架开发,所有前端开发规范均应严格遵循 Vben Admin 官方文档: - -- **官方文档地址**: https://doc.vben.pro/ -- **当前使用版本**: Element Plus 适配版本 -- **表单组件**: 使用 Vben Form,支持多种 UI 库适配 -- **组件库**: Element Plus(项目已配置) - -### 开发约束 - -1. **禁止自创规范**: 所有前端开发必须参考 Vben Admin 官方文档,禁止自创或假设性开发 -2. **参考项目文档**: 可参考项目 `docs/` 目录下的代码规范和示例 - -#### 组件使用优先级(基于项目实际代码示例) - -**组件选择原则:** -1. **Element Plus 原生组件**(最高优先级):如 `ElCard`、`ElButton`、`ElTable`、`ElMessage` 等 -2. **Vben 封装组件**(中等优先级):如 `WorkbenchHeader`、`WorkbenchProject`、`AnalysisChartCard`、`Page` 等业务组件 -3. **适配器组件**(最低优先级):自定义适配器组件,仅在特殊需求时使用 - -**实际使用示例:** - -**基础 UI 组件**:直接使用 Element Plus -```vue - - - -``` - -**业务组件**:使用 Vben 封装组件 -```vue - - - -``` - -**官方指导原则:** -> "如果你觉得现有组件的封装不够理想,或者不完全符合你的需求,大可以直接使用原生组件,亦或亲手封装一个适合的组件。框架提供的组件并非束缚,使用与否,完全取决于你的需求与自由。" —— Vben Admin 官方文档 - -**具体使用指导:** -- **基础 UI**:优先使用 Element Plus 原生组件(`ElCard`、`ElButton`、`ElTable`、`ElForm` 等) -- **页面布局**:使用 Vben 的 `Page` 组件作为页面容器 -- **业务组件**:使用 Vben 封装的业务组件(`WorkbenchHeader`、`WorkbenchProject` 等) -- **表单开发**:可选择 Element Plus 的 `ElForm` 或 Vben 的表单组件,根据复杂度决定 -- **消息提示**:直接使用 Element Plus 的 `ElMessage`、`ElNotification` -- **数据展示**:直接使用 Element Plus 的 `ElTable`、图表组件等 - -### 协同开发要求 - -开发步骤,要前后端一起协同开发,项目的数据库账号、密码、库表均为wwjcloud。前端和后端项目均有规范工具,开发前一定要检查工具是否配置正确,是否有必要的插件,是否正常运行状态。前端开发进行修改框架核心内容,可以参考前端docs目录参考代码规范禁止自创和假设造成,没有规范的去写。 - -### 技术栈确认 - -- **前端框架**: Vue 3 + TypeScript + Vite -- **UI 组件库**: Element Plus -- **表单解决方案**: Vben Form(支持 Element Plus 适配) -- **状态管理**: Pinia -- **路由**: Vue Router -- **构建工具**: Vite -- **包管理器**: pnpm \ No newline at end of file +时间戳 → 使用 TypeORM 的 @CreateDateColumn, @UpdateDateColumn,但指定 type: 'int' +软删除 → 使用 @Column 手动定义 is_del 和 delete_time,都是 number 类型 +JSON 字段 → 使用 @Column('json') +状态管理 → 使用 NestJS 的 ValidationPipe +**注意**: 本文档是 AI 开发的重要参考,请严格按照平衡原则进行开发,既要尊重 NestJS 框架特性,又要与 PHP 项目业务逻辑保持一致。 + 开发步骤和注意事项 + 开发检查清单 +✅ 开发前检查 +[ ] 查看PHP模型字段定义 +[ ] 检查数据库表结构 +[ ] 确认字段类型和约束 +[ ] 了解业务逻辑关系 +✅ 开发中检查 +[ ] 字段名与数据库一致 +[ ] 时间戳使用int类型 +[ ] 软删除使用is_del字段 +[ ] 关联关系正确定义 +[ ] 查询语法使用TypeORM操作符 +✅ 开发后检查 +[ ] npm run build 无错误 +[ ] 与PHP项目字段名100%一致 +[ ] 业务逻辑与PHP保持一致 +[ ] 类型安全无警告 +🚀 简化处理步骤 +三步快速修复法 +看PHP → 找到对应字段名和类型 +查数据库 → 确认实际字段结构 +写NestJS → 使用框架特性实现相同逻辑 +优先级处理顺序 +高优先级:字段名不匹配(直接复用PHP) +中优先级:类型不匹配(遵守NestJS规范) +低优先级:语法优化(保持代码整洁) +一句话总结 +"用NestJS的语法,写PHP的逻辑,保持数据库的一致性" diff --git a/AI-DEVELOPMENT-GUIDE.md b/AI-DEVELOPMENT-GUIDE.md new file mode 100644 index 0000000..5c54fae --- /dev/null +++ b/AI-DEVELOPMENT-GUIDE.md @@ -0,0 +1,385 @@ +# WWJ Cloud AI 开发规范 + +> 本文档为AI开发WWJ Cloud项目时必须遵循的规范,确保代码质量和架构一致性 + +## 🏗️ **架构分层规范** + +### **微服务架构原则** +1. **按业务领域拆分**,不是按技术层级拆分 +2. **每个微服务 = 一个完整的业务模块** +3. **服务内部保持分层架构**(Controller/Service/Entity) +4. **服务间通过接口通信**,避免直接依赖 + +### **微服务迁移路径** +- **阶段1**:模块化重构(当前) +- **阶段2**:服务边界定义 +- **阶段3**:微服务拆分部署 + +### **目录结构规范** +``` +wwjcloud/src/common/{module}/ +├── entities/ # 实体层(数据模型) +│ ├── {Entity}.ts # 主实体 +│ └── {SubEntity}.ts # 子实体 +├── services/ # 服务层 +│ ├── core/ # 核心业务逻辑(共用) +│ │ └── Core{Entity}Service.ts +│ ├── api/ # 前台API服务 +│ │ └── {Entity}Service.ts +│ └── admin/ # 后台管理服务 +│ └── {Entity}Service.ts +├── controllers/ # 控制器层 +│ ├── api/ # 前台API控制器 +│ │ └── {Entity}Controller.ts +│ └── admin/ # 后台管理控制器 +│ └── {Entity}Controller.ts +├── dto/ # 数据传输对象 +│ ├── api/ # 前台API DTO +│ │ ├── create-{entity}.dto.ts +│ │ └── update-{entity}.dto.ts +│ └── admin/ # 后台管理 DTO +│ ├── create-{entity}.dto.ts +│ └── update-{entity}.dto.ts +└── {module}.module.ts # 模块定义 +``` + +### **命名规范** +```typescript +// 实体类:PascalCase +export class Member {} +export class MemberLevel {} + +// 服务类:PascalCase + Service +export class CoreMemberService {} +export class MemberService {} +export class AdminMemberService {} + +// 控制器类:PascalCase + Controller +export class ApiMemberController {} +export class AdminMemberController {} + +// 文件名:kebab-case +// member.service.ts +// member.controller.ts +``` + +## 🛣️ **路由设计规范** + +### **路由前缀规范** +```typescript +// 前台API路由 +@Controller('api/member') +export class ApiMemberController { + // 前台用户访问的接口 +} + +// 后台管理路由 +@Controller('admin/member') +export class AdminMemberController { + // 后台管理员访问的接口 +} +``` + +### **API路径规范** +```typescript +// 前台API路径 +GET /api/member/profile # 获取个人资料 +PUT /api/member/profile # 更新个人资料 +POST /api/member/sign # 会员签到 +GET /api/member/orders # 获取订单列表 + +// 后台管理路径 +GET /admin/member # 获取会员列表 +POST /admin/member # 创建会员 +GET /admin/member/:id # 获取会员详情 +PUT /admin/member/:id # 更新会员 +DELETE /admin/member/:id # 删除会员 +``` + +## 📚 **OpenAPI 文档规范** + +### **API分组规范** +```typescript +// 前台API文档 +@ApiTags('前台-会员管理') +@Controller('api/member') +export class ApiMemberController {} + +// 后台管理文档 +@ApiTags('后台-会员管理') +@Controller('admin/member') +export class AdminMemberController {} +``` + +### **认证和权限规范** + +#### **基础认证规范** +```typescript +// 前台用户认证 +@UseGuards(JwtAuthGuard) +@ApiBearerAuth() +@Controller('api/member') + +// 后台管理员认证 +@UseGuards(JwtAuthGuard, AdminGuard) +@ApiBearerAuth() +@Controller('admin/member') + +// 公开接口 +@Public() +@Controller('api/member') +``` + +#### **RBAC权限控制规范** + +##### **权限模型设计** +``` +用户(User) → 角色(Role) → 权限(Permission) → 资源(Resource) +``` + +##### **权限控制层次** +1. **接口级权限**:通过Guard控制API访问 +2. **数据级权限**:通过Service控制数据访问 +3. **字段级权限**:通过DTO控制字段显示 + +##### **权限装饰器规范** +```typescript +// 角色权限 +@Roles('admin', 'manager') +@Controller('admin/member') + +// 权限点 +@Permissions('member:create', 'member:update') +@Post('member') + +// 数据权限 +@DataScope('site_id') +async getMemberList() {} +``` + +##### **权限验证流程** +``` +请求到达 → 验证JWT → 检查角色 → 检查权限 → 访问资源 +``` + +## 🔧 **服务层职责规范** + +### **Core服务层(核心业务逻辑)** +```typescript +// 职责:纯业务逻辑,不涉及HTTP请求 +export class CoreMemberService { + // 会员等级计算 + calculateMemberLevel(points: number): MemberLevel {} + + // 会员积分计算 + calculateMemberPoints(actions: MemberAction[]): number {} + + // 会员状态验证 + validateMemberStatus(member: Member): boolean {} +} +``` + +### **API服务层(前台API服务)** +```typescript +// 职责:前台用户相关的业务逻辑 +export class MemberService { + constructor(private coreService: CoreMemberService) {} + + // 获取个人资料 + async getProfile(memberId: number): Promise {} + + // 更新个人资料 + async updateProfile(memberId: number, data: UpdateProfileDto): Promise {} + + // 会员签到 + async signIn(memberId: number): Promise {} +} +``` + +### **Admin服务层(后台管理服务)** +```typescript +// 职责:后台管理相关的业务逻辑 +export class AdminMemberService { + constructor(private coreService: CoreMemberService) {} + + // 获取会员列表 + async getMemberList(query: QueryMemberDto): Promise> {} + + // 创建会员 + async createMember(data: CreateMemberDto): Promise {} + + // 更新会员 + async updateMember(id: number, data: UpdateMemberDto): Promise {} + + // 删除会员 + async deleteMember(id: number): Promise {} +} +``` + +## 🔗 **模块间依赖规范** + +### **依赖注入原则** +1. **禁止循环依赖**:A模块不能依赖B模块,B模块也不能依赖A模块 +2. **单向依赖**:只能从上层依赖下层,不能从下层依赖上层 +3. **接口隔离**:通过接口定义模块间的契约 + +### **模块依赖关系** +``` +Auth模块 ← Admin模块 ← RBAC模块 + ↓ ↓ ↓ +Core模块 ← Common模块 ← 其他模块 +``` + +### **共享服务规范** +- **Core服务**:可以被多个模块共享 +- **Common服务**:提供通用功能(如工具类、常量等) +- **避免跨模块直接调用**:通过接口或事件通信 + +## 🔄 **数据流规范** + +### **前台用户数据流** +``` +前台用户 → API控制器 → API服务 → Core服务 → 数据库 +``` + +### **后台管理数据流** +``` +后台管理员 → Admin控制器 → Admin服务 → Core服务 → 数据库 +``` + +### **数据隔离规范** +```typescript +// 前台用户只能访问自己的数据 +async getProfile(memberId: number, currentMemberId: number): Promise { + if (memberId !== currentMemberId) { + throw new ForbiddenException('无权访问其他用户数据'); + } + return this.memberRepository.findOne({ where: { memberId } }); +} + +// 后台管理员可以访问所有数据 +async getMemberList(query: QueryMemberDto): Promise> { + return this.memberRepository.findAndCount({ + where: query, + skip: (query.page - 1) * query.limit, + take: query.limit, + }); +} +``` + +## ❌ **错误处理规范** + +### **统一错误码** +```typescript +export enum ErrorCode { + // 通用错误 + SUCCESS = 200, + BAD_REQUEST = 400, + UNAUTHORIZED = 401, + FORBIDDEN = 403, + NOT_FOUND = 404, + + // 业务错误 + MEMBER_NOT_FOUND = 1001, + MEMBER_ALREADY_EXISTS = 1002, + INSUFFICIENT_PERMISSIONS = 1003, +} +``` + +### **统一响应格式** +```typescript +export interface ApiResponse { + code: number; + message: string; + data: T; + timestamp: string; + path: string; +} +``` + +## 🚀 **开发流程规范** + +### **第一步:创建实体** +1. 根据数据库表结构创建实体类 +2. 确保字段名与数据库一致 +3. 添加必要的装饰器和验证 + +### **第二步:创建Core服务** +1. 实现纯业务逻辑 +2. 不涉及HTTP请求和响应 +3. 可被其他服务复用 + +### **第三步:创建API服务** +1. 实现前台用户相关逻辑 +2. 调用Core服务 +3. 处理前台特定的业务需求 +4. **必须完整实现所有业务逻辑,禁止TODO和Mock** + +### **第四步:创建Admin服务** +1. 实现后台管理相关逻辑 +2. 调用Core服务 +3. 处理后台管理需求 +4. **必须完整实现所有业务逻辑,禁止TODO和Mock** + +### **第五步:创建控制器** +1. 实现HTTP接口 +2. 调用对应的服务 +3. 处理请求和响应 + +### **第六步:配置模块** +1. 导入所有依赖 +2. 配置提供者和控制器 +3. 导出必要的服务 + +### **第七步:代码审查** +1. 检查是否还有TODO标记 +2. 检查是否还有Mock数据 +3. 检查是否还有空实现 +4. 确保所有业务逻辑完整 + +## 📋 **必须遵循的规则** + +### **1. 数据库字段一致性** +- 所有实体字段名必须与 `sql/wwjcloud.sql` 中的表结构完全一致 +- 使用 `@Column({ name: 'field_name' })` 确保字段映射正确 + +### **2. 多租户支持** +- 所有业务表必须包含 `site_id` 字段 +- 独立版模式 `site_id = 0` +- SaaS模式 `site_id > 0` + +### **3. 认证和权限** +- 前台API使用 `JwtAuthGuard` +- 后台API使用 `JwtAuthGuard + AdminGuard` +- 公开接口使用 `@Public()` 装饰器 + +### **4. 错误处理** +- 使用统一的错误码和响应格式 +- 前台用户只能访问自己的数据 +- 后台管理员可以访问所有数据 + +### **5. 代码质量** +- 遵循TypeScript严格模式 +- 使用ESLint和Prettier规范 +- 编写完整的JSDoc注释 +- 实现单元测试 + +### **6. 业务逻辑完整性** +- **禁止TODO标记**:所有方法必须完整实现 +- **禁止Mock数据**:必须使用真实数据库数据 +- **禁止空实现**:所有方法必须包含完整业务逻辑 +- **禁止硬编码**:配置值必须从配置文件读取 + +## ⚠️ **禁止事项** + +1. **禁止硬编码**:所有配置值必须从配置文件或环境变量读取 +2. **禁止Mock数据**:生产环境必须使用真实数据库,禁止返回硬编码的测试数据 +3. **禁止TODO标记**:所有业务逻辑必须完整实现,不允许留下TODO注释 +4. **禁止混合职责**:每个服务层必须职责单一 +5. **禁止跳过验证**:所有输入必须进行验证 +6. **禁止忽略错误**:必须正确处理所有异常情况 +7. **禁止空实现**:所有方法必须包含完整的业务逻辑实现 + +--- + +**注意:AI开发时必须严格遵循此规范,确保代码质量和架构一致性!** \ No newline at end of file diff --git a/admin/apps/web-ele/src/api/README.md b/admin/apps/web-ele/src/api/README.md new file mode 100644 index 0000000..bc43239 --- /dev/null +++ b/admin/apps/web-ele/src/api/README.md @@ -0,0 +1,118 @@ +# WWJ Cloud 前端API使用说明 + +## 📁 目录结构 + +``` +src/api/ +├── core/ # 核心API(通用功能) +├── frontend/ # 前台API(前台用户访问) +│ ├── member.ts # 前台会员API +│ ├── site.ts # 前台站点API +│ └── index.ts # 前台API统一导出 +├── admin/ # 后台管理API(后台管理员访问) +│ ├── member.ts # 后台会员管理API +│ └── index.ts # 后台管理API统一导出 +└── index.ts # 主API统一导出 +``` + +## 🎯 使用方式 + +### 前台API(前台用户访问) + +```typescript +import { getMemberProfile, memberSignIn, getSiteInfo } from '@/api/frontend'; + +// 获取个人资料 +const profile = await getMemberProfile(); + +// 会员签到 +const signResult = await memberSignIn(); + +// 获取站点信息 +const siteInfo = await getSiteInfo(); +``` + +### 后台管理API(后台管理员访问) + +```typescript +import { getMemberList, createMember, updateMember } from '@/api/admin'; + +// 获取会员列表 +const memberList = await getMemberList({ page: 1, limit: 10 }); + +// 创建会员 +const newMember = await createMember({ + username: 'test', + nickname: '测试用户', + mobile: '13800138000', + email: 'test@example.com', + password: '123456', + levelId: 1, + status: 1 +}); + +// 更新会员 +const updatedMember = await updateMember(1, { + nickname: '新昵称', + status: 0 +}); +``` + +## 🔄 OpenAPI自动同步 + +### 生成前台API类型 +```bash +npm run openapi:gen:frontend +``` + +### 生成后台管理API类型 +```bash +npm run openapi:gen:admin +``` + +### 生成统一API类型 +```bash +npm run openapi:gen +``` + +## 📋 API分类说明 + +### 前台API(/api/*) +- **会员相关**:个人资料、签到、等级、积分等 +- **站点相关**:站点信息、配置、公告等 +- **菜单相关**:前台菜单、权限等 +- **角色相关**:前台角色、权限等 + +### 后台管理API(/admin/*) +- **会员管理**:会员列表、创建、编辑、删除等 +- **站点管理**:站点配置、管理、设置等 +- **菜单管理**:后台菜单、权限配置等 +- **角色管理**:后台角色、权限分配等 + +## 🛡️ 认证和权限 + +### 前台API认证 +- 使用JWT Token认证 +- 用户只能访问自己的数据 +- 支持公开接口(无需认证) + +### 后台管理API认证 +- 使用JWT Token + 管理员权限认证 +- 管理员可以访问所有数据 +- 严格的权限控制 + +## 📝 注意事项 + +1. **类型安全**:所有API都使用TypeScript类型定义 +2. **错误处理**:统一的错误处理机制 +3. **数据验证**:前后端数据验证一致 +4. **多租户**:支持SaaS多租户和独立版部署 +5. **自动同步**:OpenAPI自动生成前端类型定义 + +## 🚀 开发建议 + +1. **优先使用前台API**:前台用户功能优先使用前台API +2. **合理使用后台API**:管理功能使用后台管理API +3. **保持类型同步**:定期运行OpenAPI生成命令 +4. **遵循命名规范**:API路径和函数名保持一致 +5. **测试覆盖**:为所有API编写测试用例 \ No newline at end of file diff --git a/admin/apps/web-ele/src/api/admin/index.ts b/admin/apps/web-ele/src/api/admin/index.ts new file mode 100644 index 0000000..f9ef935 --- /dev/null +++ b/admin/apps/web-ele/src/api/admin/index.ts @@ -0,0 +1,5 @@ +/** + * 后台管理API统一导出 + */ + +export * from './member'; \ No newline at end of file diff --git a/admin/apps/web-ele/src/api/admin/member.ts b/admin/apps/web-ele/src/api/admin/member.ts new file mode 100644 index 0000000..61a31be --- /dev/null +++ b/admin/apps/web-ele/src/api/admin/member.ts @@ -0,0 +1,102 @@ +/** + * 后台会员管理API服务 + * 后台管理员访问的会员管理接口 + */ + +import { request } from '../request'; +import type { + AdminMember, + AdminMemberLevel, + AdminMemberQuery, + AdminMemberCreate, + AdminMemberUpdate, + PaginatedResult +} from '../../../types/admin-api'; + +// 后台会员管理API基础路径 +const ADMIN_MEMBER_API = '/admin/member'; + +/** + * 获取会员列表 + */ +export function getMemberList(params: AdminMemberQuery) { + return request>({ + url: ADMIN_MEMBER_API, + method: 'GET', + params, + }); +} + +/** + * 获取会员详情 + */ +export function getMemberDetail(id: number) { + return request({ + url: `${ADMIN_MEMBER_API}/${id}`, + method: 'GET', + }); +} + +/** + * 创建会员 + */ +export function createMember(data: AdminMemberCreate) { + return request({ + url: ADMIN_MEMBER_API, + method: 'POST', + data, + }); +} + +/** + * 更新会员 + */ +export function updateMember(id: number, data: AdminMemberUpdate) { + return request({ + url: `${ADMIN_MEMBER_API}/${id}`, + method: 'PUT', + data, + }); +} + +/** + * 删除会员 + */ +export function deleteMember(id: number) { + return request({ + url: `${ADMIN_MEMBER_API}/${id}`, + method: 'DELETE', + }); +} + +/** + * 批量删除会员 + */ +export function batchDeleteMembers(ids: number[]) { + return request({ + url: `${ADMIN_MEMBER_API}/batch`, + method: 'DELETE', + data: { ids }, + }); +} + +/** + * 获取会员等级列表 + */ +export function getMemberLevelList() { + return request({ + url: `${ADMIN_MEMBER_API}/levels`, + method: 'GET', + }); +} + +/** + * 更新会员状态 + */ +export function updateMemberStatus(id: number, status: number) { + return request({ + url: `${ADMIN_MEMBER_API}/${id}/status`, + method: 'PATCH', + data: { status }, + }); +} \ No newline at end of file diff --git a/admin/apps/web-ele/src/api/frontend/index.ts b/admin/apps/web-ele/src/api/frontend/index.ts new file mode 100644 index 0000000..3f50992 --- /dev/null +++ b/admin/apps/web-ele/src/api/frontend/index.ts @@ -0,0 +1,6 @@ +/** + * 前台API统一导出 + */ + +export * from './member'; +export * from './site'; \ No newline at end of file diff --git a/admin/apps/web-ele/src/api/frontend/member.ts b/admin/apps/web-ele/src/api/frontend/member.ts new file mode 100644 index 0000000..678216c --- /dev/null +++ b/admin/apps/web-ele/src/api/frontend/member.ts @@ -0,0 +1,62 @@ +/** + * 前台会员API服务 + * 前台用户访问的会员相关接口 + */ + +import { request } from '../request'; +import type { FrontendMemberProfile, FrontendMemberSignResult } from '../../../types/frontend-api'; + +// 前台会员API基础路径 +const FRONTEND_MEMBER_API = '/api/member'; + +/** + * 获取个人资料 + */ +export function getMemberProfile() { + return request({ + url: `${FRONTEND_MEMBER_API}/profile`, + method: 'GET', + }); +} + +/** + * 更新个人资料 + */ +export function updateMemberProfile(data: Partial) { + return request({ + url: `${FRONTEND_MEMBER_API}/profile`, + method: 'PUT', + data, + }); +} + +/** + * 会员签到 + */ +export function memberSignIn() { + return request({ + url: `${FRONTEND_MEMBER_API}/sign`, + method: 'POST', + }); +} + +/** + * 获取会员等级信息 + */ +export function getMemberLevel() { + return request({ + url: `${FRONTEND_MEMBER_API}/level`, + method: 'GET', + }); +} + +/** + * 获取会员积分记录 + */ +export function getMemberPointsHistory(params: { page: number; limit: number }) { + return request({ + url: `${FRONTEND_MEMBER_API}/points/history`, + method: 'GET', + params, + }); +} \ No newline at end of file diff --git a/admin/apps/web-ele/src/api/frontend/site.ts b/admin/apps/web-ele/src/api/frontend/site.ts new file mode 100644 index 0000000..853c701 --- /dev/null +++ b/admin/apps/web-ele/src/api/frontend/site.ts @@ -0,0 +1,41 @@ +/** + * 前台站点API服务 + * 前台用户访问的站点相关接口 + */ + +import { request } from '../request'; +import type { FrontendSiteInfo } from '../../../types/frontend-api'; + +// 前台站点API基础路径 +const FRONTEND_SITE_API = '/api/site'; + +/** + * 获取站点信息 + */ +export function getSiteInfo() { + return request({ + url: `${FRONTEND_SITE_API}/info`, + method: 'GET', + }); +} + +/** + * 获取站点配置 + */ +export function getSiteConfig() { + return request({ + url: `${FRONTEND_SITE_API}/config`, + method: 'GET', + }); +} + +/** + * 获取站点公告 + */ +export function getSiteAnnouncements(params: { page: number; limit: number }) { + return request({ + url: `${FRONTEND_SITE_API}/announcements`, + method: 'GET', + params, + }); +} \ No newline at end of file diff --git a/admin/apps/web-ele/src/api/index.ts b/admin/apps/web-ele/src/api/index.ts index 4b0e041..70eb5e9 100644 --- a/admin/apps/web-ele/src/api/index.ts +++ b/admin/apps/web-ele/src/api/index.ts @@ -1 +1,3 @@ export * from './core'; +export * from './frontend'; +export * from './admin'; diff --git a/admin/src/types/admin-api.d.ts b/admin/src/types/admin-api.d.ts new file mode 100644 index 0000000..419be72 --- /dev/null +++ b/admin/src/types/admin-api.d.ts @@ -0,0 +1,207 @@ +/** + * 后台管理API类型定义 + * 由OpenAPI自动生成,请勿手动修改 + * 生成命令: npm run openapi:gen:admin + */ + +export interface AdminApiResponse { + code: number; + message: string; + data: T; + timestamp: string; + path: string; +} + +// 后台会员管理相关接口类型 +export interface AdminMember { + memberId: number; + memberNo: string; + siteId: number; + username: string; + nickname: string; + avatar: string; + mobile: string; + email: string; + status: number; + levelId: number; + points: number; + balance: number; + createTime: string; + updateTime: string; +} + +export interface AdminMemberLevel { + levelId: number; + siteId: number; + levelName: string; + levelIcon: string; + levelColor: string; + levelSort: number; + levelStatus: number; + levelDescription: string; + levelCondition: number; + levelDiscount: number; + levelPoints: number; + createTime: string; + updateTime: string; +} + +export interface AdminMemberQuery { + page: number; + limit: number; + keyword?: string; + status?: number; + levelId?: number; + startTime?: string; + endTime?: string; +} + +export interface AdminMemberCreate { + username: string; + nickname: string; + mobile: string; + email: string; + password: string; + levelId: number; + status: number; +} + +export interface AdminMemberUpdate { + nickname?: string; + avatar?: string; + mobile?: string; + email?: string; + levelId?: number; + status?: number; +} + +// 后台站点管理相关接口类型 +export interface AdminSite { + siteId: number; + siteName: string; + siteLogo: string; + siteFavicon: string; + siteDescription: string; + siteKeywords: string; + siteCopyright: string; + siteIcp: string; + siteStatus: number; + siteType: number; + siteDomain: string; + siteEmail: string; + sitePhone: string; + siteAddress: string; + createTime: string; + updateTime: string; +} + +export interface AdminSiteCreate { + siteName: string; + siteLogo: string; + siteFavicon: string; + siteDescription: string; + siteKeywords: string; + siteCopyright: string; + siteIcp: string; + siteType: number; + siteDomain: string; + siteEmail: string; + sitePhone: string; + siteAddress: string; +} + +export interface AdminSiteUpdate { + siteName?: string; + siteLogo?: string; + siteFavicon?: string; + siteDescription?: string; + siteKeywords?: string; + siteCopyright?: string; + siteIcp?: string; + siteType?: number; + siteDomain?: string; + siteEmail?: string; + sitePhone?: string; + siteAddress?: string; + siteStatus?: number; +} + +// 后台菜单管理相关接口类型 +export interface AdminMenu { + menuId: number; + siteId: number; + menuName: string; + menuUrl: string; + menuIcon: string; + parentId: number; + sort: number; + status: number; + menuType: number; + menuTarget: string; + menuPermission: string; + createTime: string; + updateTime: string; + children?: AdminMenu[]; +} + +export interface AdminMenuCreate { + menuName: string; + menuUrl: string; + menuIcon: string; + parentId: number; + sort: number; + status: number; + menuType: number; + menuTarget: string; + menuPermission: string; +} + +export interface AdminMenuUpdate { + menuName?: string; + menuUrl?: string; + menuIcon?: string; + parentId?: number; + sort?: number; + status?: number; + menuType?: number; + menuTarget?: string; + menuPermission?: string; +} + +// 后台角色管理相关接口类型 +export interface AdminRole { + roleId: number; + siteId: number; + roleName: string; + roleDescription: string; + roleStatus: number; + roleSort: number; + rolePermissions: string[]; + createTime: string; + updateTime: string; +} + +export interface AdminRoleCreate { + roleName: string; + roleDescription: string; + roleStatus: number; + roleSort: number; + rolePermissions: string[]; +} + +export interface AdminRoleUpdate { + roleName?: string; + roleDescription?: string; + roleStatus?: number; + roleSort?: number; + rolePermissions?: string[]; +} + +// 分页响应类型 +export interface PaginatedResult { + items: T[]; + total: number; + page: number; + limit: number; + totalPages: number; +} \ No newline at end of file diff --git a/admin/src/types/frontend-api.d.ts b/admin/src/types/frontend-api.d.ts new file mode 100644 index 0000000..83faadb --- /dev/null +++ b/admin/src/types/frontend-api.d.ts @@ -0,0 +1,70 @@ +/** + * 前台API类型定义 + * 由OpenAPI自动生成,请勿手动修改 + * 生成命令: npm run openapi:gen:frontend + */ + +export interface FrontendApiResponse { + code: number; + message: string; + data: T; + timestamp: string; + path: string; +} + +// 前台会员相关接口类型 +export interface FrontendMemberProfile { + memberId: number; + memberNo: string; + username: string; + nickname: string; + avatar: string; + mobile: string; + email: string; + status: number; + levelId: number; + points: number; + balance: number; + createTime: string; +} + +export interface FrontendMemberSignResult { + success: boolean; + message: string; + points: number; + signDate: string; +} + +// 前台站点相关接口类型 +export interface FrontendSiteInfo { + siteId: number; + siteName: string; + siteLogo: string; + siteFavicon: string; + siteDescription: string; + siteKeywords: string; + siteCopyright: string; + siteIcp: string; + siteStatus: number; +} + +// 前台菜单相关接口类型 +export interface FrontendMenu { + menuId: number; + menuName: string; + menuUrl: string; + menuIcon: string; + parentId: number; + sort: number; + status: number; + children?: FrontendMenu[]; +} + +// 前台角色相关接口类型 +export interface FrontendRole { + roleId: number; + roleName: string; + roleDescription: string; + status: number; + permissions: string[]; +} \ No newline at end of file diff --git a/lefthook.yml b/lefthook.yml new file mode 100644 index 0000000..3ac5730 --- /dev/null +++ b/lefthook.yml @@ -0,0 +1,42 @@ +# EXAMPLE USAGE: +# +# Refer for explanation to following link: +# https://lefthook.dev/configuration/ +# +# pre-push: +# jobs: +# - name: packages audit +# tags: +# - frontend +# - security +# run: yarn audit +# +# - name: gems audit +# tags: +# - backend +# - security +# run: bundle audit +# +# pre-commit: +# parallel: true +# jobs: +# - run: yarn eslint {staged_files} +# glob: "*.{js,ts,jsx,tsx}" +# +# - name: rubocop +# glob: "*.rb" +# exclude: +# - config/application.rb +# - config/routes.rb +# run: bundle exec rubocop --force-exclusion {all_files} +# +# - name: govet +# files: git ls-files -m +# glob: "*.go" +# run: go vet {files} +# +# - script: "hello.js" +# runner: node +# +# - script: "hello.go" +# runner: go run diff --git a/package.json b/package.json new file mode 100644 index 0000000..9cc9f33 --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "axios": "^1.11.0" + } +} diff --git a/sql/test-data.sql b/sql/test-data.sql new file mode 100644 index 0000000..3c99ae0 --- /dev/null +++ b/sql/test-data.sql @@ -0,0 +1,289 @@ +-- WWJ Cloud 核心模块测试数据 +-- 验证Admin、Member、RBAC、Auth模块的真实字段名和业务逻辑 + +-- ========================================== +-- 1. Admin模块测试数据 +-- ========================================== + +-- 插入测试管理员用户 +INSERT INTO `sys_user` ( + `username`, + `password`, + `real_name`, + `head_img`, + `last_ip`, + `last_time`, + `create_time`, + `login_count`, + `status`, + `is_del`, + `delete_time`, + `update_time` +) VALUES +('admin', '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', '超级管理员', '', '127.0.0.1', UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), 0, 1, 0, 0, UNIX_TIMESTAMP()), +('testadmin', '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', '测试管理员', '', '127.0.0.1', UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), 0, 1, 0, 0, UNIX_TIMESTAMP()), +('manager', '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', '运营经理', '', '127.0.0.1', UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), 0, 1, 0, 0, UNIX_TIMESTAMP()); + +-- 插入用户角色关联 +INSERT INTO `sys_user_role` ( + `uid`, + `site_id`, + `role_ids`, + `create_time`, + `is_admin`, + `status`, + `delete_time` +) VALUES +(1, 0, '1', UNIX_TIMESTAMP(), 1, 1, 0), +(2, 0, '2', UNIX_TIMESTAMP(), 0, 1, 0), +(3, 0, '3', UNIX_TIMESTAMP(), 0, 1, 0); + +-- 插入用户操作日志 +INSERT INTO `sys_user_log` ( + `ip`, + `site_id`, + `uid`, + `username`, + `operation`, + `url`, + `params`, + `type`, + `create_time` +) VALUES +('127.0.0.1', 0, 1, 'admin', '用户登录', '/auth/admin/login', '{"username":"admin"}', 'POST', UNIX_TIMESTAMP()), +('127.0.0.1', 0, 1, 'admin', '查看用户列表', '/adminapi/admin', '{"page":1,"limit":10}', 'GET', UNIX_TIMESTAMP()), +('127.0.0.1', 0, 2, 'testadmin', '用户登录', '/auth/admin/login', '{"username":"testadmin"}', 'POST', UNIX_TIMESTAMP()); + +-- ========================================== +-- 2. Member模块测试数据 +-- ========================================== + +-- 插入测试会员用户 +INSERT INTO `member` ( + `member_no`, + `pid`, + `site_id`, + `username`, + `mobile`, + `password`, + `nickname`, + `headimg`, + `member_level`, + `member_label`, + `wx_openid`, + `weapp_openid`, + `wx_unionid`, + `ali_openid`, + `douyin_openid`, + `register_channel`, + `register_type`, + `login_ip`, + `login_type`, + `login_channel`, + `login_count`, + `login_time`, + `create_time`, + `last_visit_time`, + `last_consum_time`, + `sex`, + `status`, + `birthday`, + `id_card`, + `point`, + `point_get`, + `balance`, + `balance_get`, + `money`, + `money_get`, + `money_cash_outing`, + `growth`, + `growth_get`, + `commission`, + `commission_get`, + `commission_cash_outing`, + `is_member`, + `member_time`, + `is_del`, + `province_id`, + `city_id`, + `district_id`, + `address`, + `location`, + `remark`, + `delete_time`, + `update_time` +) VALUES +('M001', 0, 0, 'member', '13800138000', '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', '测试会员', '', 1, 'VIP', '', '', '', '', '', 'H5', 'password', '127.0.0.1', 'h5', '', 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), 0, 1, 1, '', '', 100, 100, 1000.00, 1000.00, 500.00, 500.00, 0.00, 50, 50, 0.00, 0.00, 0.00, 1, UNIX_TIMESTAMP(), 0, 0, 0, 0, '', '', '', 0, UNIX_TIMESTAMP()), +('M002', 0, 0, 'testmember', '13800138001', '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', '普通会员', '', 0, '普通', '', '', '', '', '', 'H5', 'password', '127.0.0.1', 'h5', '', 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), 0, 1, 1, '', '', 50, 50, 500.00, 500.00, 200.00, 200.00, 0.00, 20, 20, 0.00, 0.00, 0.00, 0, 0, 0, 0, 0, 0, '', '', '', 0, UNIX_TIMESTAMP()), +('M003', 0, 0, 'vipmember', '13800138002', '$2b$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', 'VIP会员', '', 2, '钻石', '', '', '', '', '', 'H5', 'password', '127.0.0.1', 'h5', '', 0, UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), 0, 1, 1, '', '', 500, 500, 5000.00, 5000.00, 2000.00, 2000.00, 0.00, 200, 200, 100.00, 100.00, 0.00, 1, UNIX_TIMESTAMP(), 0, 0, 0, 0, '', '', '', 0, UNIX_TIMESTAMP()); + +-- 插入会员地址 +INSERT INTO `member_address` ( + `member_id`, + `site_id`, + `name`, + `mobile`, + `province_id`, + `city_id`, + `district_id`, + `address`, + `address_name`, + `full_address`, + `lng`, + `lat`, + `is_default` +) VALUES +(1, 0, '张三', '13800138000', 110000, 110100, 110101, '朝阳区建国路88号', '家', '北京市朝阳区建国路88号', '116.4074', '39.9042', 1), +(1, 0, '张三', '13800138000', 110000, 110100, 110102, '西城区西单大街1号', '公司', '北京市西城区西单大街1号', '116.3741', '39.9139', 0), +(2, 0, '李四', '13800138001', 310000, 310100, 310101, '黄浦区南京东路1号', '家', '上海市黄浦区南京东路1号', '121.4737', '31.2304', 1); + +-- 插入会员等级 +INSERT INTO `member_level` ( + `level_id`, + `site_id`, + `level_name`, + `level_weight`, + `level_icon`, + `level_bg_color`, + `level_text_color`, + `level_condition`, + `level_discount`, + `level_point_rate`, + `level_description`, + `status`, + `create_time`, + `update_time` +) VALUES +(1, 0, '普通会员', 0, '', '#FFFFFF', '#000000', 0, 100, 1, '新注册用户', 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()), +(2, 0, 'VIP会员', 1, '', '#FFD700', '#000000', 1000, 95, 1.2, '消费满1000元', 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()), +(3, 0, '钻石会员', 2, '', '#C0C0C0', '#000000', 5000, 90, 1.5, '消费满5000元', 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()); + +-- ========================================== +-- 3. RBAC模块测试数据 +-- ========================================== + +-- 插入角色 +INSERT INTO `sys_role` ( + `role_id`, + `site_id`, + `role_name`, + `rules`, + `status`, + `create_time`, + `update_time` +) VALUES +(1, 0, '超级管理员', '1,2,3,4,5,6,7,8,9,10', 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()), +(2, 0, '运营管理员', '1,2,3,4,5', 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()), +(3, 0, '内容管理员', '1,2,3', 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()), +(4, 0, '财务管理员', '1,2,6,7', 1, UNIX_TIMESTAMP(), UNIX_TIMESTAMP()); + +-- 插入菜单 +INSERT INTO `sys_menu` ( + `id`, + `app_type`, + `menu_name`, + `menu_short_name`, + `menu_key`, + `parent_key`, + `menu_type`, + `icon`, + `api_url`, + `router_path`, + `view_path`, + `methods`, + `sort`, + `status`, + `is_show`, + `create_time`, + `delete_time`, + `addon`, + `source`, + `menu_attr`, + `parent_select_key` +) VALUES +(1, 'admin', '系统管理', '系统', 'system', '', 0, 'setting', '', '/system', 'system/index', '', 1, 1, 1, UNIX_TIMESTAMP(), 0, '', 'system', 'system', ''), +(2, 'admin', '用户管理', '用户', 'user', 'system', 1, 'user', '/adminapi/admin', '/system/user', 'system/user/index', 'GET,POST,PUT,DELETE', 1, 1, 1, UNIX_TIMESTAMP(), 0, '', 'system', 'system', 'system'), +(3, 'admin', '角色管理', '角色', 'role', 'system', 1, 'team', '/adminapi/role', '/system/role', 'system/role/index', 'GET,POST,PUT,DELETE', 2, 1, 1, UNIX_TIMESTAMP(), 0, '', 'system', 'system', 'system'), +(4, 'admin', '菜单管理', '菜单', 'menu', 'system', 1, 'menu', '/adminapi/menu', '/system/menu', 'system/menu/index', 'GET,POST,PUT,DELETE', 3, 1, 1, UNIX_TIMESTAMP(), 0, '', 'system', 'system', 'system'), +(5, 'admin', '会员管理', '会员', 'member', '', 0, 'user', '', '/member', 'member/index', '', 2, 1, 1, UNIX_TIMESTAMP(), 0, '', 'system', 'system', ''), +(6, 'admin', '会员列表', '列表', 'member_list', 'member', 1, 'table', '/adminapi/member', '/member/list', 'member/list/index', 'GET,POST,PUT,DELETE', 1, 1, 1, UNIX_TIMESTAMP(), 0, '', 'system', 'system', 'member'), +(7, 'admin', '会员等级', '等级', 'member_level', 'member', 1, 'star', '/adminapi/member-level', '/member/level', 'member/level/index', 'GET,POST,PUT,DELETE', 2, 1, 1, UNIX_TIMESTAMP(), 0, '', 'system', 'system', 'member'), +(8, 'admin', '财务管理', '财务', 'finance', '', 0, 'money-collect', '', '/finance', 'finance/index', '', 3, 1, 1, UNIX_TIMESTAMP(), 0, '', 'system', 'system', ''), +(9, 'admin', '收入统计', '收入', 'income', 'finance', 1, 'rise', '/adminapi/finance/income', '/finance/income', 'finance/income/index', 'GET', 1, 1, 1, UNIX_TIMESTAMP(), 0, '', 'system', 'system', 'finance'), +(10, 'admin', '支出统计', '支出', 'expense', 'finance', 1, 'fall', '/adminapi/finance/expense', '/finance/expense', 'finance/expense/index', 'GET', 2, 1, 1, UNIX_TIMESTAMP(), 0, '', 'system', 'system', 'finance'); + +-- ========================================== +-- 4. Auth模块测试数据 +-- ========================================== + +-- 创建auth_token表(如果不存在) +CREATE TABLE IF NOT EXISTS `auth_token` ( + `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键', + `token` varchar(500) NOT NULL COMMENT 'JWT Token', + `user_id` int(11) NOT NULL COMMENT '用户ID', + `user_type` varchar(20) NOT NULL COMMENT '用户类型:admin/member', + `site_id` int(11) NOT NULL DEFAULT 0 COMMENT '站点ID,0为独立版', + `expires_at` datetime NOT NULL COMMENT '过期时间', + `refresh_token` varchar(500) DEFAULT NULL COMMENT '刷新Token', + `refresh_expires_at` datetime DEFAULT NULL COMMENT '刷新Token过期时间', + `ip_address` varchar(45) DEFAULT NULL COMMENT 'IP地址', + `user_agent` varchar(500) DEFAULT NULL COMMENT '用户代理', + `device_type` varchar(20) DEFAULT NULL COMMENT '设备类型:web/mobile/app', + `is_revoked` tinyint(4) NOT NULL DEFAULT 0 COMMENT '是否已撤销:0未撤销,1已撤销', + `revoked_at` datetime DEFAULT NULL COMMENT '撤销时间', + `revoked_reason` varchar(200) DEFAULT NULL COMMENT '撤销原因', + `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_token` (`token`), + KEY `idx_user_type` (`user_id`,`user_type`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='认证Token表'; + +-- 插入测试Token记录 +INSERT INTO `auth_token` ( + `token`, + `user_id`, + `user_type`, + `site_id`, + `expires_at`, + `refresh_token`, + `refresh_expires_at`, + `ip_address`, + `user_agent`, + `device_type`, + `is_revoked`, + `revoked_at`, + `revoked_reason` +) VALUES +('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.test_admin_token', 1, 'admin', 0, DATE_ADD(NOW(), INTERVAL 7 DAY), 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.test_admin_refresh', DATE_ADD(NOW(), INTERVAL 30 DAY), '127.0.0.1', 'Mozilla/5.0', 'web', 0, NULL, NULL), +('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.test_member_token', 1, 'member', 0, DATE_ADD(NOW(), INTERVAL 7 DAY), 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.test_member_refresh', DATE_ADD(NOW(), INTERVAL 30 DAY), '127.0.0.1', 'Mozilla/5.0', 'web', 0, NULL, NULL); + +-- ========================================== +-- 5. 验证数据插入结果 +-- ========================================== + +-- 查询验证Admin模块数据 +SELECT 'Admin模块数据验证' as module, COUNT(*) as count FROM `sys_user` WHERE `is_del` = 0; +SELECT 'Admin角色关联验证' as module, COUNT(*) as count FROM `sys_user_role` WHERE `delete_time` = 0; +SELECT 'Admin操作日志验证' as module, COUNT(*) as count FROM `sys_user_log`; + +-- 查询验证Member模块数据 +SELECT 'Member模块数据验证' as module, COUNT(*) as count FROM `member` WHERE `is_del` = 0; +SELECT 'Member地址验证' as module, COUNT(*) as count FROM `member_address`; +SELECT 'Member等级验证' as module, COUNT(*) as count FROM `member_level`; + +-- 查询验证RBAC模块数据 +SELECT 'RBAC角色验证' as module, COUNT(*) as count FROM `sys_role`; +SELECT 'RBAC菜单验证' as module, COUNT(*) as count FROM `sys_menu`; + +-- 查询验证Auth模块数据 +SELECT 'Auth Token验证' as module, COUNT(*) as count FROM `auth_token` WHERE `is_revoked` = 0; + +-- 显示测试数据概览 +SELECT + '数据概览' as info, + (SELECT COUNT(*) FROM `sys_user` WHERE `is_del` = 0) as admin_count, + (SELECT COUNT(*) FROM `member` WHERE `is_del` = 0) as member_count, + (SELECT COUNT(*) FROM `sys_role`) as role_count, + (SELECT COUNT(*) FROM `sys_menu`) as menu_count, + (SELECT COUNT(*) FROM `auth_token` WHERE `is_revoked` = 0) as token_count; \ No newline at end of file