341 lines
9.8 KiB
JavaScript
341 lines
9.8 KiB
JavaScript
/**
|
||
* 邀请注册管理控制器(后台管理)
|
||
* 提供邀请注册相关的接口,不需要登录验证
|
||
*/
|
||
|
||
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 || '验证失败'
|
||
};
|
||
}
|
||
}
|
||
|