Просмотр исходного кода

新增注册功能,更新会员控制器、服务和路由以支持用户注册,添加必要的字段验证和错误处理,同时扩展DTO以包含注册所需的字段。

wuyi 3 месяцев назад
Родитель
Сommit
fd5ed64f7d

+ 63 - 1
src/controllers/member.controller.ts

@@ -7,7 +7,8 @@ import {
   MemberResponse,
   UpdateGuestBody,
   MemberLoginBody,
-  ResetPasswordBody
+  ResetPasswordBody,
+  RegisterBody
 } from '../dto/member.dto'
 import { VipLevel, MemberStatus } from '../entities/member.entity'
 import { UserService } from '../services/user.service'
@@ -82,6 +83,67 @@ export class MemberController {
     }
   }
 
+  async register(request: FastifyRequest<{ Body: RegisterBody }>, reply: FastifyReply) {
+    try {
+      const { name, password, email, phone, code } = request.body
+
+      // 验证必填字段
+      if (!name || !password) {
+        return reply.code(400).send({ message: '用户名和密码为必填字段' })
+      }
+
+      // 验证用户名格式
+      if (name.length < 3 || name.length > 20) {
+        return reply.code(400).send({ message: '用户名长度必须在3-20个字符之间' })
+      }
+
+      // 验证密码格式
+      if (password.length < 6) {
+        return reply.code(400).send({ message: '密码长度不能少于6个字符' })
+      }
+
+      // 验证邮箱格式
+      if (email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
+        return reply.code(400).send({ message: '邮箱格式不正确' })
+      }
+
+      // 验证手机号格式
+      if (phone && !/^1[3-9]\d{9}$/.test(phone)) {
+        return reply.code(400).send({ message: '手机号格式不正确' })
+      }
+
+      // 获取客户端IP
+      const ip =
+        request.ip ||
+        (request.headers['x-forwarded-for'] as string) ||
+        (request.headers['x-real-ip'] as string) ||
+        'unknown'
+
+      // 调用注册服务
+      const { user, member } = await this.memberService.register(name, password, email, phone, code, ip)
+
+      // 生成JWT token
+      const token = await reply.jwtSign({ id: user.id, name: user.name, role: user.role })
+
+      return reply.code(201).send({
+        message: '注册成功',
+        user: {
+          id: user.id,
+          name: user.name,
+          role: user.role,
+          vipLevel: member.vipLevel
+        },
+        token
+      })
+    } catch (error) {
+      const errorMessage = error instanceof Error ? error.message : '注册失败'
+      if (errorMessage.includes('已被使用')) {
+        return reply.code(400).send({ message: errorMessage })
+      }
+      return reply.code(500).send({ message: '注册失败', error: errorMessage })
+    }
+  }
+
   async memberLogin(request: FastifyRequest<{ Body: MemberLoginBody }>, reply: FastifyReply) {
     try {
       const { name, password } = request.body

+ 52 - 16
src/controllers/sys-config.controller.ts

@@ -1,6 +1,14 @@
 import { FastifyRequest, FastifyReply, FastifyInstance } from 'fastify'
 import { SysConfigService } from '../services/sys-config.service'
-import { CreateSysConfigBody, UpdateSysConfigBody, ListSysConfigQuery, CreateTeamConfigBody, UpdateTeamConfigBody, ListTeamConfigQuery, GetTeamConfigParams } from '../dto/sys-config.dto'
+import {
+  CreateSysConfigBody,
+  UpdateSysConfigBody,
+  ListSysConfigQuery,
+  CreateTeamConfigBody,
+  UpdateTeamConfigBody,
+  ListTeamConfigQuery,
+  GetTeamConfigParams
+} from '../dto/sys-config.dto'
 import { UserRole } from '../entities/user.entity'
 
 export class SysConfigController {
@@ -21,7 +29,7 @@ export class SysConfigController {
     }
   }
 
-  async update(request: FastifyRequest<{ Params: { name: string }, Body: UpdateSysConfigBody }>, reply: FastifyReply) {
+  async update(request: FastifyRequest<{ Params: { name: string }; Body: UpdateSysConfigBody }>, reply: FastifyReply) {
     try {
       const config = await this.sysConfigService.update(request.params.name, request.body, request.body.teamId)
       return reply.send(config)
@@ -68,12 +76,15 @@ export class SysConfigController {
   }
 
   // 团队配置相关接口
-  async createTeamConfig(request: FastifyRequest<{ Body: CreateTeamConfigBody & { teamId?: number } }>, reply: FastifyReply) {
+  async createTeamConfig(
+    request: FastifyRequest<{ Body: CreateTeamConfigBody & { teamId?: number } }>,
+    reply: FastifyReply
+  ) {
     try {
       const { teamId, ...body } = request.body
       const userId = request.user.id
       const userRole = request.user.role
-      
+
       const config = await this.sysConfigService.createTeamConfig(body, userId, userRole, teamId)
       return reply.code(201).send(config)
     } catch (error) {
@@ -81,13 +92,16 @@ export class SysConfigController {
     }
   }
 
-  async updateTeamConfig(request: FastifyRequest<{ Params: { name: string }, Body: UpdateTeamConfigBody & { teamId?: number } }>, reply: FastifyReply) {
+  async updateTeamConfig(
+    request: FastifyRequest<{ Params: { name: string }; Body: UpdateTeamConfigBody & { teamId?: number } }>,
+    reply: FastifyReply
+  ) {
     try {
       const { name } = request.params
       const { teamId, ...body } = request.body
       const userId = request.user.id
       const userRole = request.user.role
-      
+
       const config = await this.sysConfigService.updateTeamConfig(name, body, userId, userRole, teamId)
       return reply.send(config)
     } catch (error) {
@@ -95,13 +109,16 @@ export class SysConfigController {
     }
   }
 
-  async deleteTeamConfig(request: FastifyRequest<{ Params: { name: string }, Body: { teamId?: number } }>, reply: FastifyReply) {
+  async deleteTeamConfig(
+    request: FastifyRequest<{ Params: { name: string }; Body: { teamId?: number } }>,
+    reply: FastifyReply
+  ) {
     try {
       const { name } = request.params
       const { teamId } = request.body
       const userId = request.user.id
       const userRole = request.user.role
-      
+
       await this.sysConfigService.deleteTeamConfig(name, userId, userRole, teamId)
       return reply.send({ success: true })
     } catch (error) {
@@ -109,27 +126,43 @@ export class SysConfigController {
     }
   }
 
-  async getTeamConfig(request: FastifyRequest<{ Params: { name: string }, Querystring: { teamId?: string } }>, reply: FastifyReply) {
+  async getTeamConfig(
+    request: FastifyRequest<{ Params: { name: string }; Querystring: { teamId?: string } }>,
+    reply: FastifyReply
+  ) {
     try {
       const { name } = request.params
       const { teamId } = request.query
       const userId = request.user.id
       const userRole = request.user.role
-      
-      const config = await this.sysConfigService.getTeamConfig(name, userId, userRole, teamId ? Number(teamId) : undefined)
+
+      const config = await this.sysConfigService.getTeamConfig(
+        name,
+        userId,
+        userRole,
+        teamId ? Number(teamId) : undefined
+      )
       return reply.send(config)
     } catch (error) {
       return reply.code(500).send(error)
     }
   }
 
-  async listTeamConfigs(request: FastifyRequest<{ Querystring: ListTeamConfigQuery & { teamId?: string } }>, reply: FastifyReply) {
+  async listTeamConfigs(
+    request: FastifyRequest<{ Querystring: ListTeamConfigQuery & { teamId?: string } }>,
+    reply: FastifyReply
+  ) {
     try {
       const { teamId, ...query } = request.query
       const userId = request.user.id
       const userRole = request.user.role
-      
-      const configs = await this.sysConfigService.listTeamConfigs(query, userId, userRole, teamId ? Number(teamId) : undefined)
+
+      const configs = await this.sysConfigService.listTeamConfigs(
+        query,
+        userId,
+        userRole,
+        teamId ? Number(teamId) : undefined
+      )
       return reply.send(configs)
     } catch (error) {
       return reply.code(500).send(error)
@@ -138,11 +171,14 @@ export class SysConfigController {
 
   async getUserTeamConfigs(request: FastifyRequest, reply: FastifyReply) {
     try {
-      const userId = request.user.id
+      let userId = undefined
+      if (request.user) {
+        userId = request.user.id
+      }
       const configs = await this.sysConfigService.getUserTeamConfigs(userId)
       return reply.send(configs)
     } catch (error) {
       return reply.code(500).send(error)
     }
   }
-} 
+}

+ 8 - 0
src/dto/member.dto.ts

@@ -59,3 +59,11 @@ export interface MemberLoginBody {
 export interface ResetPasswordBody {
   password: string
 }
+
+export interface RegisterBody {
+  name: string
+  password: string
+  email?: string
+  phone?: string
+  code?: string
+}

+ 5 - 1
src/routes/member.routes.ts

@@ -7,7 +7,8 @@ import {
   ListMemberQuery,
   UpdateGuestBody,
   MemberLoginBody,
-  ResetPasswordBody
+  ResetPasswordBody,
+  RegisterBody
 } from '../dto/member.dto'
 import { VipLevel, MemberStatus } from '../entities/member.entity'
 import { UserRole } from '../entities/user.entity'
@@ -25,6 +26,9 @@ export default async function memberRoutes(fastify: FastifyInstance) {
     memberController.upgradeGuest.bind(memberController)
   )
 
+  // 注册
+  fastify.post<{ Body: RegisterBody }>('/register', memberController.register.bind(memberController))
+
   // 登录
   fastify.post<{ Body: MemberLoginBody }>('/login', memberController.memberLogin.bind(memberController))
 

+ 1 - 5
src/routes/sys-config.routes.ts

@@ -73,9 +73,5 @@ export default async function sysConfigRoutes(fastify: FastifyInstance) {
     sysConfigController.listTeamConfigs.bind(sysConfigController)
   )
 
-  fastify.get(
-    '/team/user',
-    { onRequest: [hasRole(UserRole.USER)] },
-    sysConfigController.getUserTeamConfigs.bind(sysConfigController)
-  )
+  fastify.get('/team/user', sysConfigController.getUserTeamConfigs.bind(sysConfigController))
 }

+ 54 - 0
src/services/member.service.ts

@@ -285,4 +285,58 @@ export class MemberService {
       await this.update(member.id, member)
     }
   }
+
+  async register(name: string, password: string, email?: string, phone?: string, code?: string, ip?: string): Promise<{ user: User; member: Member }> {
+    return await this.dataSource.transaction(async manager => {
+      // 检查用户名是否已存在
+      const existingUser = await manager.findOne(User, { where: { name } })
+      if (existingUser) {
+        throw new Error('用户名已被使用')
+      }
+
+      // 检查邮箱是否已存在
+      if (email) {
+        const existingEmail = await manager.findOne(Member, { where: { email } })
+        if (existingEmail) {
+          throw new Error('邮箱已被使用')
+        }
+      }
+
+      // 检查手机号是否已存在
+      if (phone) {
+        const existingPhone = await manager.findOne(Member, { where: { phone } })
+        if (existingPhone) {
+          throw new Error('手机号已被使用')
+        }
+      }
+
+      // 获取推荐团队
+      const team = await manager.findOne(Team, { where: { affCode: code } })
+      const parentId = team ? team.userId : 1
+
+      // 创建用户
+      const hashedPassword = await bcrypt.hash(password, 10)
+      const user = manager.create(User, {
+        name,
+        password: hashedPassword,
+        role: UserRole.USER,
+        parentId
+      })
+      const savedUser = await manager.save(user)
+
+      // 创建会员
+      const member = manager.create(Member, {
+        userId: savedUser.id,
+        email,
+        phone,
+        vipLevel: VipLevel.FREE,
+        status: MemberStatus.ACTIVE,
+        ip: ip || 'unknown',
+        lastLoginAt: new Date()
+      })
+      const savedMember = await manager.save(member)
+
+      return { user: savedUser, member: savedMember }
+    })
+  }
 }

+ 9 - 7
src/services/sys-config.service.ts

@@ -263,13 +263,15 @@ export class SysConfigService {
     }
   }
 
-  async getUserTeamConfigs(userId: number) {
+  async getUserTeamConfigs(userId?: number) {
     let teamId = 0
-    const user = await this.userRepository.findOne({ where: { id: userId } })
-    if (user) {
-      const team = await this.teamRepository.findOne({ where: { userId: user.parentId } })
-      if (team) {
-        teamId = team.id
+    if (userId) {
+      const user = await this.userRepository.findOne({ where: { id: userId } })
+      if (user) {
+        const team = await this.teamRepository.findOne({ where: { userId: user.parentId } })
+        if (team) {
+          teamId = team.id
+        }
       }
     }
 
@@ -296,7 +298,7 @@ export class SysConfigService {
       { name: 'preview_duration', value: '30', remark: '试看时间(秒)', type: ConfigType.Number }
     ]
 
-    const configs = defaultConfigs.map(config => 
+    const configs = defaultConfigs.map(config =>
       this.sysConfigRepository.create({
         ...config,
         teamId