123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414 |
- '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;
|