|
@@ -42,7 +42,6 @@ class ClassService extends CrudService {
|
|
const claGroup = _.groupBy(newclass, 'batchid');
|
|
const claGroup = _.groupBy(newclass, 'batchid');
|
|
const keys = Object.keys(claGroup);
|
|
const keys = Object.keys(claGroup);
|
|
const result = {}; // key:班级id;value:学生id数组
|
|
const result = {}; // key:班级id;value:学生id数组
|
|
- const pList = []; // 补人结果
|
|
|
|
// 循环批次
|
|
// 循环批次
|
|
for (const bkey of keys) {
|
|
for (const bkey of keys) {
|
|
const classList = claGroup[bkey]; // 该批次下的班级列表
|
|
const classList = claGroup[bkey]; // 该批次下的班级列表
|
|
@@ -75,9 +74,10 @@ class ClassService extends CrudService {
|
|
|
|
|
|
}
|
|
}
|
|
// 验证最佳结果是否超出班级的最少人数,不是,则处理
|
|
// 验证最佳结果是否超出班级的最少人数,不是,则处理
|
|
|
|
+ // console.log('计算最优解');
|
|
solve = this.checkSolve(solve, minNumber, classnum);
|
|
solve = this.checkSolve(solve, minNumber, classnum);
|
|
- console.log(solve);
|
|
|
|
// 塞人
|
|
// 塞人
|
|
|
|
+ // console.log('塞人前');
|
|
for (const c of classList) {
|
|
for (const c of classList) {
|
|
const { _id } = c;
|
|
const { _id } = c;
|
|
if (!(result[_id] && _.isArray(result[_id]))) result[_id] = [];
|
|
if (!(result[_id] && _.isArray(result[_id]))) result[_id] = [];
|
|
@@ -95,55 +95,125 @@ class ClassService extends CrudService {
|
|
studentGroup[key] = sList;
|
|
studentGroup[key] = sList;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
- // 补人
|
|
|
|
- for (const c of classList) {
|
|
|
|
- const { _id, number, name } = c;
|
|
|
|
- let nowNum = result[_id].length;
|
|
|
|
- const soldup = _.cloneDeep(solve);
|
|
|
|
- while (nowNum < number) {
|
|
|
|
- // 补人,根据solve,获取补人的选择列表
|
|
|
|
- const res = this.getSelects(soldup);
|
|
|
|
- for (const r of res) {
|
|
|
|
- const { schid, gender } = r;
|
|
|
|
- // 获取学生列表
|
|
|
|
- const sList = studentGroup[`${schid}-${gender}`];
|
|
|
|
- const head = _.head(sList);
|
|
|
|
- if (head) {
|
|
|
|
- // 有学生,往班级里推,备选中删除,重新计算当前分配的班级人数,修改solve副本
|
|
|
|
- // result[_id].push(head._id);
|
|
|
|
|
|
+ // console.log('塞人后');
|
|
|
|
+ // console.log('补人前');
|
|
|
|
+ // 检查是否有学生剩余,如果有学生剩余,都需要继续进行人的处理,无论是补人还是加人.
|
|
|
|
+ let els = _.flatten(Object.values(studentGroup)).length;
|
|
|
|
+ // 没有剩余的学生,下面也没法处理,结束了(跳过while了)
|
|
|
|
+ while (els > 0) {
|
|
|
|
+ // 还有学生,那就需要继续处理,看看是补人还是加人
|
|
|
|
+ // 检查各个班级是否达到人数要求
|
|
|
|
+ const { ok, not } = this.checkClassStatus(result, classList);
|
|
|
|
+ // 如果not里有值,说明还有班级没满足人数要求->补人
|
|
|
|
+ // 如果not没有值,说明参培人员多了->分到每个班里
|
|
|
|
+ if (not.length > 0) {
|
|
|
|
+ // 进行补人
|
|
|
|
+ // not中的每个班进行补人,每个班补一个人(result[classid]加人),studentGroup[key]减人
|
|
|
|
+ // 如果没人了,break;
|
|
|
|
+ for (const c of not) {
|
|
|
|
+ // 需要计算出这个班补人的最优解
|
|
|
|
+ const { _id } = c;
|
|
|
|
+ const stuList = result[_id];
|
|
|
|
+ const claSolve = this.getClassSolve(stuList);
|
|
|
|
+ // 根据solve结果,取对应的值,然后做加减
|
|
|
|
+ for (const s of claSolve) {
|
|
|
|
+ const { schid, gender } = s;
|
|
|
|
+ let midList = studentGroup[`${schid}-${gender}`];
|
|
|
|
+ if (midList.length <= 0) continue;
|
|
|
|
+ const head = _.head(midList);
|
|
|
|
+ result[_id].push(head);
|
|
|
|
+ midList = _.drop(midList);
|
|
|
|
+ studentGroup[`${schid}-${gender}`] = midList;
|
|
|
|
+ break;// 一次,一班只补一个的重要关键词
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ } else {
|
|
|
|
+ // 额外加人
|
|
|
|
+ // 说明下这里为什么要ok:因为在上面的solve,强制以班级最少的人数为均分标准
|
|
|
|
+ // 所以在班级人数不均等的情况下,出现某班均分完是正好的人数,剩下的班少人,但这时还得先把少人的班先补上才能均摊多的人
|
|
|
|
+ // 为什么要说明,因为下面代码除了循环的 数组 外,都一样啊
|
|
|
|
+ for (const c of ok) {
|
|
|
|
+ // 需要计算出这个班补人的最优解
|
|
|
|
+ const { _id } = c;
|
|
|
|
+ const stuList = result[_id];
|
|
|
|
+ const claSolve = this.getClassSolve(stuList);
|
|
|
|
+ // 根据solve结果,取对应的值,然后做加减
|
|
|
|
+ for (const s of claSolve) {
|
|
|
|
+ const { schid, gender } = s;
|
|
|
|
+ let midList = studentGroup[`${schid}-${gender}`];
|
|
|
|
+ if (midList.length <= 0) continue;
|
|
|
|
+ const head = _.head(midList);
|
|
result[_id].push(head);
|
|
result[_id].push(head);
|
|
- studentGroup[`${schid}-${gender}`] = _.drop(sList);
|
|
|
|
- nowNum = result[_id].length;
|
|
|
|
- soldup[`${schid}-${gender}`].res++;
|
|
|
|
- pList.push(`${name}班用${schid}-${gender}补人:当前人数为${nowNum};要求${number}`);
|
|
|
|
|
|
+ midList = _.drop(midList);
|
|
|
|
+ studentGroup[`${schid}-${gender}`] = midList;
|
|
break;
|
|
break;
|
|
- } else continue;
|
|
|
|
|
|
+ }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
|
|
+ // 重新计算人数,就是上面的els计算再执行一次
|
|
|
|
+ els = _.flatten(Object.values(studentGroup)).length;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+
|
|
}
|
|
}
|
|
- // console.log(pList);
|
|
|
|
// for (const key in result) {
|
|
// for (const key in result) {
|
|
// console.group(key);
|
|
// console.group(key);
|
|
// const list = result[key];
|
|
// const list = result[key];
|
|
// const b = list.filter(f => f.gender === '男');
|
|
// const b = list.filter(f => f.gender === '男');
|
|
// const g = list.filter(f => f.gender === '女');
|
|
// const g = list.filter(f => f.gender === '女');
|
|
- // console.log(b.length, g.length);
|
|
|
|
|
|
+ // console.log(list.length, b.length, g.length);
|
|
// console.groupEnd();
|
|
// console.groupEnd();
|
|
// }
|
|
// }
|
|
|
|
|
|
// 更新学生的班级
|
|
// 更新学生的班级
|
|
- const ckeys = Object.keys(result);
|
|
|
|
- for (const classid of ckeys) {
|
|
|
|
- await this.stumodel.updateMany({ _id: result[classid] }, { classid });
|
|
|
|
- }
|
|
|
|
- // 新添,给学生排序号
|
|
|
|
- const claList = await this.model.find({ termid });
|
|
|
|
- for (const cla of claList) {
|
|
|
|
- await this.ctx.service.student.arrangeNumber({ classid: cla._id });
|
|
|
|
- }
|
|
|
|
|
|
+ // const ckeys = Object.keys(result);
|
|
|
|
+ // for (const classid of ckeys) {
|
|
|
|
+ // await this.stumodel.updateMany({ _id: result[classid] }, { classid });
|
|
|
|
+ // }
|
|
|
|
+ // // 新添,给学生排序号
|
|
|
|
+ // const claList = await this.model.find({ termid });
|
|
|
|
+ // for (const cla of claList) {
|
|
|
|
+ // await this.ctx.service.student.arrangeNumber({ classid: cla._id });
|
|
|
|
+ // }
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 获取某班补人的最优解列表
|
|
|
|
+ * @param {Array} list 某班级的学生列表
|
|
|
|
+ */
|
|
|
|
+ getClassSolve(list) {
|
|
|
|
+ // 因为按总人数的最优解已经均分过了,所以此处只计算,如何让该班平衡的最优解,不考虑整体了
|
|
|
|
+ const schGroup = _.groupBy(list, 'schid');
|
|
|
|
+ const midArr = [];
|
|
|
|
+ for (const key in schGroup) {
|
|
|
|
+ const midList = schGroup[key];
|
|
|
|
+ const bl = midList.filter(f => f.gender === '男');
|
|
|
|
+ midArr.push({ schid: key, gender: 'boy', number: bl.length });
|
|
|
|
+ const gl = midList.filter(f => f.gender === '女');
|
|
|
|
+ midArr.push({ schid: key, gender: 'girl', number: gl.length });
|
|
|
|
+ }
|
|
|
|
+ // 按 人数升序, 性别先男后女
|
|
|
|
+ return _.orderBy(midArr, [ 'number', 'gender' ], [ 'asc', 'desc' ]);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * 检查班级是否达到人数
|
|
|
|
+ * @param {Array} alreadyList 已分配的列表
|
|
|
|
+ * @param {Array} classList 班级列表
|
|
|
|
+ */
|
|
|
|
+ checkClassStatus(alreadyList, classList) {
|
|
|
|
+ let ok = []; // 已经满足人数的班级
|
|
|
|
+ const not = []; // 未满足人数的班级
|
|
|
|
+ for (const c of classList) {
|
|
|
|
+ const { _id, number } = c;
|
|
|
|
+ const nowNum = alreadyList[_id].length;
|
|
|
|
+ if (nowNum < number) not.push(c); else ok.push(c);
|
|
|
|
+ }
|
|
|
|
+ // 需要排序,需要按上限人数排列,把人少的放上面
|
|
|
|
+ ok = _.orderBy(ok, [ 'number' ], [ 'asc' ]);
|
|
|
|
+ return { ok, not };
|
|
|
|
+ }
|
|
|
|
+
|
|
/**
|
|
/**
|
|
* 算整除和取余
|
|
* 算整除和取余
|
|
* @param {Any} num1 性别人数
|
|
* @param {Any} num1 性别人数
|
|
@@ -187,64 +257,7 @@ class ClassService extends CrudService {
|
|
}
|
|
}
|
|
return solve;
|
|
return solve;
|
|
}
|
|
}
|
|
- // 获取选择结果
|
|
|
|
- getSelects(solve) {
|
|
|
|
- // 做出补人选项的集合:按学校人数,性别人数来看
|
|
|
|
- const keys = Object.keys(solve);
|
|
|
|
- let schids = keys.map(i => {
|
|
|
|
- const arr = i.split('-');
|
|
|
|
- let res;
|
|
|
|
- if (arr[0] && parseInt(arr[0])) res = arr[0];
|
|
|
|
- return res;
|
|
|
|
- });
|
|
|
|
- schids = _.compact(_.uniq(schids));
|
|
|
|
- let genders = keys.map(i => {
|
|
|
|
- const arr = i.split('-');
|
|
|
|
- return arr[1];
|
|
|
|
- });
|
|
|
|
- genders = _.compact(_.uniq(genders));
|
|
|
|
- let schArr = [];
|
|
|
|
- for (const schid of schids) {
|
|
|
|
- const midKeys = keys.filter(f => f.includes(schid));
|
|
|
|
- let count = 0;
|
|
|
|
- for (const k of midKeys) {
|
|
|
|
- count += solve[k].res;
|
|
|
|
- }
|
|
|
|
- const obj = { schid, count };
|
|
|
|
- schArr.push(obj);
|
|
|
|
- }
|
|
|
|
- let genArr = [];
|
|
|
|
- for (const gen of genders) {
|
|
|
|
- const midKeys = keys.filter(f => f.includes(gen));
|
|
|
|
- let count = 0;
|
|
|
|
- for (const k of midKeys) {
|
|
|
|
- count += solve[k].res;
|
|
|
|
- }
|
|
|
|
- const obj = { gender: gen, count };
|
|
|
|
- genArr.push(obj);
|
|
|
|
- }
|
|
|
|
- // 2个数组升序排序
|
|
|
|
- schArr = _.orderBy(schArr, [ 'count' ], [ 'asc' ]);
|
|
|
|
- genArr = _.orderBy(genArr, [ 'count' ], [ 'asc' ]);
|
|
|
|
- const arr = [];
|
|
|
|
- // 优先性别
|
|
|
|
- // for (const gen of genArr) {
|
|
|
|
- // for (const sch of schArr) {
|
|
|
|
- // const { schid } = sch;
|
|
|
|
- // const { gender } = gen;
|
|
|
|
- // arr.push({ schid, gender });
|
|
|
|
- // }
|
|
|
|
- // }
|
|
|
|
- // 优先学校
|
|
|
|
- for (const sch of schArr) {
|
|
|
|
- for (const gen of genArr) {
|
|
|
|
- const { schid } = sch;
|
|
|
|
- const { gender } = gen;
|
|
|
|
- arr.push({ schid, gender });
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return arr;
|
|
|
|
- }
|
|
|
|
|
|
+
|
|
|
|
|
|
// 取得同样类型的学生
|
|
// 取得同样类型的学生
|
|
async getstutype(_students, type) {
|
|
async getstutype(_students, type) {
|