'use strict'; const { CrudService } = require('naf-framework-mongoose/lib/service'); const { BusinessError, ErrorCode } = require('naf-core').Error; const { ObjectId } = require('mongoose').Types; const _ = require('lodash'); const moment = require('moment'); /** * 核心逻辑 * . A * /_\ B * /_ _\ C crime * /_ _ _\ D crime * /_ _ _ _\ E crime */ class CardService extends CrudService { constructor(ctx) { super(ctx, 'card'); this.model = this.ctx.model.Card; this.record = this.ctx.model.Record; this.set = this.ctx.model.Set; /** * @constant Number 车奖的积分 default:131419 */ this.car_point = 131419; /** * @constant Number 股东的积分 default:10 */ this.stockholder_point = 10; /** * @constant Number 股东的单数界限,前xxxx单不是股东 default: 9999 */ this.stockholder_limit = 20; /** * @constant Number 股东的等级界限,前x级不是股东 default:6 */ this.stockholder_limit_level = 6; /** * @constant Number 车奖的等级界限,前x级不是能获得车奖 default:4 */ this.car_show_limit_level = 4; /** * @constant Number 车奖的B梯队等级界限 default:4 */ this.car_show_b_limit_level = 4; /** * @constant Number 车奖的B梯队等级界限的人数 default:5 */ this.car_show_b_limit_person = 5; } /** * 1创建卡用户;2检查推荐人 * @param {Object} data 参数 */ async create(data) { // 先创建用户 // 1,检查手机号是否存在 const { password, mobile, set, r_mobile } = data; const is_exists = await this.model.count({ mobile }); if (is_exists) { throw new BusinessError(ErrorCode.DATA_EXISTED, '手机号已存在'); } // 2,创建用户 let user; try { data.password = { secret: password }; user = await this.model.create({ ...data, create_time: moment().format('YYYY-MM-DD HH:mm:ss') }); user = _.omit(user, [ 'password' ]); } catch (e) { this.ctx.logger.error(e); throw new BusinessError( ErrorCode.SERVICE_FAULT, '输入信息有误,用户创建失败!' ); } return user; // 2021-04-02 12:00 修改,添加用户审核,需要将非创建用户部分移至通过审核后进行 } async check({ id }, { status }) { // 2021-04-02 12:00 修改,将积分计算部分移至用户审核后进行 const user = await this.model.findById(id); if (!user) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到用户信息'); if (status === '-1') { // 审核不通过,直接删除用户 await this.model.deleteOne({ _id: ObjectId(id) }); return '审核未通过,用户已删除'; } user.status = status; await user.save(); const { set, r_mobile } = user; // 3,检查有没有推荐人,如果没有推荐人,那就完事了,有推荐人,还需要检查给推荐人加多少分 if (!r_mobile) return user; const recommender = await this.model.findOne({ mobile: r_mobile }); if (!recommender) { await this.model.deleteOne({ _id: user._id }); throw new BusinessError( ErrorCode.SERVICE_FAULT, '推荐人信息错误,用户创建失败!' ); } // 4,判断是套餐有没有推荐制度(此处因为套餐可维护,修改了) const setInfo = await this.set.findById(set); if (!setInfo) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到选择的套餐信息!'); const { has_group } = setInfo; if (!has_group) return user; // 5,根据等级,找下推荐人应该加多少分 const { stockholder, level } = recommender; // 6,重新计算推荐人用户等级 const nlevel = await this.reComputedLevel(recommender); recommender.level = nlevel; const rank = this.getRank().find(f => f.rank === nlevel); if (!rank) { // TODO 告知推荐人,等级信息有错误,中断,返回用户添加成功 return user; } let { score } = rank; // 7,需要检查股东奖,是否需要+10 if (stockholder) score = score + this.stockholder_point; else { // 去写检查是不是股东的问题 const result = await this.checkStockholder(recommender); if (result === undefined) { // TODO 告知推荐人,股东检测有错误,中断,返回用户添加成功 return user; } // 这次加上这个用户就是股东了 if (result) { // 此情况主要讨论的是 9999 单时,是否加=>9999单时,不加 recommender.stockholder = true; score = score + 10; } } // 8,加积分 recommender.points = `${recommender.points + score}`; await recommender.save(); const record = _.pick(recommender, [ 'mobile', 'name' ]); record.points = score; record.opera = '1'; record.params = { name: user.name, mobile: user.mobile, level: user.level }; try { await this.record.create({ ...record, create_time: moment().format('YYYY-MM-DD HH:mm:ss') }); } catch (error) { this.logger.error(`line-121-积分记录添加失败${JSON.stringify(record)}`); } // 9,以上为用户创建,计算推荐人等级=>计算推荐人积分=>生成记录 // 先算等级,避免分数基础部队 // 推荐人升级,进行推荐人等级检查与同级加分(原等级和现等级不同,说明升级了,需要检查下) if (level !== nlevel) this.toCheckRecommender(recommender._id); // 10,给推荐人的推荐人反佣金 this.rtrScore(recommender._id, level); return user; } /** * 计算推荐人信息的等级;找到@property的2个变量 * @param {Object} user 推荐人信息 * @property r_number A推出去的人数 B级总数 * @property b_r_number B级推出去的人数 C级总数 * @property group_number 团队完成单数: r_number + b_r_number * @return {Number} level 等级 */ async reComputedLevel(user) { const result = await this.getEchelon(user); if (!result) return; const { b = [], c = 0 } = result; /** * @constant Number group_number 团队完成单数 * @type Number */ const group_number = b.length + c; // 先用1阶条件过滤,是否满足推荐人数要求 + 是否满足团队单数要求 let ranks = this.getRank().filter(f => b.length >= f.person && group_number >= f.group); // 按rank 倒序 ranks = _.orderBy(ranks, [ 'rank' ], [ 'desc' ]); // 使用find查找,因为倒序排列,所以按照条件挨个比,取的就是结果 const rank = ranks.find(f => { const { n_rank, n_r_person } = f; // 查找B梯队里,满足当前阶级要求的 B梯队职位 的人数 const list = b.filter(bl => bl.level === n_rank); // 如果人数也满足要求,推荐人就应该是这个阶级了,反之继续找 return list >= n_r_person; }); if (rank) return rank.rank; return user.level; } /** * 检查该用户是否满足股东身份 * @param {Object} user 用户 */ async checkStockholder(user) { try { const level = _.get(user, 'level', 1) * 1; if (level < this.stockholder_limit_level) return false; const result = await this.getEchelon(user); if (!result) return; const { b = [], c = 0 } = result; const group_number = b.length + c; return group_number > this.stockholder_limit; } catch (error) { this.ctx.logger.error(`line-178-${moment().format('YYYY-MM-DD HH:mm:ss')}-${user.name}-${user.mobile}:股东检查出错`); return undefined; } } /** * 检查该用户是否满足车奖 */ async checkCarshow() { const list = await this.model.find({ level: { $gte: this.car_show_limit_level }, car_show: false }); try { for (const user of list) { const b = await this.model.count({ r_mobile: user.mobile, level: { $gte: this.car_show_b_limit_level } }); if (b >= this.car_show_b_limit_person) { user.car_show = true; user.points = user.points + this.car_point; await user.save(); // 添加积分记录 const record = _.pick(user, [ 'mobile', 'name' ]); record.points = this.car_point; record.opera = '2'; try { await this.record.create({ ...record, create_time: moment().format('YYYY-MM-DD HH:mm:ss') }); } catch (error) { this.logger.error(`line-202-积分记录添加失败${JSON.stringify(record)}`); } } } } catch (error) { this.ctx.logger.error(`line-207-${moment().format('YYYY-MM-DD HH:mm:ss')}-车奖检查出错`); return undefined; } } /** * 查找用户的梯队 * @param {Object} user 用户(其实指的就是推荐人) * @return {Object} {b:b梯队列表,c:c梯队数据} */ async getEchelon(user) { // 推荐的人 B梯队 const b_echelon = await this.model.find({ r_mobile: user.mobile }); // c梯队的总人数 const c_echelon_number = await this.model.count({ r_mobile: b_echelon.map(i => i.mobile) }); return { b: b_echelon, c: c_echelon_number }; } /** * 添加用户后,检查推荐人是否和他自己的推荐人同级了 * @param {String} id 该用户的推荐人id */ async toCheckRecommender(id) { try { const rec = await this.model.findById(id); if (!rec) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到推荐人信息'); const { r_mobile, level } = rec; // 查出推荐人的推荐人 const r_rec = await this.model.findOne({ mobile: r_mobile }); if (!r_rec) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到推荐人的推荐人信息'); const { level: rlevel } = r_rec; // 推荐人等级已经计算过了,直接比较 if (level === rlevel) { rec.points = rec.points + 20; await rec.save(); } const record = _.pick(rec, [ 'mobile', 'name' ]); try { record.points = 20; record.opera = '3'; await this.record.create({ ...record, create_time: moment().format('YYYY-MM-DD HH:mm:ss') }); } catch (error) { this.logger.error(`line-250-积分记录添加失败${JSON.stringify(record)}`); } } catch (error) { this.logger.error(`line-253-捕获检查用户推荐人的推荐人失败:${JSON.stringify(error)}`); } } /** * 给该用户的推荐人的推荐人反佣金 * @param {String} id 该用户的推荐人id * @param {Number} level 该用户的推荐人原等级,需要按这个等级去反佣 */ async rtrScore(id, level) { try { const rec = await this.model.findById(id); if (!rec) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到推荐人信息'); const { r_mobile } = rec; // 查出推荐人的推荐人 const r_rec = await this.model.findOne({ mobile: r_mobile }); if (!r_rec) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到推荐人的推荐人信息'); // r_level:推荐人的推荐人的等级 const { level: r_level } = r_rec; const points = this.getReturns(level, r_level); r_rec.points = r_rec.points + points; await r_rec.save(); const record = _.pick(r_rec, [ 'mobile', 'name' ]); try { record.points = points; record.opera = '4'; await this.record.create({ ...record, create_time: moment().format('YYYY-MM-DD HH:mm:ss') }); } catch (error) { this.logger.error(`line-282-积分记录添加失败${JSON.stringify(record)}`); } } catch (error) { this.logger.error(`line-285-捕获检查用户推荐人的推荐人失败:${JSON.stringify(error)}`); } } /** * 等级设置 * @property rank 等级:1,业务员;2,经理;3一星经理;4,二星经理;5,三星经理;6,四星经理; * @property person 该等级人数底线要求 * @property group 该等级团队单数底线要求 * @property n_rank 该团队中,要求B梯队中的等级 * @property n_r_person 该团队中,要求B梯队指定等级的人数 */ getRank() { return [ { rank: 1, person: 1, group: 0, score: 600, n_rank: undefined, n_r_person: 0, }, { rank: 2, person: 3, group: 11, score: 600 + 100, n_rank: undefined, n_r_person: 0, }, { rank: 3, person: 10, group: 111, score: 600 + 150, n_rank: 2, n_r_person: 1, }, { rank: 4, person: 20, group: 555, score: 600 + 200, n_rank: 3, n_r_person: 2, }, { rank: 5, person: 25, group: 1111, score: 600 + 250, n_rank: 4, n_r_person: 2, }, { rank: 6, person: 30, group: 5555, score: 600 + 300, n_rank: 5, n_r_person: 3, }, ]; } /** * 根据推荐人和推荐人的推荐人等级判断,该如何反給推荐人的推荐人佣金 * @param {Number} level 推荐人等级 * @param {Number} r_level 推荐人的推荐人等级 */ getReturns(level, r_level) { // 同级固定反20 if (level === r_level) return 20; const levels = [ { rank: 1, 2: 100, 3: 150, 4: 200, 5: 250, 6: 300 }, { rank: 2, 3: 50, 4: 100, 5: 150, 6: 200 }, { rank: 3, 4: 50, 5: 100, 6: 150 }, { rank: 4, 5: 50, 6: 100 }, { rank: 5, 6: 50 }, ]; // 根据推荐人等级,获取价格表的指定行 const obj = levels.find(f => f.rank === level); if (!obj) throw new BusinessError(ErrorCode.SERVICE_FAULT, '为找到推荐人适合的规则'); return _.get(obj, r_level); } /** * 修改密码 * @param {Object} param 参数 */ async passwd({ id, password }) { password = { secret: password }; const res = await this.model.update({ _id: ObjectId(id) }, { password }); return res; } /** * 根据条件查询该用户的团队人员 * @param {Object} query 查询条件 */ async group(query) { const condition = this.ctx.service.util.queryReset(query); const { filter } = condition; const bTerm = await this.model.find(filter); const cTerm = await this.model.find({ r_mobile: bTerm.map(i => i.mobile) }); return { list: _.uniqBy([ ...bTerm, ...cTerm ], '_id'), total: bTerm.length + cTerm.length }; } } module.exports = CardService;