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 = JSON.parse( fs.readFileSync("./data/hours.json", "utf8") ).selections; export default async function handler( req: NextApiRequest, res: NextApiResponse> ) { // 读入全部json文件 // users: {姓名: {坐标: 权重}} const users: Record> = {}; const files = await g("./data/json/*.json"); for (const file of files) { const jsonStr = await read(file, "utf8"); const json: { user: string; selections: Record; } = 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>> = {}; 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 = {}; // 找到选择数量最少的格子 // 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 = {}; 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); }