# 后端模块重构实施指南 ## 概述 本指南提供了完整的后端模块重构步骤和代码示例。重构涉及: - 创建 50+ 个新文件 - 修改所有 Controller 的路由前缀 - 更新前端 API 调用路径 - 创建新的模块组织结构 ## 一、快速开始 ### 方案选择 **方案A:手动逐步执行**(推荐,风险低) - 按照本文档逐个模块迁移 - 每完成一个端就测试验证 - 适合希望完全掌控过程的团队 **方案B:使用自动化脚本**(快速,需要验证) - 使用提供的 Node.js 脚本批量迁移 - 完成后统一测试和修复 - 适合有经验的团队 **方案C:AI辅助完成**(平衡) - AI 创建关键示例和模板 - 人工复制粘贴并调整其他模块 - 本文档提供所有必要的模板 ## 二、目录结构 ``` apps/server/src/modules/ ├── user/ # 用户端(C端) │ ├── auth/ │ │ ├── auth.controller.ts │ │ ├── auth.service.ts │ │ ├── auth.module.ts │ │ └── dto/ │ ├── profile/ │ ├── guest/ │ ├── order/ │ ├── review/ │ ├── coupon/ │ ├── finance/ │ ├── activity/ │ └── user.module.ts # 用户端总模块 │ ├── merchant/ # 商家端(B端) │ ├── auth/ │ ├── profile/ │ ├── room/ │ ├── room-calendar/ │ ├── order/ │ ├── review/ │ ├── finance/ │ ├── statistics/ │ └── merchant.module.ts # 商家端总模块 │ ├── admin/ # 平台管理端 │ ├── auth/ │ ├── user/ │ ├── merchant/ │ ├── room/ │ ├── order/ │ ├── review/ │ ├── coupon/ │ ├── activity/ │ ├── config/ │ ├── finance/ │ ├── website/ │ └── admin.module.ts # 管理端总模块 │ ├── website/ # 官网 │ ├── info/ │ └── website.module.ts │ └── shared/ # 公共模块 ├── room/ ├── merchant/ ├── activity/ ├── upload/ └── shared.module.ts ``` ## 三、路由前缀映射表 ### 用户端 | 旧路由 | 新路由 | 说明 | |--------|--------|------| | `/auth/*` | `/api/user/auth/*` | 用户认证 | | `/user/*` | `/api/user/profile/*` | 个人信息 | | `/user/guests` | `/api/user/guests` | 入住人管理 | | `/orders` | `/api/user/orders` | 用户订单 | | `/reviews` | `/api/user/reviews` | 用户评价 | | `/user/coupons` | `/api/user/coupons` | 用户优惠券 | | `/user/finance` | `/api/user/finance` | 用户财务 | | `/user/activity/invite` | `/api/user/activity/invite` | 邀请活动 | ### 商家端 | 旧路由 | 新路由 | 说明 | |--------|--------|------| | `/seller/auth/*` | `/api/merchant/auth/*` | 商家认证 | | `/seller/merchant` | `/api/merchant/profile` | 商家信息 | | `/seller/statistics` | `/api/merchant/statistics` | 数据统计 | | `/seller/rooms` | `/api/merchant/rooms` | 房源管理 | | `/seller/room-calendar` | `/api/merchant/room-calendar` | 房量房价 | | `/seller/orders` | `/api/merchant/orders` | 商家订单 | | `/seller/reviews` | `/api/merchant/reviews` | 商家评价 | | `/merchant/finance/*` | `/api/merchant/finance/*` | 商家财务 | ### 平台管理端 | 旧路由 | 新路由 | 说明 | |--------|--------|------| | `/admin/auth/*` | `/api/admin/auth/*` | 管理员认证 | | `/admin/users` | `/api/admin/users` | 用户管理 | | `/admin/merchants` | `/api/admin/merchants` | 商家管理 | | `/admin/rooms` | `/api/admin/rooms` | 房源审核 | | `/admin/orders` | `/api/admin/orders` | 订单管理 | | `/admin/reviews` | `/api/admin/reviews` | 评价审核 | | `/admin/coupons` | `/api/admin/coupons` | 优惠券管理 | | `/admin/activity` | `/api/admin/activity` | 活动管理 | | `/admin/config` | `/api/admin/config` | 系统配置 | | `/admin/finance/*` | `/api/admin/finance/*` | 财务管理 | ### 公共接口 | 旧路由 | 新路由 | 说明 | |--------|--------|------| | `/rooms` | `/api/public/rooms` | 房源公开查询 | | `/merchants` | `/api/public/merchants` | 商家公开信息 | | `/activity` | `/api/public/activity` | 活动公开信息 | | `/upload` | `/api/user/upload` | 用户上传 | | `/seller/upload` | `/api/merchant/upload` | 商家上传 | | `/admin/upload` | `/api/admin/upload` | 管理员上传 | ## 四、代码模板 ### 1. Controller 模板(用户端认证示例) **文件位置**: `apps/server/src/modules/user/auth/auth.controller.ts` ```typescript import { Controller, Post, Body, UseGuards, Get, Req } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger'; import { AuthService } from './auth.service'; import { LoginByPhoneDto, LoginByPasswordDto, RegisterDto, SendCodeDto, WechatLoginDto, } from './dto/auth.dto'; import { JwtAuthGuard } from '@/common/guards/jwt-auth.guard'; @ApiTags('用户认证') @Controller('api/user/auth') // ⚠️ 关键:新的路由前缀 export class AuthController { constructor(private readonly authService: AuthService) {} @Post('send-code') @ApiOperation({ summary: '发送验证码' }) async sendCode(@Body() dto: SendCodeDto) { return this.authService.sendCode(dto.phone); } @Post('login/phone') @ApiOperation({ summary: '手机号验证码登录' }) async loginByPhone(@Body() dto: LoginByPhoneDto) { return this.authService.loginByPhone(dto); } @Post('login/password') @ApiOperation({ summary: '账号密码登录' }) async loginByPassword(@Body() dto: LoginByPasswordDto) { return this.authService.loginByPassword(dto); } @Post('login/wechat') @ApiOperation({ summary: '微信授权登录' }) async loginByWechat(@Body() dto: WechatLoginDto) { return this.authService.loginByWechat(dto); } @Post('register') @ApiOperation({ summary: '用户注册' }) async register(@Body() dto: RegisterDto) { return this.authService.register(dto); } @Post('refresh') @ApiOperation({ summary: '刷新令牌' }) async refresh(@Body('refreshToken') refreshToken: string) { return this.authService.refresh(refreshToken); } @Get('profile') @UseGuards(JwtAuthGuard) @ApiBearerAuth() @ApiOperation({ summary: '获取当前用户信息' }) async getProfile(@Req() req) { return req.user; } } ``` ### 2. Module 模板(子模块) **文件位置**: `apps/server/src/modules/user/auth/auth.module.ts` ```typescript import { Module, Global } from '@nestjs/common'; import { JwtModule } from '@nestjs/jwt'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { TypeOrmModule } from '@nestjs/typeorm'; import { AuthController } from './auth.controller'; import { AuthService } from './auth.service'; import { User } from '@/entities/user.entity'; import { UserAccount } from '@/entities/user-account.entity'; @Global() @Module({ imports: [ TypeOrmModule.forFeature([User, UserAccount]), JwtModule.registerAsync({ imports: [ConfigModule], useFactory: (configService: ConfigService) => ({ secret: configService.get('jwt.secret') || 'dev_secret_key', signOptions: { expiresIn: configService.get('jwt.expiresIn') || '7d', }, }), inject: [ConfigService], }), ], controllers: [AuthController], providers: [AuthService], exports: [AuthService, JwtModule], }) export class UserAuthModule {} // ⚠️ 重命名避免冲突 ``` ### 3. 端总模块模板 **文件位置**: `apps/server/src/modules/user/user.module.ts` ```typescript import { Module } from '@nestjs/common'; import { UserAuthModule } from './auth/auth.module'; import { UserProfileModule } from './profile/profile.module'; import { UserGuestModule } from './guest/guest.module'; import { UserOrderModule } from './order/order.module'; import { UserReviewModule } from './review/review.module'; import { UserCouponModule } from './coupon/coupon.module'; import { UserFinanceModule } from './finance/finance.module'; import { UserActivityModule } from './activity/activity.module'; @Module({ imports: [ UserAuthModule, UserProfileModule, UserGuestModule, UserOrderModule, UserReviewModule, UserCouponModule, UserFinanceModule, UserActivityModule, ], }) export class UserModule {} ``` ### 4. 根模块更新 **文件位置**: `apps/server/src/app.module.ts` ```typescript import { Module } from '@nestjs/common'; import { ConfigModule } from '@nestjs/config'; import { TypeOrmModule } from '@nestjs/typeorm'; import { UserModule } from './modules/user/user.module'; import { MerchantModule } from './modules/merchant/merchant.module'; import { AdminModule } from './modules/admin/admin.module'; import { WebsiteModule } from './modules/website/website.module'; import { SharedModule } from './modules/shared/shared.module'; // ... 其他导入 @Module({ imports: [ ConfigModule.forRoot(/* ... */), TypeOrmModule.forRoot(/* ... */), UserModule, // 用户端 MerchantModule, // 商家端 AdminModule, // 管理端 WebsiteModule, // 官网 SharedModule, // 公共模块 // 删除旧的模块导入 ], }) export class AppModule {} ``` ## 五、前端 API 路径更新 ### 小程序 (miniapp) **文件**: `apps/miniapp/src/api/auth.ts` ```typescript // 旧代码 export function login(data: any) { return request.post('/auth/login/phone', data); } // 新代码 export function login(data: any) { return request.post('/api/user/auth/login/phone', data); } ``` ### 批量替换规则(使用 VS Code 全局搜索替换) **用户端接口**: - 搜索: `'/auth/` - 替换: `'/api/user/auth/` - 搜索: `'/user/` - 替换: `'/api/user/profile/` - 搜索: `'/orders` - 替换: `'/api/user/orders` **商家端接口**: - 搜索: `'/seller/auth/` - 替换: `'/api/merchant/auth/` - 搜索: `'/seller/` - 替换: `'/api/merchant/` - 搜索: `'/merchant/finance/` - 替换: `'/api/merchant/finance/` **管理端接口**: - 搜索: `'/admin/` - 替换: `'/api/admin/` **公共接口**: - 搜索: `'/rooms` - 替换: `'/api/public/rooms` - 搜索: `'/merchants` - 替换: `'/api/public/merchants` ## 六、官网模块实现 ### 1. 创建 WebsiteInfo 实体 **文件**: `apps/server/src/entities/website-info.entity.ts` ```typescript import { Entity, PrimaryGeneratedColumn, Column, CreateDateColumn, UpdateDateColumn, } from 'typeorm'; @Entity('website_info') export class WebsiteInfo { @PrimaryGeneratedColumn() id: number; @Column({ name: 'site_name', length: 100 }) siteName: string; @Column({ name: 'site_title', length: 200 }) siteTitle: string; @Column({ name: 'site_description', type: 'text' }) siteDescription: string; @Column({ name: 'site_keywords', length: 500 }) siteKeywords: string; @Column({ length: 500 }) logo: string; @Column({ length: 500 }) favicon: string; @Column({ name: 'contact_phone', length: 50 }) contactPhone: string; @Column({ name: 'contact_email', length: 100 }) contactEmail: string; @Column({ name: 'contact_address', length: 500 }) contactAddress: string; @Column({ length: 100 }) icp: string; @Column({ type: 'text' }) copyright: string; @Column({ name: 'about_us', type: 'text' }) aboutUs: string; @Column({ name: 'service_agreement', type: 'text' }) serviceAgreement: string; @Column({ name: 'privacy_policy', type: 'text' }) privacyPolicy: string; @Column({ name: 'social_links', type: 'json', nullable: true }) socialLinks: Record; @Column({ type: 'enum', enum: ['active', 'inactive'], default: 'active', }) status: 'active' | 'inactive'; @CreateDateColumn({ name: 'created_at' }) createdAt: Date; @UpdateDateColumn({ name: 'updated_at' }) updatedAt: Date; } ``` ### 2. 数据库迁移脚本 **文件**: `database/migrations/002_create_website_info.sql` ```sql CREATE TABLE IF NOT EXISTS `website_info` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, `site_name` VARCHAR(100) NOT NULL COMMENT '网站名称', `site_title` VARCHAR(200) NOT NULL COMMENT '网站标题', `site_description` TEXT NOT NULL COMMENT '网站描述', `site_keywords` VARCHAR(500) NOT NULL COMMENT 'SEO关键词', `logo` VARCHAR(500) NOT NULL COMMENT 'Logo URL', `favicon` VARCHAR(500) NOT NULL COMMENT '网站图标', `contact_phone` VARCHAR(50) NOT NULL COMMENT '联系电话', `contact_email` VARCHAR(100) NOT NULL COMMENT '联系邮箱', `contact_address` VARCHAR(500) NOT NULL COMMENT '联系地址', `icp` VARCHAR(100) NOT NULL COMMENT 'ICP备案号', `copyright` TEXT NOT NULL COMMENT '版权信息', `about_us` TEXT COMMENT '关于我们', `service_agreement` TEXT COMMENT '服务协议', `privacy_policy` TEXT COMMENT '隐私政策', `social_links` JSON COMMENT '社交媒体链接', `status` ENUM('active', 'inactive') DEFAULT 'active' COMMENT '状态', `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX `idx_status` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='网站信息表'; -- 插入默认数据 INSERT INTO `website_info` ( `site_name`, `site_title`, `site_description`, `site_keywords`, `logo`, `favicon`, `contact_phone`, `contact_email`, `contact_address`, `icp`, `copyright`, `about_us`, `service_agreement`, `privacy_policy` ) VALUES ( '租赁平台', '租赁平台 - 您的租赁服务专家', '提供优质的租赁服务', '租赁,房屋租赁,短租', '', '', '400-000-0000', 'contact@example.com', '北京市朝阳区', '京ICP备00000000号', '© 2024 租赁平台 版权所有', '关于我们的内容...', '服务协议内容...', '隐私政策内容...' ); ``` ### 3. WebsiteInfo Service **文件**: `apps/server/src/modules/website/info/info.service.ts` ```typescript import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { WebsiteInfo } from '@/entities/website-info.entity'; @Injectable() export class WebsiteInfoService { constructor( @InjectRepository(WebsiteInfo) private websiteInfoRepo: Repository, ) {} async getInfo(): Promise { // 获取第一条记录(通常只有一条) const info = await this.websiteInfoRepo.findOne({ where: { status: 'active' }, order: { id: 'ASC' }, }); if (!info) { throw new Error('网站信息未配置'); } return info; } async updateInfo(data: Partial): Promise { const info = await this.getInfo(); Object.assign(info, data); return this.websiteInfoRepo.save(info); } } ``` ### 4. WebsiteInfo Controller(公开接口) **文件**: `apps/server/src/modules/website/info/info.controller.ts` ```typescript import { Controller, Get } from '@nestjs/common'; import { ApiTags, ApiOperation } from '@nestjs/swagger'; import { WebsiteInfoService } from './info.service'; @ApiTags('网站信息') @Controller('api/website/info') export class WebsiteInfoController { constructor(private readonly websiteInfoService: WebsiteInfoService) {} @Get() @ApiOperation({ summary: '获取网站信息' }) async getInfo() { return this.websiteInfoService.getInfo(); } } ``` ### 5. WebsiteInfo 管理接口 **文件**: `apps/server/src/modules/admin/website/website.controller.ts` ```typescript import { Controller, Put, Body, UseGuards } from '@nestjs/common'; import { ApiTags, ApiOperation, ApiBearerAuth } from '@nestjs/swagger'; import { JwtAuthGuard } from '@/common/guards/jwt-auth.guard'; import { RolesGuard } from '@/common/guards/roles.guard'; import { Roles } from '@/common/decorators/roles.decorator'; import { WebsiteInfoService } from '@/modules/website/info/info.service'; @ApiTags('网站管理') @Controller('api/admin/website/info') @UseGuards(JwtAuthGuard, RolesGuard) @Roles('admin') @ApiBearerAuth() export class AdminWebsiteController { constructor(private readonly websiteInfoService: WebsiteInfoService) {} @Put() @ApiOperation({ summary: '更新网站信息' }) async updateInfo(@Body() data: any) { return this.websiteInfoService.updateInfo(data); } } ``` ## 七、执行步骤 ### 步骤1:创建目录结构 ```bash cd d:/project/company/rent/apps/server/src/modules mkdir -p user/{auth,profile,guest,order,review,coupon,finance,activity} mkdir -p merchant/{auth,profile,room,room-calendar,order,review,finance,statistics} mkdir -p admin/{auth,user,merchant,room,order,review,coupon,activity,config,finance,website} mkdir -p website/info mkdir -p shared/{room,merchant,activity,upload} ``` ### 步骤2:复制并修改文件 对于每个模块: 1. 复制原文件到新位置 2. 修改 Controller 的 `@Controller()` 装饰器路由前缀 3. 如果是 Module 文件,重命名类名避免冲突(如 `AuthModule` → `UserAuthModule`) 4. 复制 Service 和 DTO 文件(通常不需要修改) ### 步骤3:创建端总模块 为每个端创建总模块文件(user.module.ts, merchant.module.ts, admin.module.ts等) ### 步骤4:更新 app.module.ts 导入新的四个端模块,删除旧模块导入 ### 步骤5:创建官网模块 1. 创建 WebsiteInfo 实体 2. 运行数据库迁移 3. 创建 Service、Controller 4. 创建管理接口 ### 步骤6:更新前端 API 路径 使用 VS Code 全局搜索替换,按照第五节的规则批量替换 ### 步骤7:测试验证 1. 启动后端服务 2. 检查 Swagger 文档 3. 测试各端的核心接口 4. 启动三个前端应用测试 ### 步骤8:清理旧模块 确认新模块工作正常后,删除旧的模块文件 ## 八、常见问题 ### Q1: 模块导入路径错误 **问题**: Service 或 Entity 导入路径找不到 **解决**: 使用绝对路径 `@/entities/...` 或 `@/modules/...` ### Q2: 模块名称冲突 **问题**: 多个模块都叫 `AuthModule` **解决**: 重命名为 `UserAuthModule`, `MerchantAuthModule`, `AdminAuthModule` ### Q3: 前端接口404 **问题**: 前端调用新接口返回404 **解决**: 检查是否遗漏了某些路径的替换,特别注意动态路径 ### Q4: Guards 不生效 **问题**: 认证守卫没有正确应用 **解决**: 确保 JwtModule 在对应的 Auth Module 中正确导出 ## 九、验证清单 - [ ] 后端服务启动无错误 - [ ] Swagger 文档显示所有新接口 - [ ] 用户端接口测试通过(登录、下单、支付等) - [ ] 商家端接口测试通过(登录、房源管理、订单处理等) - [ ] 管理端接口测试通过(登录、审核、财务管理等) - [ ] 公共接口测试通过(房源列表、商家列表等) - [ ] 官网接口测试通过(获取网站信息) - [ ] 小程序正常运行 - [ ] 商家后台正常运行 - [ ] 平台后台正常运行 - [ ] 旧模块已删除 - [ ] 代码已提交到版本控制 ## 十、回滚方案 如果重构出现问题需要回滚: 1. 使用 git 恢复到重构前的提交 2. 或者保留旧模块,在 app.module.ts 中切换回旧模块 3. 前端恢复旧的 API 路径 建议:在开始重构前创建一个 git 分支,确保可以随时回滚。