card.js 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. 'use strict';
  2. const { CrudService } = require('naf-framework-mongoose/lib/service');
  3. const { BusinessError, ErrorCode } = require('naf-core').Error;
  4. const { ObjectId } = require('mongoose').Types;
  5. const _ = require('lodash');
  6. const moment = require('moment');
  7. /**
  8. * 核心逻辑
  9. * . A
  10. * /_\ B
  11. * /_ _\ C crime
  12. * /_ _ _\ D crime
  13. * /_ _ _ _\ E crime
  14. */
  15. class CardService extends CrudService {
  16. constructor(ctx) {
  17. super(ctx, 'card');
  18. this.model = this.ctx.model.Card;
  19. this.record = this.ctx.model.Record;
  20. /**
  21. * @constant Number 车奖的积分 default:131400
  22. */
  23. this.car_point = 131400;
  24. /**
  25. * @constant Number 股东的积分 default:10
  26. */
  27. this.stockholder_point = 10;
  28. /**
  29. * @constant Number 股东的单数界限,前xxxx单不是股东 default: 9999
  30. */
  31. this.stockholder_limit = 20;
  32. /**
  33. * @constant Number 股东的等级界限,前x级不是股东 default:6
  34. */
  35. this.stockholder_limit_level = 6;
  36. /**
  37. * @constant Number 车奖的等级界限,前x级不是能获得车奖 default:4
  38. */
  39. this.car_show_limit_level = 4;
  40. /**
  41. * @constant Number 车奖的B梯队等级界限 default:4
  42. */
  43. this.car_show_b_limit_level = 4;
  44. /**
  45. * @constant Number 车奖的B梯队等级界限的人数 default:5
  46. */
  47. this.car_show_b_limit_person = 5;
  48. }
  49. /**
  50. * 修改密码
  51. * @param {Object} param 参数
  52. */
  53. async passwd({ id, password }) {
  54. password = { secret: password };
  55. const res = await this.model.update({ _id: ObjectId(id) }, { password });
  56. return res;
  57. }
  58. /**
  59. * 1创建卡用户;2检查推荐人
  60. * @param {Object} data 参数
  61. */
  62. async create(data) {
  63. // 先创建用户
  64. // 1,检查手机号是否存在
  65. const { password, mobile, set, r_mobile } = data;
  66. const is_exists = await this.model.count({ mobile });
  67. if (is_exists) { throw new BusinessError(ErrorCode.DATA_EXISTED, '手机号已存在'); }
  68. // 2,创建用户
  69. let user;
  70. try {
  71. data.password = { secret: password };
  72. user = await this.model.create(data);
  73. user = _.omit(user, [ 'password' ]);
  74. } catch (e) {
  75. this.ctx.logger.error(e);
  76. throw new BusinessError(
  77. ErrorCode.SERVICE_FAULT,
  78. '输入信息有误,用户创建失败!'
  79. );
  80. }
  81. // 3,检查有没有推荐人,如果没有推荐人,那就完事了,有推荐人,还需要检查给推荐人加多少分
  82. if (!r_mobile) return user;
  83. const recommender = await this.model.findOne({ mobile: r_mobile });
  84. if (!recommender) {
  85. await this.model.deleteOne({ _id: user._id });
  86. throw new BusinessError(
  87. ErrorCode.SERVICE_FAULT,
  88. '推荐人信息错误,用户创建失败!'
  89. );
  90. }
  91. // 4,判断是129套餐,还是169套餐,如果是129套餐,那就不需要继续了
  92. if (set === '129') return user;
  93. // 根据等级,找下推荐人应该加多少分
  94. const { stockholder } = recommender;
  95. // 重新计算推荐人用户等级
  96. const nlevel = await this.reComputedLevel(recommender);
  97. recommender.level = nlevel;
  98. const rank = this.getRank().find(f => f.rank === nlevel);
  99. if (!rank) {
  100. // TODO 告知推荐人,等级信息有错误,中断,返回用户添加成功
  101. return user;
  102. }
  103. let { score } = rank;
  104. // 需要检查股东奖,是否需要+10
  105. if (stockholder) score = score + this.stockholder_point;
  106. else {
  107. // 去写检查是不是股东的问题
  108. const result = await this.checkStockholder(recommender);
  109. if (result === undefined) {
  110. // TODO 告知推荐人,股东检测有错误,中断,返回用户添加成功
  111. return user;
  112. }
  113. // 这次加上这个用户就是股东了
  114. if (result) {
  115. // 此情况主要讨论的是 9999 单时,是否加=>9999单时,不加
  116. recommender.stockholder = true;
  117. score = score + 10;
  118. }
  119. }
  120. // 加积分
  121. recommender.points = `${recommender.points + score}`;
  122. await recommender.save();
  123. const record = _.pick(recommender, [ 'mobile', 'name' ]);
  124. record.points = score;
  125. record.opera = '推荐办卡';
  126. record.params = { name: user.name, mobile: user.mobile };
  127. try {
  128. await this.record.create(record);
  129. } catch (error) {
  130. this.logger.error(`积分记录添加失败${JSON.stringify(record)}`);
  131. }
  132. return user;
  133. }
  134. /**
  135. * 计算推荐人信息的等级;找到@property的2个变量
  136. * @param {Object} user 推荐人信息
  137. * @property r_number A推出去的人数 B级总数
  138. * @property b_r_number B级推出去的人数 C级总数
  139. * @property group_number 团队完成单数: r_number + b_r_number
  140. * @return {Number} level 等级
  141. */
  142. async reComputedLevel(user) {
  143. const result = await this.getEchelon(user);
  144. if (!result) return;
  145. const { b = [], c = 0 } = result;
  146. /**
  147. * @constant Number group_number 团队完成单数
  148. * @type Number
  149. */
  150. const group_number = b.length + c;
  151. // 先用1阶条件过滤,是否满足推荐人数要求 + 是否满足团队单数要求
  152. let ranks = this.getRank().filter(f => b.length >= f.person && group_number >= f.group);
  153. // 按rank 倒序
  154. ranks = _.orderBy(ranks, [ 'rank' ], [ 'desc' ]);
  155. // 使用find查找,因为倒序排列,所以按照条件挨个比,取的就是结果
  156. const rank = ranks.find(f => {
  157. const { n_rank, n_r_person } = f;
  158. // 查找B梯队里,满足当前阶级要求的 B梯队职位 的人数
  159. const list = b.filter(bl => bl.level === n_rank);
  160. // 如果人数也满足要求,推荐人就应该是这个阶级了,反之继续找
  161. return list >= n_r_person;
  162. });
  163. if (rank) return rank.rank;
  164. return user.level;
  165. }
  166. /**
  167. * 检查该用户是否满足股东身份
  168. * @param {Object} user 用户
  169. */
  170. async checkStockholder(user) {
  171. try {
  172. const level = _.get(user, 'level', 1) * 1;
  173. if (level < this.stockholder_limit_level) return false;
  174. const result = await this.getEchelon(user);
  175. if (!result) return;
  176. const { b = [], c = 0 } = result;
  177. const group_number = b.length + c;
  178. return group_number > this.stockholder_limit;
  179. } catch (error) {
  180. this.ctx.logger.error(`${moment().format('YYYY-MM-DD HH:mm:ss')}-${user.name}-${user.mobile}:股东检查出错`);
  181. return undefined;
  182. }
  183. }
  184. /**
  185. * 检查该用户是否满足车奖
  186. */
  187. async checkCarshow() {
  188. const list = await this.model.find({ level: { $gte: this.car_show_limit_level }, car_show: false });
  189. try {
  190. for (const user of list) {
  191. const b = await this.model.count({ r_mobile: user.mobile, level: { $gte: this.car_show_b_limit_level } });
  192. if (b >= this.car_show_b_limit_person) {
  193. user.car_show = true;
  194. user.points = user.points + this.car_point;
  195. await user.save();
  196. // TODO 添加积分记录
  197. const record = _.pick(user, [ 'mobile', 'name' ]);
  198. record.points = this.car_point;
  199. record.opera = '车奖';
  200. try {
  201. await this.record.create(record);
  202. } catch (error) {
  203. this.logger.error(`积分记录添加失败${JSON.stringify(record)}`);
  204. }
  205. }
  206. }
  207. } catch (error) {
  208. this.ctx.logger.error(`${moment().format('YYYY-MM-DD HH:mm:ss')}-车奖检查出错`);
  209. return undefined;
  210. }
  211. }
  212. /**
  213. * 查找用户的梯队
  214. * @param {Object} user 用户(其实指的就是推荐人)
  215. * @return {Object} {b:b梯队列表,c:c梯队数据}
  216. */
  217. async getEchelon(user) {
  218. // 推荐的人 B梯队
  219. const b_echelon = await this.model.find({ r_mobile: user.mobile });
  220. // c梯队的总人数
  221. const c_echelon_number = await this.model.count({ r_mobile: b_echelon.map(i => i.mobile) });
  222. return { b: b_echelon, c: c_echelon_number };
  223. }
  224. /**
  225. * 等级设置
  226. * @property rank 等级:1,业务员;2,经理;3一星经理;4,二星经理;5,三星经理;6,四星经理;
  227. * @property person 该等级人数底线要求
  228. * @property group 该等级团队单数底线要求
  229. * @property n_rank 该团队中,要求B梯队中的等级
  230. * @property n_r_person 该团队中,要求B梯队指定等级的人数
  231. */
  232. getRank() {
  233. return [
  234. {
  235. rank: 1,
  236. person: 1,
  237. group: 0,
  238. score: 600,
  239. n_rank: undefined,
  240. n_r_person: 0,
  241. },
  242. {
  243. rank: 2,
  244. person: 3,
  245. group: 11,
  246. score: 600 + 100,
  247. n_rank: undefined,
  248. n_r_person: 0,
  249. },
  250. {
  251. rank: 3,
  252. person: 10,
  253. group: 111,
  254. score: 600 + 150,
  255. n_rank: 2,
  256. n_r_person: 1,
  257. },
  258. {
  259. rank: 4,
  260. person: 20,
  261. group: 555,
  262. score: 600 + 200,
  263. n_rank: 3,
  264. n_r_person: 2,
  265. },
  266. {
  267. rank: 5,
  268. person: 25,
  269. group: 1111,
  270. score: 600 + 250,
  271. n_rank: 4,
  272. n_r_person: 2,
  273. },
  274. {
  275. rank: 6,
  276. person: 30,
  277. group: 5555,
  278. score: 600 + 300,
  279. n_rank: 5,
  280. n_r_person: 3,
  281. },
  282. ];
  283. }
  284. }
  285. module.exports = CardService;