109 lines
3.6 KiB
TypeScript
109 lines
3.6 KiB
TypeScript
import type { NextApiRequest, NextApiResponse } from "next";
|
||
import fs from "fs";
|
||
import { store, html } from "@/store";
|
||
import glob from "glob";
|
||
import { promisify } from "util";
|
||
const g = promisify(glob);
|
||
const read = promisify(fs.readFile);
|
||
|
||
// 索引与工时
|
||
const indexToHour: Record<string, number> = JSON.parse(
|
||
fs.readFileSync("./data/hours.json", "utf8")
|
||
).selections;
|
||
|
||
export default async function handler(
|
||
req: NextApiRequest,
|
||
res: NextApiResponse<Record<string, string>>
|
||
) {
|
||
// 读入全部json文件
|
||
// users: {姓名: {坐标: 权重}}
|
||
const users: Record<string, Record<string, number>> = {};
|
||
const files = await g("./data/json/*.json");
|
||
for (const file of files) {
|
||
const jsonStr = await read(file, "utf8");
|
||
const json: {
|
||
user: string;
|
||
selections: Record<string, number>;
|
||
} = JSON.parse(jsonStr);
|
||
|
||
// Normalization
|
||
// 使用 权重值 / 权重值之和
|
||
const { selections } = json;
|
||
const nums = Object.values(selections);
|
||
const sum = nums.reduce((a, b) => a + b);
|
||
const mean = sum / nums.length;
|
||
const std = Math.sqrt(
|
||
nums.map((x) => Math.pow(x - mean, 2)).reduce((a, b) => a + b) /
|
||
nums.length
|
||
);
|
||
for (const index in selections) {
|
||
selections[index] = selections[index] / sum;
|
||
}
|
||
users[json.user] = selections;
|
||
}
|
||
// 计算每一个格子有多少人选择
|
||
// counts: {坐标: {姓名: 权重}}
|
||
const counts: Record<string, Record<string, Record<string, number>>> = {};
|
||
for (const user in users) {
|
||
for (const select in users[user]) {
|
||
if (counts[select] === undefined) counts[select] = {};
|
||
counts[select][user] = users[user];
|
||
}
|
||
}
|
||
|
||
// 构造返回数据, key 为 坐标,value 为姓名
|
||
const resp: Record<string, string> = {};
|
||
|
||
// 找到选择数量最少的格子
|
||
// sortable: [[坐标, 选择人数]]
|
||
// 如果遇到选择数量相同的格子,随机打乱顺序进行迭代
|
||
const sortable: [string, number][] = [];
|
||
for (const index in counts) {
|
||
sortable.push([index, Object.keys(counts[index]).length]);
|
||
}
|
||
sortable.sort((a, b) => a[1] - b[1] || Math.random() - 0.5);
|
||
|
||
// 记录这个人的出现次数,还有这个人的工时总数
|
||
const hoursCount: Record<string, number> = {};
|
||
|
||
for (const [smallestIndex, _] of sortable) {
|
||
// console.log("smallestIndex", smallestIndex);
|
||
// console.log("cell", counts[smallestIndex]);
|
||
// 找到格子里权重最低的人选 [姓名, 小时数, 出现次数, 权重]
|
||
const weightList: [string, number, number][] = [];
|
||
for (const user in counts[smallestIndex]) {
|
||
weightList.push([
|
||
user,
|
||
hoursCount[user] || 0,
|
||
counts[smallestIndex][user][smallestIndex],
|
||
]);
|
||
}
|
||
// 相同工时数量、相同权重的情况下随机选择
|
||
weightList.sort(
|
||
(a, b) => a[1] - b[1] || b[2] - a[2] || Math.random() - 0.5
|
||
);
|
||
console.log("weightList", smallestIndex, weightList);
|
||
const theChoosenUser = weightList[0][0];
|
||
// console.log("theChoosenUser", theChoosenUser);
|
||
// 记录结果
|
||
resp[smallestIndex] = theChoosenUser;
|
||
hoursCount[theChoosenUser] =
|
||
(hoursCount[theChoosenUser] || 0) + indexToHour[smallestIndex];
|
||
}
|
||
// console.log("hoursCount", hoursCount);
|
||
|
||
const sortedHoursCount: [string, number][] = [];
|
||
for (const user in hoursCount) {
|
||
sortedHoursCount.push([user, hoursCount[user]]);
|
||
}
|
||
sortedHoursCount.sort((a, b) => a[1] - b[1]);
|
||
console.log("sortedHoursCount", sortedHoursCount);
|
||
console.log("sortedHoursCount.length", sortedHoursCount.length);
|
||
console.log(
|
||
"user.length === sorted.length",
|
||
Object.keys(users).length === sortedHoursCount.length
|
||
);
|
||
|
||
res.status(200).json(resp);
|
||
}
|