社群活动、读书会和内部培训,经常需要把一份名单分成若干组。人数不多时,主持人真正需要的可能只是一个打开就能用的小网页:粘贴昵称、填写组数、查看结果、导出留档,不需要账号系统和服务器。
这是一类适合练习AI辅助开发的具体需求。本文从需求确认到制作、测试和交接,完成一个离线活动分组工具。完整代码放在后面,使用浏览器本地运行,不调用模型API。AI可以协助理解需求和改代码,但交付是否合格,要靠输入、输出和实际下载文件来检查。
本文没有真实客户订单或收益案例。本站对这份自写示例运行了函数检查和浏览器操作;这些证据只适用于当前示例,不代表所有设备都已测试,也不代表已有付费需求。
一、先判断客户需要的是哪一种分组
开始写代码前,问清活动人数、组数、是否允许随机分配,以及是否存在必须同组或不能同组的成员。普通随机分组与按年龄、技能、性别等条件配平,是不同范围的需求。
第一版约定:每行一个昵称,最多200人;昵称不能重复;组数是1到人数之间的整数;打乱名单后依次放入各组;各组人数最多相差一人;结果支持JSON导出。
明确不包含账号登录、云端同步、Excel导入、固定组长和跨次活动避重复。这些不是藏起来以后再收费的缺口,而是签约前需要双方确认的范围。 如果客户必须要这些功能,应重新评估实现和验收,不把本例直接说成已经满足。
本工具不适合抽奖、考试分配或其他高风险决策。它使用普通伪随机数,不提供可审计的公平抽签机制;“人数均衡”也不等于能力、经验或其他属性均衡。
二、准备一个能交接的最小目录
电脑上准备文本编辑器和常用浏览器。新建文件夹“活动分组交付”,保存三个文件:grouping.html、使用说明.txt、验收记录.txt。网页包含样式和脚本,不依赖外部字体、图片或库。
首次制作使用“甲、乙、丙”等虚构昵称,不粘贴客户真实名单到AI对话。如果两位参与者同名,可由主持人先改为“小林01、小林02”,不要用电话或身份证号码消除重名。
离线运行表示本例代码没有网络上传逻辑,不表示整个电脑环境天然安全。真实交付仍要检查浏览器插件、共享电脑和文件转发范围,导出的JSON也需要按名单资料管理。
三、怎样让AI辅助,而不是只要一段漂亮页面
可以把下面这段需求发给你已经能正常使用的AI开发助手。它是任务示例,不是某个产品的专用命令,也不要求购买新的模型套餐。
制作一个单文件HTML活动分组网页,不使用外部库或网络请求。
每行一个昵称,去掉首尾空白和空行,最多200人,重复昵称报错。
组数必须是1到人数之间的整数;打乱后轮流分配,人数最多差1。
名单和组数修改后,旧结果立即失效,导出按钮禁用。
输出昵称用textContent,不把用户输入作为HTML执行。
成功后导出二维数组JSON,失败时显示中文错误并禁止旧结果下载。
请解释算法、边界与验收方法,不宣称已在我的设备测试。生成后先检查是否出现外部脚本、远程接口或多余的登录功能。发现新增了需求外的第三方服务,先要求说明用途,再决定是否保留。代码看起来复杂,不是交付价值更高的证据。
四、保存下面的完整网页
把完整代码复制到UTF-8文件grouping.html。Windows保存时检查文件名是否变成grouping.html.txt,文件类型选择所有文件;打开后应显示网页,而不是代码文本。
下面代码是本站自写示例。可以作为普通活动工具的起点;需要改功能时,保留一个未修改副本,便于比较和回退。
<!doctype html>
<html lang="zh-CN">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>活动名单分组练习</title>
<style>
body{max-width:760px;margin:40px auto;padding:20px;font:16px/1.7 system-ui;color:#20332e;background:#f6f8f5}
textarea{box-sizing:border-box;width:100%;min-height:180px}button,input{font:inherit;padding:6px 12px}section{background:white;padding:16px;margin-top:16px;border:1px solid #dce4da;border-radius:12px}#status{color:#a13b27}
</style>
<h1>活动名单分组练习</h1>
<p>使用昵称,每行一人;仅适用于普通活动,不用于抽奖或高风险分配。</p>
<label for="names">名单</label><textarea id="names"></textarea>
<label for="count">组数</label><input id="count" type="number" min="1" value="2">
<button id="run">生成分组</button><button id="download" disabled>导出 JSON</button>
<p id="status" role="status" aria-live="polite"></p><div id="result"></div>
<script>
function makeGroups(text, count, random = Math.random) {
const names = text.split(/\r?\n/).map(x => x.trim()).filter(Boolean);
if (!names.length) throw Error('请先填写名单');
if (names.length > 200) throw Error('本练习最多支持200人');
if (new Set(names).size !== names.length) throw Error('存在重复昵称,请先加编号区分');
if (!Number.isInteger(count) || count < 1 || count > names.length)
throw Error('组数必须是1到人数之间的整数');
const shuffled = [...names];
for (let i = shuffled.length - 1; i > 0; i--) {
const j = Math.floor(random() * (i + 1));
[shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]];
}
const groups = Array.from({length: count}, () => []);
shuffled.forEach((name, i) => groups[i % count].push(name));
return groups;
}
let current = null;
const namesInput = document.querySelector('#names');
const countInput = document.querySelector('#count');
const result = document.querySelector('#result');
const status = document.querySelector('#status');
const download = document.querySelector('#download');
function invalidate() {
current = null; download.disabled = true; result.replaceChildren();
status.textContent = '名单或组数已变化,请重新生成';
}
namesInput.addEventListener('input', invalidate);
countInput.addEventListener('input', invalidate);
document.querySelector('#run').addEventListener('click', () => {
current = null; download.disabled = true; result.replaceChildren();
try {
current = makeGroups(namesInput.value, Number(countInput.value));
current.forEach((members, i) => {
const section = document.createElement('section');
const title = document.createElement('h2'); title.textContent = `第${i + 1}组`;
const body = document.createElement('p'); body.textContent = members.join('、');
section.append(title, body); result.append(section);
});
status.textContent = '已生成,请核对后导出'; download.disabled = false;
} catch (error) { status.textContent = error.message; }
});
download.addEventListener('click', () => {
if (!current) return;
const blob = new Blob([JSON.stringify(current, null, 2)], {type:'application/json;charset=utf-8'});
const url = URL.createObjectURL(blob), a = document.createElement('a');
a.href = url; a.download = 'groups.json'; a.click();
setTimeout(() => URL.revokeObjectURL(url), 1000);
});
</script>
</html>
五、完成第一次分组与导出
双击网页,用浏览器打开。名单里输入五行“甲、乙、丙、丁、戊”,组数填2,点击“生成分组”。应出现两个组,人数分别为3和2,具体成员顺序不固定。
先逐个检查昵称是否都在、是否重复。点击“导出JSON”,浏览器应下载groups.json。用文本编辑器打开,内容是数组里的两个数组,每个内部数组表示一个组,不是表格软件的CSV格式。
不要用重新生成后的屏幕结果核对上一份下载文件。 每次生成可能改变成员顺序,应在同一次结果下完成显示与导出核对,并在交付说明中写清这一点。
如果浏览器没有自动下载,检查下载列表和权限提示,不要反复点击直到产生十几个同名文件。保存位置由浏览器设置决定;有的浏览器会给重复文件名追加编号,这不表示工具生成了额外小组。
六、为什么修改名单后必须让结果失效
容易被忽略的问题是:用户生成了一次结果,又删掉一位成员,却直接点击导出。若网页仍保留上一次结果,交付文件里就可能出现已经退出的参与者。
本例在名单或组数变化时清空结果并禁用下载,提示重新生成。生成失败时也先清除旧结果,因此不能在报错后继续导出上一批名单。这项行为应写进需求,而不是只在开发者心里默认。
重复昵称不会被悄悄删除。同名可能代表两个人,也可能是重复粘贴;程序无法判断,必须请主持人先加编号或修正输入。自动去重虽然方便,却可能把一个真实参与者丢掉。
七、读懂算法与数据处理边界
代码先用换行拆分名单,清理首尾空白和空行,再检查人数、重复昵称与组数。接着用Fisher–Yates方式打乱副本,按组数轮流放入各组,因此人数差最多一人。
随机只影响顺序,不应该改变参与人数。验收不应要求“每次点击都一定不同”,因为合法随机结果也可能相同;更有意义的检查是成员完整、每人一次、组数正确和人数差符合约定。
MDN说明Math.random不适用于需要密码学安全随机数的场景。本例没有把它包装成公平抽奖系统。若客户提出公示、复现或审计要求,需要另外定义随机来源、种子、记录与验证流程,不能只换一个按钮名称。
显示时使用textContent,昵称中的尖括号会按文字处理,而不作为网页标签。导出通过Blob创建临时对象URL,并在触发下载后释放。这些是实现细节对应的检查点,不能由此推导出整个应用已通过安全审计。
八、交付前按清单验收
正常输入:五人两组,确认3与2分配、没有遗漏或重复。再测试一人一组,以及人数等于组数,每组应一人。
错误输入:空名单、重复昵称、组数为0、组数超过人数、非整数组数和超过200人。每一种都应给出可理解的提示,不能留下可下载的旧结果。
状态变化:先生成,再修改一个昵称或组数。结果应清空,导出应禁用;重新生成后才可导出。用键盘Tab检查能到达输入和按钮,错误提示应在页面可见。
文件验收:确认实际下载的JSON可以解析,组数、人数和成员与当前屏幕一致。把网页复制到另一个文件夹再打开,检查是否缺少依赖;计划使用手机时,还要在实际目标手机验证打开与保存流程。
本站针对这份示例完成了601项函数断言,覆盖1至200人的若干组数组合、成员唯一性、人数平衡及非法输入;浏览器另检查五人两组、修改后失效、重复昵称提示和JSON下载。实际下载文件核对为三人两组、成员完整。这些检查不包含所有浏览器,也不包含尚未开发的Excel和条件分组功能。
九、如何把小工具做成可收费的交付
收费对象是明确的问题解决与交付责任,不是“AI生成过多少行代码”。可以先向真实活动组织者了解频率、现有痛点和必需导出格式,用虚构数据演示,再判断是否值得继续开发。
报价时拆开需求沟通、制作、测试、说明文档和约定范围的售后修改。域名、服务器、账号体系并非本例必需成本;如果客户要多人在线协作,成本结构与维护责任都会变化,需要单独说明。
交付包中写明文件名称、版本说明、打开方法、200人限制、重复昵称处理方式和数据保存建议。让客户用自己的非敏感测试名单现场走一遍,而不是只收到一张演示截图。
验收记录保留输入规模、操作步骤、浏览器与系统、结果和未覆盖项;双方确认后再冻结版本。后续提出固定组长或历史分组避重复,应登记为新需求,先确认规则再改代码。
小工具副业能否持续,取决于是否找到反复发生的具体需求,并把可靠、易用和支持边界交代清楚。本例完成了一个可运行的起点,不保证存在订单,更不保证固定收益。

还没有公开评论,欢迎分享你的想法。