'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 })); eliminateData = _.reverse(eliminateData); 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;