'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 { ObjectId } = require('mongoose').Types; // class MatchSmallGroupScheduleService extends CrudService { constructor(ctx) { super(ctx, 'matchsmallgroupschedule'); this.model = this.ctx.model.Race.MatchSmallGroupSchedule; this.baseUserModel = this.ctx.model.Base.User; this.userModel = this.ctx.model.Race.User; this.teamApplyModel = this.ctx.model.Race.TeamApply; // 加入框架 this.defaultModule = this.app.config.defaultModel || 'Race'; } async result({ match_id, group_id, project_id }) { const { refMods, populate } = this.getRefMods(); let list = await this.model.find({ match_id, group_id, project_id }).populate({ path: 'team_id', model: this.ctx.model.Race.MatchTeamGroup, select: 'name', }); if (list.length <= 0) return; list = JSON.parse(JSON.stringify(list)); list = list.map(i => { const { winner, player_one, player_one_score, player_two, player_two_score } = i; if (winner) return i; if (player_one_score > player_two_score) i.winner = player_one; else i.winner = player_two; i.team_name = _.get(i, 'team_id.name'); i.team_id = _.get(i, 'team_id._id'); return i; }); const newArr = []; for (const i of list) { const d = await this.getPlayerName(i); newArr.push(d); } list = _.groupBy(newArr, 'team_id'); const arr = []; for (const team_id in list) { const groups = list[team_id]; const team_name = _.get(_.head(groups), 'team_name'); const obj = { team_name, team_id }; let players = []; const p1s = groups.map(i => ({ player: i.player_one, player_name: i.player_one_name })); const p2s = groups.map(i => ({ player: i.player_two, player_name: i.player_two_name })); players.push(...p1s, ...p2s); players = _.uniqBy(players, 'player'); // 转换成object[], player:选手; win: 胜场; score:净胜球 players = players.map(i => ({ player: i.player, player_name: i.player_name, win: 0, score: 0 })); for (const g of groups) { const { winner, player_one, player_one_score, player_two, player_two_score } = g; const p1RoundScore = this.getScore(player_one_score, player_two_score); const p2RoundScore = this.getScore(player_two_score, player_one_score); const p1 = players.find(f => f.player === player_one); const p2 = players.find(f => f.player === player_two); // p1,p2谁没有就不行 if (!p1 || !p2) continue; p1.score = p1.score + p1RoundScore || 0; p2.score = p2.score + p2RoundScore || 0; if (winner === player_one) p1.win += 1; else p2.win += 1; } players = _.orderBy(players, [ 'win', 'score' ], [ 'desc', 'desc' ]); obj.score = players; const playerArr = players.map(i => i.player_name); const scoreList = []; for (const p1 of players) { // 每列的第一个位置是选手 const marr = [ p1.player_name ]; for (const p2 of players) { // 循环表头的选手, 自己和自己没分 if (p1.player === p2.player) { marr.push(null); continue; } const r = groups.find(f => (f.player_one === p1.player && f.player_two === p2.player) || (f.player_one === p2.player && f.player_two === p1.player)); if (!r) marr.push(null); else { const { player_one_score, player_two_score } = r; marr.push(`${player_one_score || 0}:${player_two_score || 0}`); } } scoreList.push(marr); } obj.table = [ playerArr, ...scoreList ]; arr.push(obj); } return arr; } /** * 计算净胜分 * @param {String|Number} s1 分数1 * @param {String|Number} s2 分数2 * @return {Number} 净胜分 */ getScore(s1, s2) { return (parseInt(s1) || 0) - (parseInt(s2) || 0); } // 换选手名称 async getPlayerName(data) { if (!data) return data; data = JSON.parse(JSON.stringify(data)); const { player_type, player_one, player_two } = data; if (!player_type) return data; // 人处理 if (player_type === 'Race.User') { if (player_one) { const p1 = await this.userModel.findById(player_one, { user_id: 1 }).populate({ path: 'user_id', model: this.baseUserModel, select: 'name' }); data.player_one_name = _.get(p1, 'user_id.name'); } if (player_two) { const p2 = await this.userModel.findById(player_two, { user_id: 1 }).populate({ path: 'user_id', model: this.baseUserModel, select: 'name' }); data.player_two_name = _.get(p2, 'user_id.name'); } } else if (player_type === 'Race.TeamApply') { if (player_one) { const p1 = await this.teamApplyModel.findById(player_one, { one_member_name: 1, two_member_name: 1 }); data.player_one_name = `${_.get(p1, 'one_member_name')}-${_.get(p1, 'two_member_name')}`; } if (player_two) { const p2 = await this.teamApplyModel.findById(player_two, { one_member_name: 1, two_member_name: 1 }); data.player_two_name = `${_.get(p2, 'one_member_name')}-${_.get(p2, 'two_member_name')}`; } } return data; } async saveAll(data) { for (const i of data) { const { _id } = i; if (!_id) { // TODO: 创建前:需要检查这些人员是否出现在这个项目中的别的组中 await this.model.create(i); } else { await this.model.updateOne({ _id }, i); } } } async beforeCreate(body) { // 检查数据是否有重复,如果符合检查重复条件,就把之前的删了,用新来的 const { match_id, group_id, project_id, team_id, player_one, player_two } = body; const query = { match_id, group_id, project_id, team_id, $or: [ { player_one, player_two }, { player_one: player_two, player_two: player_one }, ], }; const d = await this.model.findOne(query); if (d) await this.model.deleteOne(query); return body; } async beforeQuery(filter) { // 可查询自己的赛程-user_id / 某人的赛程 // const { user_id, user_name } = filter; const user_name = _.get(filter, 'user_name'); let user_id = _.get(filter, 'user_id'); // 没有user_id就不需要处理查询条件 if (!user_id && !user_name) return filter; const usualCondition = _.pick(filter, [ 'match_id', 'group_id', 'project_id' ]); if (user_name) { // 要先找比赛用户模块的数据id const baseUser = await this.baseUserModel.findOne({ name: new RegExp(user_name) }, { _id: 1 }); if (!baseUser) delete filter.user_name; delete filter.user_name; const baseUserId = baseUser._id; const raceUser = await this.userModel.findOne({ user_id: baseUserId, type: '0' }); if (raceUser) user_id = raceUser._id; } if (user_id) { // 需要查:该用户是 单打 和 双打 的情况 // 单打,直接user_id 为player_one/two 就可以,双打需要查teamApply // 所以先把user_id添加进查询范围里 let probablyList = [ user_id ]; // 尽可能的缩小查询范围 // 接着找组队申请中和该用户有关的人 const teamApplyList = await this.teamApplyModel.find({ ...usualCondition, status: '1', $or: [{ one_member_id: user_id }, { two_member_id: user_id }] }, { _id: 1 }); const teamApplyIds = teamApplyList.map(i => i._id); probablyList.push(...teamApplyIds); // 删除user_id.会造成错误 delete filter.user_id; // 添加该用户正确的范围条件 probablyList = probablyList.map(i => ObjectId(i).toString()); filter.$or = [{ player_one: { $in: probablyList } }, { player_two: { $in: probablyList } }]; } return filter; } async afterQuery(filter, data) { data = JSON.parse(JSON.stringify(data)); for (const d of data) { const { player_type, player_one, player_two, referee_id } = d; if (player_type === 'Race.User') { const p1 = await this.userModel.findById(player_one, { user_id: 1 }).populate({ path: 'user_id', model: this.baseUserModel, select: 'name' }); const p2 = await this.userModel.findById(player_two, { user_id: 1 }).populate({ path: 'user_id', model: this.baseUserModel, select: 'name' }); d.player_one_name = _.get(p1, 'user_id.name'); d.player_two_name = _.get(p2, 'user_id.name'); } else if (player_type === 'Race.TeamApply') { const p1 = await this.teamApplyModel.findById(player_one, { one_member_name: 1, two_member_name: 1 }); const p2 = await this.teamApplyModel.findById(player_two, { one_member_name: 1, two_member_name: 1 }); d.player_one_name = `${_.get(p1, 'one_member_name')}-${_.get(p1, 'two_member_name')}`; d.player_two_name = `${_.get(p2, 'one_member_name')}-${_.get(p2, 'two_member_name')}`; } const referee = await this.userModel.findById(referee_id).populate({ path: 'user_id', model: this.baseUserModel, }); d.referee_id_name = _.get(referee, 'user_id.name'); } return data; } async fetch(filter, { sort, desc, projection } = {}) { assert(filter); filter = await this.beforeFetch(filter); const { _id, id } = filter; if (_id || id) filter = { _id: ObjectId(_id || id) }; // 处理排序 if (sort && _.isString(sort)) { sort = { [sort]: desc ? -1 : 1 }; } else if (sort && _.isArray(sort)) { sort = sort.map(f => ({ [f]: desc ? -1 : 1 })).reduce((p, c) => ({ ...p, ...c }), {}); } const { refMods, populate } = this.getRefMods(); let res = await this.model.findOne(filter, projection).populate(populate).exec(); res = JSON.parse(JSON.stringify(res)); for (const obj of refMods) { const { col, prop, type } = obj; if (!prop) continue; if (_.isArray(prop)) { for (const p of prop) { if (type === 'String') res[`${col}_${p}`] = _.get(res, `${col}.${p}`); if (type === 'Array') { const list = []; const oList = _.get(res, `${col}`); for (const d of oList) { const obj = { _id: d._id }; obj[p] = _.get(d, p); list.push(obj); } res[`${col}_${p}`] = list; } } res[col] = _.get(res, `${col}._id`); } } res = await this.afterFetch(filter, res); return res; } async afterFetch(filter, d) { const { player_type, player_one, player_two, referee_id } = d; if (player_type === 'Race.User') { const p1 = await this.userModel.findById(player_one, { user_id: 1 }).populate({ path: 'user_id', model: this.baseUserModel, select: 'name' }); const p2 = await this.userModel.findById(player_two, { user_id: 1 }).populate({ path: 'user_id', model: this.baseUserModel, select: 'name' }); d.player_one_name = _.get(p1, 'user_id.name'); d.player_two_name = _.get(p2, 'user_id.name'); } else if (player_type === 'Race.TeamApply') { const p1 = await this.teamApplyModel.findById(player_one, { one_member_name: 1, two_member_name: 1 }); const p2 = await this.teamApplyModel.findById(player_two, { one_member_name: 1, two_member_name: 1 }); d.player_one_name = `${_.get(p1, 'one_member_name')}-${_.get(p1, 'two_member_name')}`; d.player_two_name = `${_.get(p2, 'one_member_name')}-${_.get(p2, 'two_member_name')}`; } const referee = await this.userModel.findById(referee_id).populate({ path: 'user_id', model: this.baseUserModel, }); d.referee_id_name = _.get(referee, 'user_id.name'); return d; } } module.exports = MatchSmallGroupScheduleService;