This commit is contained in:
张成
2025-12-19 15:54:52 +08:00
parent 4db1f77536
commit cfbcbc39fd
8 changed files with 1097 additions and 456 deletions

View File

@@ -5,6 +5,7 @@
const Framework = require("../../framework/node-core-framework.js");
const dayjs = require('dayjs');
const email_service = require('../services/email_service.js');
module.exports = {
/**
@@ -21,26 +22,25 @@ module.exports = {
* schema:
* type: object
* required:
* - phone
* - email
* - password
* - sms_code
* - invite_code
* - email_code
* properties:
* phone:
* email:
* type: string
* description: 手机号
* example: '13800138000'
* description: 邮箱地址
* example: 'user@example.com'
* password:
* type: string
* description: 密码
* example: 'password123'
* sms_code:
* email_code:
* type: string
* description: 短信验证码
* description: 验证码
* example: '123456'
* invite_code:
* type: string
* description: 邀请码
* description: 邀请码(选填)
* example: 'INV123_ABC123'
* responses:
* 200:
@@ -49,17 +49,17 @@ module.exports = {
'POST /invite/register': async (ctx) => {
try {
const body = ctx.getBody();
const { phone, password, sms_code, invite_code } = body;
const { email, password, email_code, invite_code } = body;
// 验证参数
if (!phone || !password || !sms_code || !invite_code) {
return ctx.fail('手机号、密码、短信验证码和邀请码不能为空');
// 验证必填参数
if (!email || !password || !email_code) {
return ctx.fail('邮箱、密码和验证码不能为空');
}
// 验证手机号格式
const phoneRegex = /^1[3-9]\d{9}$/;
if (!phoneRegex.test(phone)) {
return ctx.fail('手机号格式不正确');
// 验证邮箱格式
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
return ctx.fail('邮箱格式不正确');
}
// 验证密码长度
@@ -67,51 +67,57 @@ module.exports = {
return ctx.fail('密码长度不能少于6位');
}
// 验证短信验证码(这里需要调用短信验证服务)
const smsVerifyResult = await verifySmsCode(phone, sms_code);
if (!smsVerifyResult.success) {
return ctx.fail(smsVerifyResult.message || '短信验证码错误或已过期');
// 验证验证码
const emailVerifyResult = await verifyEmailCode(email, email_code);
if (!emailVerifyResult.success) {
return ctx.fail(emailVerifyResult.message || '验证码错误或已过期');
}
const { pla_account } = await Framework.getModels();
// 检查手机号是否已注册
// 检查邮箱是否已注册
const existingUser = await pla_account.findOne({
where: { login_name: phone }
where: { login_name: email }
});
if (existingUser) {
return ctx.fail('该手机号已被注册');
return ctx.fail('该邮箱已被注册');
}
// 解析邀请码获取邀请人ID
// 邀请码格式INV{user_id}_{timestamp}
const inviteMatch = invite_code.match(/^INV(\d+)_/);
if (!inviteMatch) {
return ctx.fail('邀请码格式不正确');
// 验证邀请码(如果提供了邀请码)
let inviter = null;
let inviter_id = null;
if (invite_code) {
// 解析邀请码获取邀请人ID
// 邀请码格式INV{user_id}_{timestamp}
const inviteMatch = invite_code.match(/^INV(\d+)_/);
if (!inviteMatch) {
return ctx.fail('邀请码格式不正确');
}
inviter_id = parseInt(inviteMatch[1]);
// 验证邀请人是否存在
inviter = await pla_account.findOne({
where: { id: inviter_id }
});
if (!inviter) {
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码基于手机号和时间戳
// 生成设备SN码基于邮箱和时间戳
const sn_code = `SN${Date.now()}${Math.random().toString(36).substr(2, 6).toUpperCase()}`;
// 创建新用户
const newUser = await pla_account.create({
name: phone, // 默认使用手机号作为名称
name: email.split('@')[0], // 默认使用邮箱用户名作为名称
sn_code: sn_code,
device_id: '', // 设备ID由客户端登录时提供
platform_type: 'boss', // 默认平台类型
login_name: phone,
login_name: email,
pwd: password,
keyword: '',
is_enabled: 1,
@@ -158,7 +164,7 @@ module.exports = {
inviter_sn_code: inviter.sn_code,
invitee_id: newUser.id,
invitee_sn_code: newUser.sn_code,
invitee_phone: phone,
invitee_phone: email, // 使用邮箱代替手机号
invite_code: invite_code,
register_time: new Date(),
reward_status: 1, // 已发放
@@ -167,7 +173,9 @@ module.exports = {
is_delete: 0
});
console.log(`[邀请注册] 用户 ${phone} 通过邀请码 ${invite_code} 注册成功,邀请人 ${inviter.sn_code} 获得3天试用期`);
console.log(`[邀请注册] 用户 ${email} 通过邀请码 ${invite_code} 注册成功,邀请人 ${inviter.sn_code} 获得3天试用期`);
} else {
console.log(`[邀请注册] 用户 ${email} 注册成功(无邀请码)`);
}
return ctx.success({
@@ -186,10 +194,10 @@ module.exports = {
/**
* @swagger
* /admin_api/invite/send-sms:
* /admin_api/invite/send-email-code:
* post:
* summary: 发送短信验证码
* description: 发送短信验证码到指定手机号
* summary: 发送验证码
* description: 发送验证码到指定邮箱地址
* tags: [后台-邀请注册]
* requestBody:
* required: true
@@ -198,107 +206,114 @@ module.exports = {
* schema:
* type: object
* required:
* - phone
* - email
* properties:
* phone:
* email:
* type: string
* description: 手机号
* example: '13800138000'
* description: 邮箱地址
* example: 'user@example.com'
* responses:
* 200:
* description: 发送成功
*/
'POST /invite/send-sms': async (ctx) => {
'POST /invite/send-email-code': async (ctx) => {
try {
const body = ctx.getBody();
const { phone } = body;
const { email } = body;
if (!phone) {
return ctx.fail('手机号不能为空');
if (!email) {
return ctx.fail('邮箱地址不能为空');
}
// 验证手机号格式
const phoneRegex = /^1[3-9]\d{9}$/;
if (!phoneRegex.test(phone)) {
return ctx.fail('手机号格式不正确');
// 验证邮箱格式
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!emailRegex.test(email)) {
return ctx.fail('邮箱格式不正确');
}
// 发送短信验证码
const smsResult = await sendSmsCode(phone);
if (!smsResult.success) {
return ctx.fail(smsResult.message || '发送短信验证码失败');
// 发送验证码
const emailResult = await sendEmailCode(email);
if (!emailResult.success) {
return ctx.fail(emailResult.message || '发送验证码失败');
}
return ctx.success({
message: '短信验证码已发送',
expire_time: smsResult.expire_time || 300 // 默认5分钟过期
message: '验证码已发送',
expire_time: emailResult.expire_time || 300 // 默认5分钟过期
});
} catch (error) {
console.error('发送短信验证码失败:', error);
return ctx.fail('发送短信验证码失败: ' + error.message);
console.error('发送验证码失败:', error);
return ctx.fail('发送验证码失败: ' + error.message);
}
}
};
/**
* 发送短信验证码
* @param {string} phone 手机号
* 发送验证码
* @param {string} email 邮箱地址
* @returns {Promise<{success: boolean, message?: string, expire_time?: number}>}
*/
async function sendSmsCode(phone) {
async function sendEmailCode(email) {
try {
// TODO: 实现真实的短信发送逻辑
// 这里可以使用第三方短信服务(如阿里云、腾讯云等)
// 生成6位随机验证码
const code = Math.floor(100000 + Math.random() * 900000).toString();
// 将验证码存储到缓存中可以使用Redis或内存缓存
// 格式:sms_code:{phone} = {code, expire_time}
// 格式:email_code:{email} = {code, expire_time}
const expire_time = Date.now() + 5 * 60 * 1000; // 5分钟后过期
// 这里应该存储到缓存中暂时使用全局变量生产环境应使用Redis
if (!global.smsCodeCache) {
global.smsCodeCache = {};
if (!global.emailCodeCache) {
global.emailCodeCache = {};
}
global.smsCodeCache[phone] = {
global.emailCodeCache[email] = {
code: code,
expire_time: expire_time
};
// TODO: 调用真实的短信发送接口
console.log(`[短信验证] 发送验证码到 ${phone}: ${code} (5分钟内有效)`);
// 调用邮件服务发送验证码
const email_result = await email_service.send_verification_code(email, code);
if (!email_result.success) {
// 如果邮件发送失败,删除已生成的验证码
delete global.emailCodeCache[email];
return {
success: false,
message: email_result.message || '发送验证码失败'
};
}
console.log(`[邮箱验证] 验证码已发送到 ${email}: ${code} (5分钟内有效)`);
// 模拟发送成功
return {
success: true,
expire_time: 300
};
} catch (error) {
console.error('发送短信验证码失败:', error);
console.error('发送验证码失败:', error);
return {
success: false,
message: error.message || '发送短信验证码失败'
message: error.message || '发送验证码失败'
};
}
}
/**
* 验证短信验证码
* @param {string} phone 手机号
* 验证验证码
* @param {string} email 邮箱地址
* @param {string} code 验证码
* @returns {Promise<{success: boolean, message?: string}>}
*/
async function verifySmsCode(phone, code) {
async function verifyEmailCode(email, code) {
try {
if (!global.smsCodeCache) {
if (!global.emailCodeCache) {
return {
success: false,
message: '验证码不存在或已过期'
};
}
const cached = global.smsCodeCache[phone];
const cached = global.emailCodeCache[email];
if (!cached) {
return {
success: false,
@@ -308,7 +323,7 @@ async function verifySmsCode(phone, code) {
// 检查是否过期
if (Date.now() > cached.expire_time) {
delete global.smsCodeCache[phone];
delete global.emailCodeCache[email];
return {
success: false,
message: '验证码已过期,请重新获取'
@@ -324,13 +339,13 @@ async function verifySmsCode(phone, code) {
}
// 验证成功后删除缓存
delete global.smsCodeCache[phone];
delete global.emailCodeCache[email];
return {
success: true
};
} catch (error) {
console.error('验证短信验证码失败:', error);
console.error('验证验证码失败:', error);
return {
success: false,
message: error.message || '验证失败'