/** * 推广邀请管理控制器(客户端接口) * 提供客户端调用的推广邀请相关接口 */ const Framework = require("../../framework/node-core-framework.js"); const dayjs = require('dayjs'); const email_service = require('../services/email_service.js'); module.exports = { /** * @swagger * /api/invite/info: * post: * summary: 获取邀请信息 * description: 根据设备SN码获取邀请码和邀请链接 * tags: [前端-推广邀请] * requestBody: * required: true * content: * application/json: * schema: * type: object * required: * - sn_code * properties: * sn_code: * type: string * description: 设备SN码 * responses: * 200: * description: 获取成功 */ 'POST /invite/info': async (ctx) => { try { // 从body中获取sn_code const body = ctx.getBody(); const { sn_code } = body; if (!sn_code) { return ctx.fail('请提供设备SN码'); } const { pla_account } = await Framework.getModels(); // 根据sn_code查找用户 const user = await pla_account.findOne({ where: { sn_code } }); if (!user) { return ctx.fail('用户不存在'); } // 生成邀请码(基于用户ID和sn_code) const invite_code = `INV${user.id}_${Date.now().toString(36).toUpperCase()}`; const invite_link = `https://work.light120.com/register.html?code=${invite_code}`; // 保存邀请码到用户记录(可以保存到invite_code字段,如果没有则保存到备注或其他字段) // 这里暂时不保存到数据库,每次生成新的 return ctx.success({ invite_code, invite_link, user_id: user.id, sn_code: user.sn_code }); } catch (error) { console.error('获取邀请信息失败:', error); return ctx.fail('获取邀请信息失败: ' + error.message); } }, /** * @swagger * /api/invite/statistics: * post: * summary: 获取邀请统计 * description: 根据设备SN码获取邀请统计数据 * tags: [前端-推广邀请] * requestBody: * required: true * content: * application/json: * schema: * type: object * required: * - sn_code * properties: * sn_code: * type: string * description: 设备SN码 * responses: * 200: * description: 获取成功 */ 'POST /invite/statistics': async (ctx) => { try { // 从body中获取sn_code const body = ctx.getBody() || {}; const { sn_code } = body; if (!sn_code) { return ctx.fail('请提供设备SN码'); } const { pla_account } = await Framework.getModels(); // 根据sn_code查找用户 const user = await pla_account.findOne({ where: { sn_code } }); if (!user) { return ctx.fail('用户不存在'); } // 查询邀请统计 const { invite_record } = await Framework.getModels(); // 查询总邀请数 const totalInvites = await invite_record.count({ where: { inviter_id: user.id, is_delete: 0 } }); // 查询已发放奖励的邀请数(活跃邀请) const activeInvites = await invite_record.count({ where: { inviter_id: user.id, reward_status: 1, is_delete: 0 } }); // 查询总奖励天数 const rewardRecords = await invite_record.findAll({ where: { inviter_id: user.id, reward_status: 1, is_delete: 0 }, attributes: ['reward_value'] }); const totalRewardDays = rewardRecords.reduce((sum, record) => { return sum + (record.reward_value || 0); }, 0); return ctx.success({ totalInvites: totalInvites || 0 }); } catch (error) { console.error('获取邀请统计失败:', error); return ctx.fail('获取邀请统计失败: ' + error.message); } }, /** * @swagger * /api/invite/generate: * post: * summary: 生成邀请码 * description: 为用户生成新的邀请码 * tags: [前端-推广邀请] * responses: * 200: * description: 生成成功 */ 'POST /invite/generate': async (ctx) => { try { // 从body中获取sn_code const body = ctx.getBody() || {}; const { sn_code } = body; if (!sn_code) { return ctx.fail('请提供设备SN码'); } const { pla_account } = await Framework.getModels(); // 根据sn_code查找用户 const user = await pla_account.findOne({ where: { sn_code } }); if (!user) { return ctx.fail('用户不存在'); } // 生成新的邀请码 const invite_code = `INV${user.id}_${Date.now().toString(36).toUpperCase()}`; const invite_link = `https://work.light120.com/register.html?code=${invite_code}`; return ctx.success({ invite_code, invite_link }); } catch (error) { console.error('生成邀请码失败:', error); return ctx.fail('生成邀请码失败: ' + error.message); } }, /** * @swagger * /api/invite/records: * post: * summary: 获取邀请记录列表 * description: 根据设备SN码获取邀请记录列表 * tags: [前端-推广邀请] * requestBody: * required: true * content: * application/json: * schema: * type: object * required: * - sn_code * properties: * sn_code: * type: string * description: 设备SN码 * page: * type: integer * description: 页码(可选,默认1) * pageSize: * type: integer * description: 每页数量(可选,默认20) * responses: * 200: * description: 获取成功 */ 'POST /invite/records': async (ctx) => { try { const body = ctx.getBody() || {}; const { sn_code, page = 1, pageSize = 20 } = body; if (!sn_code) { return ctx.fail('请提供设备SN码'); } const { pla_account, invite_record } = await Framework.getModels(); // 根据sn_code查找用户 const user = await pla_account.findOne({ where: { sn_code } }); if (!user) { return ctx.fail('用户不存在'); } // 查询邀请记录列表 const offset = (page - 1) * pageSize; const records = await invite_record.findAll({ where: { inviter_id: user.id, is_delete: 0 }, order: [['register_time', 'DESC']], limit: pageSize, offset: offset }); const total = await invite_record.count({ where: { inviter_id: user.id, is_delete: 0 } }); // 格式化记录数据 const formattedRecords = records.map(record => { const recordData = record.toJSON(); return { id: recordData.id, invitee_phone: recordData.invitee_phone, invite_code: recordData.invite_code, register_time: recordData.register_time, reward_status: recordData.reward_status, reward_value: recordData.reward_value, reward_type: recordData.reward_type }; }); return ctx.success({ list: formattedRecords, total: total, page: page, pageSize: pageSize }); } catch (error) { console.error('获取邀请记录列表失败:', error); return ctx.fail('获取邀请记录列表失败: ' + error.message); } }, /** * @swagger * /api/invite/register: * post: * summary: 邀请注册 * description: 通过邀请码注册新用户,注册成功后给邀请人增加3天试用期 * tags: [前端-推广邀请] * requestBody: * required: true * content: * application/json: * schema: * type: object * required: * - email * - password * - email_code * properties: * email: * type: string * description: 邮箱地址 * example: 'user@example.com' * password: * type: string * description: 密码 * example: 'password123' * email_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 { email, password, email_code, invite_code } = body; const { hashPassword, maskEmail } = require('../utils/crypto_utils'); // 验证必填参数 if (!email || !password || !email_code) { return ctx.fail('邮箱、密码和验证码不能为空'); } // 验证邮箱格式 const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { return ctx.fail('邮箱格式不正确'); } // 验证密码长度 if (password.length < 6 || password.length > 50) { return ctx.fail('密码长度必须在6-50位之间'); } // 统一邮箱地址为小写 const email_normalized = email.toLowerCase().trim(); // 验证验证码 const emailVerifyResult = await email_service.verifyEmailCode(email_normalized, 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: email_normalized } }); if (existingUser) { 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('邀请码无效,邀请人不存在'); } } // 生成设备SN码(基于邮箱和时间戳) const sn_code = `SN${Date.now()}${Math.random().toString(36).substr(2, 6).toUpperCase()}`; // 加密密码 const hashedPassword = await hashPassword(password); // 创建新用户(使用统一的小写邮箱和加密密码) // 新用户注册赠送3天试用期 const newUser = await pla_account.create({ name: email_normalized.split('@')[0], // 默认使用邮箱用户名作为名称 sn_code: sn_code, device_id: '', // 设备ID由客户端登录时提供 platform_type: 'boss', // 默认平台类型 login_name: email_normalized, pwd: hashedPassword, // 使用加密后的密码 keyword: '', is_enabled: 1, is_delete: 0, authorization_date: new Date(), // 新用户从注册日期开始 authorization_days: 3 // 赠送3天试用期 }); // 给邀请人增加3天试用期 if (inviter) { const inviterData = inviter.toJSON(); const currentAuthDate = inviterData.authorization_date; const currentAuthDays = inviterData.authorization_days || 0; let newAuthDate; let newAuthDays; // 如果当前没有授权日期,则从今天开始 if (!currentAuthDate) { newAuthDate = new Date(); newAuthDays = currentAuthDays + 3; // 累加3天 } else { // 检查当前授权是否已过期 const currentEndDate = dayjs(currentAuthDate).add(currentAuthDays, 'day'); const now = dayjs(); if (currentEndDate.isBefore(now)) { // 授权已过期,重新激活并给予3天试用期 newAuthDate = new Date(); newAuthDays = 3; // 过期后重新激活给3天 } else { // 授权未过期,在现有基础上累加3天 newAuthDate = currentAuthDate; newAuthDays = currentAuthDays + 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: email, // 使用邮箱代替手机号 invite_code: invite_code, register_time: new Date(), reward_status: 1, // 已发放 reward_type: 'trial_days', reward_value: 3, is_delete: 0 }); console.log(`[邀请注册] 用户 ${maskEmail(email_normalized)} 通过邀请码 ${invite_code} 注册成功,邀请人 ${inviter.sn_code} 获得3天试用期`); } else { console.log(`[邀请注册] 用户 ${maskEmail(email_normalized)} 注册成功(无邀请码)`); } 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 * /api/invite/send_email_code: * post: * summary: 发送验证码 * description: 发送验证码到指定邮箱地址 * tags: [前端-推广邀请] * requestBody: * required: true * content: * application/json: * schema: * type: object * required: * - email * properties: * email: * type: string * description: 邮箱地址 * example: 'user@example.com' * responses: * 200: * description: 发送成功 */ 'POST /invite/send_email_code': async (ctx) => { try { const body = ctx.getBody(); const { email } = body; if (!email) { return ctx.fail('邮箱地址不能为空'); } // 验证邮箱格式 const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email)) { return ctx.fail('邮箱格式不正确'); } // 统一邮箱地址为小写 const email_normalized = email.toLowerCase().trim(); // 发送验证码 const emailResult = await email_service.sendEmailCode(email_normalized); if (!emailResult.success) { return ctx.fail(emailResult.message || '发送验证码失败'); } return ctx.success({ message: '验证码已发送', expire_time: emailResult.expire_time || 300 // 默认5分钟过期 }); } catch (error) { console.error('发送验证码失败:', error); return ctx.fail('发送验证码失败: ' + error.message); } } };