功能与角色
业务库角色枚举为 admin、moderator、candidate、voter。侧栏隐藏不能代替服务端校验。
角色矩阵
| 角色 | 登录 | 加入竞选 / 投票 | 竞选与用户管理 | 选票管理 / 开闭投票 | 侧栏「查看排名」 |
|---|---|---|---|---|---|
| admin | 需在 public.users | 不能投票;访问 /campaigns 会跳到 /admin/campaigns | 是 | 是 | 是 |
| moderator | 需在 public.users | 若已有本场 ballots 行则可投 | 否(用户 CRUD 仅 admin) | 是 | 是 |
| voter | 需在 public.users | 自助加入后可投 | 否 | 否 | 侧栏不展示;知道 URL 仍可打开结果页 |
| candidate | 需在 public.users | 与 voter 相同:名簿候选人不绑定此角色 | 否 | 否 | 同 voter |
candidate 角色仍可在用户管理里设置,但当前候选人来自文本名簿。campaigns.candidateCanVote / candidateHasVote 在创建时被服务端固定为 false。
功能概览
用户与登录
- Google OAuth,每次登录弹出账号选择器(
prompt=select_account) public.users非空时,邮箱必须已登记(不区分大小写)- 管理员可单条添加、文本批量、Excel / CSV 导入;不可去掉唯一管理员
- 用户资料含
department、position,用于匹配本场票数规则
竞选
- 创建时必须:名称、至少一条「部门 + 职位 → 票数」规则、候选人名簿(每行一姓名)
ongoing控制投票是否开放;开闭投票在「选票管理」里操作- 保存候选人名单是全量替换:旧选项删除,关联分票因外键级联删除
投票
- 无匹配规则时默认 1 票
- 必须把可投总票数完整分配;金额为正整数
- 提交后不可再次提交
- 管理员不能投票
计票与结果
- 按
ballot_allocations.amount聚合到候选人展示名 - 同票并列,下一排名跳过(竞赛排名)
ongoing = true时结果页只提示等待,不展示排名