/** * 邀请注册管理控制器(后台管理) * 提供邀请注册相关的接口,不需要登录验证 */ const Framework = require("../../framework/node-core-framework.js"); const dayjs = require('dayjs'); module.exports = { /** * @swagger * /admin_api/invite/register: * post: * summary: 邀请注册 * description: 通过邀请码注册新用户,注册成功后给邀请人增加3天试用期 * tags: [后台-邀请注册] * requestBody: * required: true * content: * application/json: * schema: * type: object * required: * - phone * - password * - sms_code * - invite_code * properties: * phone: * type: string * description: 手机号 * example: '13800138000' * password: * type: string * description: 密码 * example: 'password123' * sms_code: * type: string * description: 短信验证码 * example: '123456' * invite_code: * type: string * description: 邀请码 * example: 'INV123_ABC123' * responses: * 200: * description: 注册成功 */ 'POST /invite/register': async (ctx) => { try { const body = ctx.getBody(); const { phone, password, sms_code, invite_code } = body; // 验证参数 if (!phone || !password || !sms_code || !invite_code) { return ctx.fail('手机号、密码、短信验证码和邀请码不能为空'); } // 验证手机号格式 const phoneRegex = /^1[3-9]\d{9}$/; if (!phoneRegex.test(phone)) { return ctx.fail('手机号格式不正确'); } // 验证密码长度 if (password.length < 6) { return ctx.fail('密码长度不能少于6位'); } // 验证短信验证码(这里需要调用短信验证服务) const smsVerifyResult = await verifySmsCode(phone, sms_code); if (!smsVerifyResult.success) { return ctx.fail(smsVerifyResult.message || '短信验证码错误或已过期'); } const { pla_account } = await Framework.getModels(); // 检查手机号是否已注册 const existingUser = await pla_account.findOne({ where: { login_name: phone } }); if (existingUser) { return ctx.fail('该手机号已被注册'); } // 解析邀请码,获取邀请人ID // 邀请码格式:INV{user_id}_{timestamp} const inviteMatch = invite_code.match(/^INV(\d+)_/); if (!inviteMatch) { return ctx.fail('邀请码格式不正确'); } const inviter_id = parseInt(inviteMatch[1]); // 验证邀请人是否存在 const inviter = await pla_account.findOne({ where: { id: inviter_id } }); if (!inviter) { return ctx.fail('邀请码无效,邀请人不存在'); } // 生成设备SN码(基于手机号和时间戳) const sn_code = `SN${Date.now()}${Math.random().toString(36).substr(2, 6).toUpperCase()}`; // 创建新用户 const newUser = await pla_account.create({ name: phone, // 默认使用手机号作为名称 sn_code: sn_code, device_id: '', // 设备ID由客户端登录时提供 platform_type: 'boss', // 默认平台类型 login_name: phone, pwd: password, keyword: '', is_enabled: 1, is_delete: 0, authorization_date: null, authorization_days: 0 }); // 给邀请人增加3天试用期 if (inviter) { const inviterData = inviter.toJSON(); const currentAuthDate = inviterData.authorization_date; const currentAuthDays = inviterData.authorization_days || 0; let newAuthDate = currentAuthDate; let newAuthDays = currentAuthDays + 3; // 增加3天 // 如果当前没有授权日期,则从今天开始 if (!currentAuthDate) { newAuthDate = new Date(); } else { // 如果当前授权已过期,从今天开始计算 const currentEndDate = dayjs(currentAuthDate).add(currentAuthDays, 'day'); const now = dayjs(); if (currentEndDate.isBefore(now)) { newAuthDate = new Date(); newAuthDays = 3; // 重新设置为3天 } } // 更新邀请人的授权信息 await pla_account.update( { authorization_date: newAuthDate, authorization_days: newAuthDays }, { where: { id: inviter_id } } ); // 记录邀请记录 const { invite_record } = await Framework.getModels(); await invite_record.create({ inviter_id: inviter_id, inviter_sn_code: inviter.sn_code, invitee_id: newUser.id, invitee_sn_code: newUser.sn_code, invitee_phone: phone, invite_code: invite_code, register_time: new Date(), reward_status: 1, // 已发放 reward_type: 'trial_days', reward_value: 3, is_delete: 0 }); console.log(`[邀请注册] 用户 ${phone} 通过邀请码 ${invite_code} 注册成功,邀请人 ${inviter.sn_code} 获得3天试用期`); } return ctx.success({ message: '注册成功', user: { id: newUser.id, sn_code: newUser.sn_code, login_name: newUser.login_name } }); } catch (error) { console.error('邀请注册失败:', error); return ctx.fail('注册失败: ' + error.message); } }, /** * @swagger * /admin_api/invite/send-sms: * post: * summary: 发送短信验证码 * description: 发送短信验证码到指定手机号 * tags: [后台-邀请注册] * requestBody: * required: true * content: * application/json: * schema: * type: object * required: * - phone * properties: * phone: * type: string * description: 手机号 * example: '13800138000' * responses: * 200: * description: 发送成功 */ 'POST /invite/send-sms': async (ctx) => { try { const body = ctx.getBody(); const { phone } = body; if (!phone) { return ctx.fail('手机号不能为空'); } // 验证手机号格式 const phoneRegex = /^1[3-9]\d{9}$/; if (!phoneRegex.test(phone)) { return ctx.fail('手机号格式不正确'); } // 发送短信验证码 const smsResult = await sendSmsCode(phone); if (!smsResult.success) { return ctx.fail(smsResult.message || '发送短信验证码失败'); } return ctx.success({ message: '短信验证码已发送', expire_time: smsResult.expire_time || 300 // 默认5分钟过期 }); } catch (error) { console.error('发送短信验证码失败:', error); return ctx.fail('发送短信验证码失败: ' + error.message); } } }; /** * 发送短信验证码 * @param {string} phone 手机号 * @returns {Promise<{success: boolean, message?: string, expire_time?: number}>} */ async function sendSmsCode(phone) { try { // TODO: 实现真实的短信发送逻辑 // 这里可以使用第三方短信服务(如阿里云、腾讯云等) // 生成6位随机验证码 const code = Math.floor(100000 + Math.random() * 900000).toString(); // 将验证码存储到缓存中(可以使用Redis或内存缓存) // 格式:sms_code:{phone} = {code, expire_time} const expire_time = Date.now() + 5 * 60 * 1000; // 5分钟后过期 // 这里应该存储到缓存中,暂时使用全局变量(生产环境应使用Redis) if (!global.smsCodeCache) { global.smsCodeCache = {}; } global.smsCodeCache[phone] = { code: code, expire_time: expire_time }; // TODO: 调用真实的短信发送接口 console.log(`[短信验证] 发送验证码到 ${phone}: ${code} (5分钟内有效)`); // 模拟发送成功 return { success: true, expire_time: 300 }; } catch (error) { console.error('发送短信验证码失败:', error); return { success: false, message: error.message || '发送短信验证码失败' }; } } /** * 验证短信验证码 * @param {string} phone 手机号 * @param {string} code 验证码 * @returns {Promise<{success: boolean, message?: string}>} */ async function verifySmsCode(phone, code) { try { if (!global.smsCodeCache) { return { success: false, message: '验证码不存在或已过期' }; } const cached = global.smsCodeCache[phone]; if (!cached) { return { success: false, message: '验证码不存在或已过期' }; } // 检查是否过期 if (Date.now() > cached.expire_time) { delete global.smsCodeCache[phone]; return { success: false, message: '验证码已过期,请重新获取' }; } // 验证码是否正确 if (cached.code !== code) { return { success: false, message: '验证码错误' }; } // 验证成功后删除缓存 delete global.smsCodeCache[phone]; return { success: true }; } catch (error) { console.error('验证短信验证码失败:', error); return { success: false, message: error.message || '验证失败' }; } }