123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284 |
- 'use strict';
- const { CrudService } = require('naf-framework-mongoose-free/lib/service');
- const { BusinessError, ErrorCode } = require('naf-core').Error;
- const _ = require('lodash');
- const assert = require('assert');
- const user = require('../model/race/user');
- const { ObjectId } = require('mongoose').Types;
- //
- class MatchTeamGroupService extends CrudService {
- constructor(ctx) {
- super(ctx, 'matchteamgroup');
- this.model = this.ctx.model.Race.MatchTeamGroup;
- this.matchProjectModel = this.ctx.model.Race.MatchProject;
- this.teamApplyModel = this.ctx.model.Race.TeamApply;
- this.matchSignModel = this.ctx.model.Race.MatchSign;
- this.baseUserModel = this.ctx.model.Base.User;
- this.userModel = this.ctx.model.Race.User;
- this.matchModel = this.ctx.model.Race.Match;
- this.matchProjectModel = this.ctx.model.Race.MatchProject;
- this.eliminateModel = this.ctx.model.Race.Eliminate;
- }
- /**
- * 根据项目和选手类型查询可选择的分组人员
- * @param {Object} query 地址栏查询条件
- * @property {String} project_id 比赛项目id
- * @property {String} person_type 选手类型
- * @property {String} team_id 小组id
- */
- async findGroupPersonSelects({ project_id, person_type, team_id }) {
- // 1.查找所有报名的选手/小队
- let allPerson;
- // 2.查找该项目下已经分完组的成员, 如果是针对某组的修改,则不查该组
- const teamGroups = await this.model.find({ project_id, _id: { $ne: team_id } }, { person: 1 });
- // 已经有分组的成员id集合
- const personList = _.flattenDeep(teamGroups.map(i => i.person));
- if (person_type === 'Race.TeamApply') {
- allPerson = await this.teamApplyModel.find({ project_id, status: '1', _id: { $nin: personList } });
- } else {
- allPerson = await this.matchSignModel.find({ project_id, pay_status: '1', user_id: { $nin: personList } }).populate({
- path: 'user_id',
- select: 'user_id',
- model: this.userModel,
- populate: {
- path: 'user_id',
- select: 'name',
- model: this.baseUserModel,
- },
- });
- allPerson = JSON.parse(JSON.stringify(allPerson));
- allPerson = allPerson.map(i => {
- i.user_name = _.get(i, 'user_id.user_id.name');
- i.user_id = _.get(i, 'user_id._id');
- return i;
- });
- }
- // 过滤掉 allPerson 中 在personList中的数据
- if (person_type === 'Race.TeamApply') {
- allPerson = allPerson.filter(f => !personList.includes(f._id));
- return allPerson;
- }
- return allPerson;
- }
- // 自动建组
- async saveAll(data) {
- // 确保大家的project_id都是一个
- const match_id = _.get(_.head(data), 'match_id');
- const canOpera = await this.canOpera(match_id);
- if (!canOpera) throw new BusinessError(ErrorCode.SERVICE_FAULT, '当前赛事不处于可更改赛事相关信息状态');
- const project_id = _.get(_.head(data), 'project_id');
- const belongOneProject = data.every(e => e.project_id === project_id);
- if (!belongOneProject) throw new BusinessError(ErrorCode.DATA_INVALID, '自动创建的小组并不全都属于同一个比赛项目,无法创建小组');
- // 先删除该比赛项目的所有小组
- await this.model.deleteMany({ project_id });
- await this.model.insertMany(data);
- // 检查并添加淘汰赛信息
- const group_id = _.get(_.head(data), 'group_id');
- await this.checkEliminate(match_id, group_id, project_id);
- }
- // 检查并创建淘汰赛赛程
- async checkEliminate(match_id, group_id, project_id) {
- // 有赛程就删了,重新创建
- await this.eliminateModel.deleteMany({ match_id, group_id, project_id });
- const groups = await this.model.find({ match_id, group_id, project_id });
- const personTotal = groups.reduce((p, n) => p + (parseInt(n.rise) || 0), 0);
- const level = this.ctx.service.eliminate.getLevel(personTotal);
- const chartData = this.ctx.service.eliminate.getChartData(level);
- let eliminateData = this.ctx.service.eliminate.getEliminateData(chartData);
- const head = _.head(groups);
- const player_type = _.get(head, 'person_type');
- eliminateData = eliminateData.map(i => ({ ...i, player_type, match_id, group_id, project_id }));
- await this.eliminateModel.insertMany(eliminateData);
- }
- async auto({ match_id, group_id, project_id, team_number = 0 }) {
- // 找到比赛项目
- const project = await this.matchProjectModel.findById(project_id);
- if (!project) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到比赛项目');
- const { type } = project;
- // type 为 1 双打,是TeamApply表中的, type 为2 单打,是User表
- let personList = [];
- const obj = { match_id, group_id, project_id };
- if (type === '1') {
- // 双打,找到这个项目所有的申请,且审核通过的
- obj.person_type = 'Race.TeamApply';
- const teamApplys = await this.teamApplyModel.find({ project_id, status: '1' });
- personList = JSON.parse(JSON.stringify(teamApplys));
- } else {
- // 单打,找到这个项目所有申请报名的信息
- obj.person_type = 'Race.User';
- const sign = await this.matchSignModel.find({ project_id, pay_status: '1' });
- personList = JSON.parse(JSON.stringify(sign));
- }
- const returnData = [];
- // 选手数检测
- const number = _.floor(_.divide(personList.length, team_number));
- if (number <= 0) throw new BusinessError(ErrorCode.DATA_INVALID, '选手不足以分组,要分组的总人数 至少为 要分的组数');
- // 选手列表分组
- const teams = _.chunk(personList, number);
- // 获取 选手被均分到每个组后的余数
- const el = personList.length % team_number;
- // 最后余下的组数据
- let last = [];
- if (el !== 0) {
- // 如果有剩余不能被均分的选手,则将这些选手 放到 last数组中,再将多出来的这个去掉
- last = _.last(teams);
- teams.pop();
- }
- // 将正常均分的小组先分出来
- for (let i = 0; i < teams.length; i++) {
- const t = { name: `小组${i + 1}`, person: teams[i], ...obj };
- returnData.push(t);
- }
- // 循环剩下的选手, 依次放入每个已经分好的组中,尽可能均分人数
- for (let i = 0; i < last.length; i++) {
- returnData[i].person.push(last[i]);
- }
- // 将人的信息换回来
- if (type === '1') {
- // 双打
- } else {
- // 单打
- const users = [];
- for (const i of returnData) {
- const { person } = i;
- for (const p of person) {
- const { user_id } = p;
- const user = await this.userModel.findById(user_id).populate({
- path: 'user_id',
- select: 'name',
- model: this.baseUserModel,
- });
- p.user_name = _.get(user, 'user_id.name');
- }
- }
- }
- return returnData;
- }
- async beforeCreate(body) {
- const r = await this.checkPersonInOtherGroup(body);
- if (!r) throw new BusinessError(ErrorCode.DATA_INVALID, '组员已在其他组,请先将其退出原组后,再添置该组');
- return body;
- }
- async beforeUpdate(filter, update) {
- const r = await this.checkPersonInOtherGroup({ ...filter, ...update });
- if (!r) throw new BusinessError(ErrorCode.DATA_INVALID, '组员已在其他组,请先将其退出原组后,再添置该组');
- return { filter, update };
- }
- async beforeQuery(filter) {
- const { project_name } = filter;
- if (project_name) {
- const projectList = await this.matchProjectModel.find({ name: new RegExp(project_name) }, { _id: 1 });
- const project_id = projectList.map(i => i._id);
- filter.project_id = project_id;
- delete filter.project_name;
- }
- return filter;
- }
- // 检查该组的成员是否在该比赛项目中的其他组中
- // true:没有问题,可以操作; false:有人在其他组,不能允许操作
- async checkPersonInOtherGroup(data) {
- const { person, id, project_id } = data;
- // 没有person,就不需要检查
- if (!person) return true;
- const query = { _id: { $ne: id } };
- if (project_id) query.project_id = project_id;
- else {
- // 没有比赛项目id,也没有数据id: 是新增,但是没有比赛项目id,说明数据有错误
- if (!id) throw new BusinessError(ErrorCode.DATA_INVALID, '新增的数据中缺少比赛项目信息');
- const data = await this.model.findById(id);
- if (!data) throw new BusinessError(ErrorCode.DATA_NOT_EXIST, '未找到要修改的数据');
- query.project_id = _.get(data, 'project_id');
- }
- let res = true;
- for (const p of person) {
- const num = await this.model.count({ ...query, person: p });
- if (num > 0) {
- res = false;
- break;
- }
- }
- return res;
- }
- async afterQuery(filter, data) {
- data = JSON.parse(JSON.stringify(data));
- for (const d of data) {
- const { person, person_type } = d;
- if (person_type === 'Race.User') {
- // 单人
- const users = await this.userModel.find({ _id: person }).populate({ path: 'user_id', model: this.baseUserModel, select: 'name' });
- const parr = person.map(i => {
- const r = users.find(f => ObjectId(f._id).equals(i));
- if (r) return { id: r._id, name: _.get(r, 'user_id.name') };
- return i;
- });
- d.person = parr;
- } else if (person_type === 'Race.TeamApply') {
- // 组队
- const teamApplys = await this.teamApplyModel.find({ _id: person });
- const parr = [];
- for (const p of person) {
- const r = teamApplys.find(f => ObjectId(f._id).equals(p));
- if (r) {
- const { one_member_name, two_member_name } = r;
- parr.push({ id: p, name: `${one_member_name}-${two_member_name}` });
- }
- }
- d.person = parr;
- }
- }
- return data;
- }
- async afterFetch(filter, d) {
- const { person = [], person_type } = d;
- if (person_type === 'Race.User') {
- // 单人
- const users = await this.userModel.find({ _id: person }).populate({ path: 'user_id', model: this.baseUserModel, select: 'name' });
- const parr = person.map(i => {
- const r = users.find(f => ObjectId(f._id).equals(i));
- if (r) return { id: r._id, name: _.get(r, 'user_id.name') };
- return i;
- });
- d.person = parr;
- } else if (person_type === 'Race.TeamApply') {
- // 组队
- const teamApplys = await this.teamApplyModel.find({ _id: person });
- const parr = [];
- for (const p of person) {
- const r = teamApplys.find(f => ObjectId(f._id).equals(p));
- if (r) {
- const { one_member_name, two_member_name } = r;
- parr.push({ id: p, name: `${one_member_name}-${two_member_name}` });
- }
- }
- d.person = parr;
- }
- return d;
- }
- /**
- * 根据该赛事状态,判断是否可以操作
- * 赛事状态为 1,2可以组队, 其余状态不能组队,不能修改
- * @param {String} match_id 赛事id
- * @return {Boolean} 是否可以操作
- */
- async canOpera(match_id) {
- const num = await this.matchModel.count({ _id: match_id, status: [ '1', '2' ] });
- return num > 0;
- }
- }
- module.exports = MatchTeamGroupService;
|