交付出件箱
← 返回出件箱
近视防控 SaaS「家长子席位—礼品卡模式」终版需求立项文档与技术方案
第一部分:立项文档
一、需求背景与目标
1.1 项目背景
品牌方需要将家长子席位账号和密码预先批量生成,并印刷到刮刮卡中,由家长购买刮刮卡后完成以下操作:
- 刮开卡片获取账号和密码;
- 在微信内打开品牌方提供的登录二维码或链接;
- 完成微信授权;
- 输入账号和密码登录;
- 系统记录微信绑定时间;
- 强制修改为新的 6 位数字密码并填写手机号;
- 开始使用近视防控服务。
现有系统存在以下问题:
- 家长账号月度容量较低,仅支持 4 位月序号;
- 不支持单次大批量生成礼品卡账号;
- 批量生成过程可能因同步循环单条插入导致请求超时;
- 密码交付缺少可靠的 Excel 下载机制;
- 明细导出存在每机构固定
page=1,size=200 的硬编码上限,超过 200 条会静默丢失;
- 明细导出未返回绑定时间;
- 时间筛选参数未从前端正确传递至后端;
- 状态筛选仅支持单选;
- 绑定状态与“使用中”定义不一致。
1.2 建设目标
本期建设目标如下:
- 新增礼品卡模式的家长子席位批量开号能力;
- 新家长账号采用全平台按月递增的 10 位新格式;
- 支持一次最多生成 5 万个账号;
- 批量开号采用异步任务和分块批量插入,避免请求超时;
- 生成后导出包含明文密码的 Excel,供品牌方制作刮刮卡;
- 密码仅在生成流程中以明文存在,数据库仅保存密码哈希;
- 支持短期临时文件补下载,过期自动删除;
⚠️ 更正:本节临时文件/N小时自动删除方案已被文末「补充定案」的【Excel 永久保存 + 仅 admin 下载】取代,以补充定案为准。
- 明细导出改为全量导出,并补充绑定时间;
- 支持按开通时间、绑定时间、到期时间进行范围筛选;
- 支持状态多选;
- 保持旧家长账号正常使用,老号不迁移;
- 不建设登录入口和二维码生成功能。
二、功能范围
本期功能分为两块:
- A. 批量开号 / 礼品卡功能
- B. 明细导出增强功能
A. 批量开号 / 礼品卡
A.1 新增批量开号入口
在后台家长子席位管理页面新增“批量开号”功能。
管理员填写:
- 所属机构;
- 生成数量,最大 50,000;
- 其他现有开号必填参数。
提交后:
- 后端立即创建异步任务;
- 接口快速返回任务编号和任务状态;
- 不在当前 HTTP 请求中循环单条开号;
- 管理员可在任务列表查看处理进度、成功数量、失败信息和下载文件。
本期不新增批次号字段,也不在 Excel 中增加批次号列。
A.2 新账号格式
新生成的家长账号格式固定为:
6位全局月序号 + YYMM
例如:
2026年10月第1个新号:0000012610
2026年10月第2个新号:0000022610
账号总长度固定为 10 位。
具体规则:
- 序号部分:
000001 至 999999;
- 月份部分:年份取后两位,月份取两位;
- 2026 年 10 月表示为
2610;
- 全平台按月统一分配;
- 不按机构拆分号段;
- 每月最大容量 999,999 个新账号;
- 账号每月从
000001 重新开始;
- 新号仅用于以后新生成的家长账号;
- 旧账号保持原格式和原数据不变。
旧格式示例:
0001202610
旧号与新号均为 10 位,因此不能通过简单解析账号字符串判断新旧格式。
A.3 密码规则
- 初始密码仍为随机 6 位数字;
- 不调整现有密码长度和规则;
- 初始密码用于刮刮卡印刷;
- 家长首次激活登录后,必须修改为新的 6 位数字密码;
- 数据库仅保存密码哈希,不保存初始密码明文;
- 初始密码明文仅在异步生成任务期间暂存,用于生成一次性 Excel。
A.4 礼品卡激活流程
礼品卡激活沿用现有家长激活逻辑:
- 家长必须在微信内打开登录页面;
- 非微信环境访问直接返回 403;
- 家长完成微信授权;
- 输入卡片上的账号和密码;
- 登录成功;
- 系统写入
seat_bound_at;
- 系统设置或保持
must_change_password=true;
- 强制修改为新的 6 位数字密码;
- 强制填写手机号;
- 完成激活后进入正常使用流程。
有效期起算规则:
- 有效期从家长首次激活登录日计算;
- 不是从批量生成日计算;
- 不是从 Excel 下载日计算;
- 未激活账号不消耗有效期。
A.5 生成后 Excel 下载
批量开号完成后,系统生成 Excel 文件,列固定为:
说明:
- 不输出有效期;
- 不输出批次号;
- 不输出密码哈希;
- “开通日期”表示账号生成/开号日期,不表示激活日期;
- 有效期起算日期不在 Excel 中展示;
- Excel 行数与本次成功生成的账号数一致。
A.6 临时文件补下载
⚠️ 更正:本节临时文件/N小时自动删除方案已被文末「补充定案」的【Excel 永久保存 + 仅 admin 下载】取代,以补充定案为准。
为防止管理员首次下载失败,系统保留一份短期临时文件:
- 默认保留时间:24~72 小时,建议配置为 48 小时;
- 过期后自动删除;
- 仅授权管理员可下载;
- 下载链接必须经过服务端鉴权;
- 不允许通过公开 URL 直接访问;
- 任务失败但账号已经部分生成时,可以重新生成 Excel;
- 重新生成 Excel 不重新创建账号,不重新生成密码;
- Excel 内容必须基于本次已生成账号对应的明文密码临时数据。
A.7 部分失败处理
批量任务可能出现以下情况:
- 已完成部分批量插入,任务进程异常;
- Excel 生成失败;
- 文件上传或落盘失败;
- 任务执行节点重启;
- 部分账号成功、部分账号失败。
处理规则:
- 已成功插入的账号保留,不回滚;
- 已分配的账号序号不回收;
- 任务状态标记为“部分成功”或“失败待补文件”;
- 管理员可以基于已生成账号重新生成 Excel;
- 重新生成 Excel 不重复开号;
- 对没有成功插入的记录,不应出现在本次 Excel 中;
- 对已插入但密码明文临时数据丢失的情况,必须提供明确错误提示,不能凭哈希反推出密码;
- 若需重新补充缺失密码,只能重新创建新账号,不允许为已有账号“重置初始密码并继续使用原卡片”。
B. 明细导出增强
B.1 明细导出改为全量
现有按机构明细导出存在固定:
page=1
size=200
的限制,导致超过 200 条数据后静默丢失。
本期要求:
- 导出不再受 200 条上限限制;
- 导出当前筛选条件下的全部数据;
- 超过 200 条不得静默截断;
- 导出数量必须与查询结果总数一致;
- 建议后端采用流式查询或游标分页,避免一次性加载全部数据到内存;
- 前端下载过程中展示“生成中/下载中”状态。
B.2 明细导出新增绑定时间列
导出文件新增:
绑定时间
字段值来源:
seat_bound_at
含义:
- 表示家长微信绑定成功的时间;
- 未绑定账号为空;
- 不用开号时间代替;
- 不用首次登录时间以外的字段代替;
- 时间格式应统一,例如:
yyyy-MM-dd HH:mm:ss
B.3 时间范围筛选
明细页新增时间范围筛选,并允许用户选择时间类型:
- 开通时间;
- 绑定时间;
- 到期时间。
前端请求参数必须明确传递:
time_type
start_time
end_time
例如:
time_type=opened
start_time=2026-10-01 00:00:00
end_time=2026-10-31 23:59:59
后端根据 time_type 映射查询字段:
| time_type |
查询字段 |
| opened |
账号开通/生成时间 |
| bound |
seat_bound_at |
| expired |
到期时间 |
要求:
start_time 和 end_time 均为空时不增加时间条件;
- 只传开始时间时,查询大于等于开始时间的数据;
- 只传结束时间时,查询小于等于结束时间的数据;
- 开始时间不能晚于结束时间;
- 时间统一按系统约定时区处理;
- 前端展示日期范围时,结束日期应覆盖当天结束时间;
- 修复当前明细弹窗前端未带参数、后端
org_detail 路由无 start/end 通道的问题。
B.4 状态筛选改为多选
状态筛选支持多选。
状态保持三态:
- 待绑定;
- 使用中;
- 已到期。
绑定状态不单独增加为第四种状态。
状态规则:
- 待绑定:未绑定且未到期;
- 使用中:已绑定且未到期;
- 已到期:已达到到期时间,无论是否绑定;
- 已绑定账号归类为“使用中”,不单独显示“已绑定”。
前端请求示例:
status=pending,using
后端使用集合查询:
status IN ('pending', 'using')
若未选择任何状态,则不增加状态过滤条件。
B.5 明细页面和导出条件一致
明细页面查询和 Excel 导出必须复用同一套筛选条件,包括:
- 机构;
- 账号;
- 状态多选;
- 时间类型;
- 开始时间;
- 结束时间;
- 其他已有筛选条件。
避免出现页面展示结果和导出结果不一致。
三、关键设计决策及理由
3.1 账号采用“6 位序号 + YYMM”
该方案已确定,原因如下:
- 月度容量由旧版 9,999 提升到 999,999;
- 账号总长度仍为 10 位;
- 前端输入框无需调整;
- 礼品卡卡面模板无需调整;
- 保留年月信息,便于定位账号生成月份;
- 全平台统一按月分配,避免机构维度的号段管理复杂度;
- 新老账号可以通过数据库字段明确区分,不依赖字符串解析。
3.2 新旧账号不能通过账号字符串解析区分
新旧账号长度相同:
- 旧格式:4 位序号 + YYYYMM;
- 新格式:6 位序号 + YYMM。
因此不能使用以下方式作为唯一判断依据:
- 账号长度;
- 截取后四位;
- 根据年份位置推断;
- 正则表达式猜测。
必须通过以下方式区分:
seat_type 字段;
- 或单独的月度序号计数表;
- 推荐实际实现中同时保留
seat_type,并使用独立计数表生成新号。
3.3 全局按月分配,不按机构拆分
新账号序号是全平台共享资源:
202610 月所有机构共用 000001~999999
原因:
- 满足已拍板的统一容量模型;
- 避免每机构维护独立计数器;
- 避免不同机构生成重复账号;
- 降低批量生成和并发控制复杂度;
- 便于统一监控月度容量。
3.4 密码采用简单可靠方案
本期不采用以下方案:
- KMS;
- AES-GCM;
- 专用高安全对象存储;
- 银行级密钥管理体系;
- 复杂的密码分发系统。
原因:
- 密码为刮刮卡上的 6 位数字;
- 首次激活后必须强制修改;
- 密码本身不是长期使用密码;
- 本期重点是可靠生成和交付,不是构建高敏密钥托管平台;
- 过度设计会增加实施复杂度和运维成本。
采用的方案是:
- 数据库仅保存密码哈希;
- 生成任务中短暂保留明文;
- 明文一次性写入 Excel;
- Excel 短期可补下载;
- 到期自动删除;
- 任务完成后立即清理不再需要的明文数据。
3.5 账号生成采用异步分块批量插入
不能在单个 HTTP 请求中逐条创建 5 万个账号。
采用:
- 异步任务;
- 每批 500~2,000 条批量 INSERT;
- 推荐默认每批 1,000 条;
- 每批独立事务;
- 批次间更新进度;
- 失败后保留已成功提交的数据。
原因:
- 避免请求超时;
- 降低数据库单事务压力;
- 提高吞吐量;
- 便于断点记录和失败定位;
- 支持部分成功后重新生成 Excel。
3.6 不增加批次号
本期明确不增加批次号:
- 数据库不新增用于业务展示的批次号;
- Excel 不输出批次号;
- 前端不提供批次号筛选;
- 如需停用某次生成的数据,按开通日期、机构、账号范围等条件处理;
- 运营上应要求管理员记录开号日期和机构信息。
四、边界与非目标
以下内容明确不在本期范围内:
- 不新增品牌方登录入口;
- 不建设二维码生成能力;
- 不建设二维码投放和渠道追踪;
- 不迁移老家长账号;
- 不修改旧账号格式;
- 不按机构分配独立账号序号;
- 不增加批次号;
- 不在 Excel 中输出有效期;
- 不在 Excel 中输出绑定时间;
- 不改变初始密码为其他长度;
- 不允许管理员查看数据库中的密码明文;
- 不允许从密码哈希恢复密码;
- 不建设 KMS、AES-GCM、专用对象存储等银行级密码保管体系;
- 不将“已绑定”增加为独立状态;
- 不改变现有微信授权和强制改密流程;
- 不为老账号补发新格式账号;
- 不回收已分配但尚未使用的月序号;
- 不因 Excel 生成失败回滚已经成功创建的账号。
五、验收标准
5.1 账号格式与容量
- [ ] 新账号格式为 6 位序号 + YYMM;
- [ ] 新账号总长度为 10 位;
- [ ] 2026 年 10 月生成账号示例为
0000012610;
- [ ] 同月序号从 000001 开始递增;
- [ ] 同月全平台累计最大 999,999 个;
- [ ] 不按机构拆分序号;
- [ ] 新旧账号均为 10 位时,系统不依赖字符串解析判断类型;
- [ ] 新账号通过
seat_type 或明确的新号计数机制识别;
- [ ] 老账号数据不被迁移、不被改号;
- [ ] 下月序号重新从 000001 开始。
5.2 批量开号
- [ ] 单次最多生成 50,000 个账号;
- [ ] 超过 50,000 时前端和后端均拒绝;
- [ ] 批量开号接口异步返回,不同步循环单条创建;
- [ ] 数据库按 500~2,000 条分块批量 INSERT;
- [ ] 生成过程中可查看任务状态和进度;
- [ ] 账号已成功插入后,任务失败不会删除已生成账号;
- [ ] 已分配序号不会因失败回收;
- [ ] 支持对已成功账号重新生成 Excel;
- [ ] 不产生批次号。
5.3 激活和有效期
- [ ] 非微信环境访问返回 403;
- [ ] 微信内打开后必须完成微信授权;
- [ ] 输入正确账号密码后可登录;
- [ ] 首次登录写入
seat_bound_at;
- [ ] 首次登录强制修改 6 位数字密码;
- [ ] 首次登录强制填写手机号;
- [ ] 有效期从首次激活登录日计算;
- [ ] 未激活账号不从开号日开始计算有效期。
5.4 Excel 交付
- [ ] Excel 包含账号、密码、机构名称、开通日期四列;
- [ ] Excel 不包含有效期;
- [ ] Excel 不包含批次号;
- [ ] Excel 中密码为生成时的 6 位明文密码;
- [ ] 数据库不保存密码明文;
- [ ] 生成任务完成后可下载 Excel;
- [ ] 文件支持 24~72 小时短期补下载;
- [ ] 文件到期自动删除;
⚠️ 更正:本节临时文件/N小时自动删除方案已被文末「补充定案」的【Excel 永久保存 + 仅 admin 下载】取代,以补充定案为准。
- [ ] 下载接口具备管理员权限校验;
- [ ] 首次下载失败时支持再次下载;
- [ ] Excel 生成失败时不删除已生成账号;
- [ ] 明文密码临时数据丢失时,不允许伪造或恢复密码。
5.5 明细导出
- [ ] 导出不再固定
page=1,size=200;
- [ ] 超过 200 条时不丢数据;
- [ ] 导出结果数量与筛选查询结果一致;
- [ ] 支持新增“绑定时间”列;
- [ ] 绑定时间来源为
seat_bound_at;
- [ ] 未绑定账号绑定时间为空;
- [ ] 支持按开通时间筛选;
- [ ] 支持按绑定时间筛选;
- [ ] 支持按到期时间筛选;
- [ ] 支持开始时间和结束时间;
- [ ] 前端正确传递时间类型、开始时间、结束时间;
- [ ] 后端
org_detail 路由正确接收并处理时间参数;
- [ ] 状态支持多选;
- [ ] 状态仅有待绑定、使用中、已到期三态;
- [ ] 已绑定账号归类为使用中;
- [ ] 页面查询和 Excel 导出使用同一套筛选条件。
第二部分:技术实现要点
1. 号码生成与号段分配
1.1 数据模型
建议在家长子席位表中明确保留账号类型字段:
seat_type
示例值:
legacy -- 旧格式账号
gift_card -- 礼品卡新格式账号
同时使用独立的全局月度序号表:
CREATE TABLE parent_seat_month_counter (
id BIGINT PRIMARY KEY,
year_month CHAR(4) NOT NULL, -- YYMM,例如 2610
next_sequence INT NOT NULL,
created_at DATETIME NOT NULL,
updated_at DATETIME NOT NULL,
UNIQUE KEY uk_year_month (year_month)
);
推荐将 year_month 实际按业务年月唯一管理。若系统跨世纪运行,需要额外保留完整年月字段,例如:
business_month CHAR(6) NOT NULL -- YYYYMM
display_month CHAR(4) NOT NULL -- YYMM
账号展示仍使用 YYMM,计数唯一性使用完整业务年月,避免不同世纪发生计数器冲突。
1.2 原子分配序号
批量任务不应逐条调用“获取下一个序号”,而应一次申请一个连续号段。
例如本批需要 1,000 个账号:
- 锁定对应月份计数器;
- 读取当前
next_sequence;
- 校验:
next_sequence + 1000 - 1 <= 999999
- 分配:
start_sequence = next_sequence
end_sequence = next_sequence + 999
- 更新:
next_sequence = next_sequence + 1000
- 提交事务;
- 释放锁;
- 应用层按连续序号生成账号。
伪 SQL:
BEGIN;
SELECT next_sequence
FROM parent_seat_month_counter
WHERE business_month = '202610'
FOR UPDATE;
-- 校验 next_sequence + batch_size - 1 <= 999999
UPDATE parent_seat_month_counter
SET next_sequence = next_sequence + :batch_size,
updated_at = NOW()
WHERE business_month = '202610';
COMMIT;
首次使用某月份时,通过唯一索引和事务创建计数器,避免并发初始化产生重复月份记录。
1.3 账号生成
应用层生成账号:
account = LPAD(sequence, 6, '0') + YYMM
示例:
sequence = 1
YYMM = 2610
account = 0000012610
生成时必须同时写入:
account;
seat_type = gift_card;
organization_id;
password_hash;
must_change_password = true;
seat_bound_at = NULL;
opened_at 或现有开号时间字段;
- 有效期相关字段按现有模型初始化,但不能从开号时间开始计时;
- 必要的状态字段。
1.4 并发和唯一性
账号表应存在唯一索引:
UNIQUE KEY uk_parent_account (account)
月度序号表负责分配连续号段,账号唯一索引负责最终兜底。
注意:
- 号段一旦分配,不因后续 INSERT 失败而回收;
- 因此可能出现未被使用的序号空洞;
- 空洞是可接受的,优先保证并发下不重复;
- 不允许通过回收号段降低空洞,否则会增加重复账号风险。
1.5 月度容量校验
批量任务开始前校验:
本月已分配数量 + 本次申请数量 <= 999999
如果不足:
- 任务直接失败;
- 不插入任何本批账号;
- 返回“本月新账号容量不足”;
- 不允许跨月继续占用其他月份序号;
- 不允许超出 999999 生成账号。
2. 一次 5 万账号的异步分块批量 INSERT 流程
2.1 任务创建
接口示例:
POST /admin/parent-seats/batch-create
请求参数:
{
"organization_id": 10001,
"count": 50000
}
接口处理:
- 校验管理员权限;
- 校验机构权限;
- 校验数量范围
1 <= count <= 50000;
- 校验当月剩余容量;
- 创建批量任务记录;
- 投递异步任务;
- 立即返回任务 ID。
任务表建议包含:
batch_task_id
organization_id
requested_count
success_count
failed_count
status
current_chunk
total_chunk
excel_status
error_message
created_by
created_at
finished_at
本表的 batch_task_id 仅用于异步任务技术管理,不作为业务批次号,也不输出到 Excel。
2.2 分块策略
推荐:
单批 1,000 条
允许在配置中设置范围:
500~2,000 条
5 万条账号约分为:
50000 / 1000 = 50 批
每批处理流程:
- 申请一个月度连续号段;
- 在内存中生成本批账号;
- 为每个账号生成随机 6 位数字密码;
- 计算密码哈希;
- 构造批量 INSERT 参数;
- 批量写入数据库;
- 记录本批明文密码映射;
- 更新任务进度;
- 进入下一批。
2.3 密码生成
密码规则:
000000~999999 的随机 6 位数字
要求:
- 使用合适的随机数生成器;
- 必须补齐前导零;
- 例如数字 123 应生成
000123;
- 数据库保存哈希值;
- Excel 使用原始明文;
- 首次激活后强制修改。
建议密码哈希使用系统现有密码哈希方案,例如 bcrypt、Argon2 或现有统一实现,不在本期更换密码哈希体系。
2.4 数据库事务
每个分块建议一个独立事务:
事务开始
申请月度号段
批量 INSERT 家长子席位
事务提交
记录本批明文密码
更新任务进度
需要明确一个关键点:
- 月度号段分配和账号插入最好处于同一事务内;
- 如果 INSERT 失败,号段事务可以回滚;
- 如果号段已经提交后再 INSERT,必须接受号段空洞;
- 不允许在高并发下通过应用层自行重试而重复插入同一账号。
如果数据库和任务系统采用独立事务,必须依赖账号唯一索引和任务幂等标识,避免任务重试重复建号。
2.5 任务幂等
任务执行节点重启后,不能从头无条件重新开号。
应记录:
- 已完成分块序号;
- 已成功账号数量;
- 每批号段起止;
- 每批处理状态。
任务重试时:
- 已完成分块不重复执行;
- 未完成分块继续执行;
- 不能根据
requested_count 重新生成全部账号;
- 不允许重复生成同一任务的密码和账号。
2.6 进度展示
前端可展示:
任务状态:排队中 / 生成中 / Excel生成中 / 成功 / 部分成功 / 失败
已完成:12000 / 50000
成功:12000
失败:0
进度更新不应每插入一条更新一次,建议每批更新一次,降低数据库写压力。
3. 密码明文一次性落 Excel 的可靠交付流程
3.1 明文数据组织
在任务执行期间维护本次成功账号的密码记录:
account -> plaintext_password
由于一次最多 5 万条,短时间内保存 5 万组账号密码在任务进程内是可接受的,但应注意:
- 不打印到日志;
- 不写入普通业务表;
- 不写入任务错误信息;
- 不通过消息队列明文传输;
- 不返回给前端接口;
- 任务结束后及时释放内存。
如果任务执行过程中需要跨节点,应将明文密码放入受访问控制的短期任务工作区,不能依赖普通日志或永久数据库表。
3.2 Excel 生成
全部账号成功写入或任务确定为部分成功后,生成 Excel:
列顺序固定:
账号 | 密码 | 机构名称 | 开通日期
生成内容必须满足:
- 仅包含已成功创建的账号;
- 账号和密码一一对应;
- 机构名称从任务所属机构读取;
- 开通日期使用账号创建时间或系统定义的开号时间;
- 不输出有效期;
- 不输出批次号;
- 不输出密码哈希。
建议采用流式 Excel 写入,避免一次性构造超大内存对象。
3.3 文件保存
文件可以保存到现有文件服务或应用文件目录,但需要满足:
- 文件路径不可被用户直接猜测;
- 下载必须经过后台接口鉴权;
- 文件名不包含密码;
- 文件名可包含机构名称和开号日期;
- 文件记录包含过期时间;
- 定时任务删除过期文件;
- 删除失败要有监控和告警。
例如文件记录:
provision_file_id
task_id
file_path
file_size
expires_at
status
created_at
deleted_at
3.4 下载接口
示例:
GET /admin/parent-seats/batch-tasks/{task_id}/download
服务端校验:
- 当前用户是否为管理员;
- 当前用户是否有对应机构权限;
- 任务是否已生成文件;
- 文件是否未过期;
- 文件是否实际存在;
- 文件状态是否正常。
校验通过后再返回文件流。
3.5 失败处理
场景一:数据库部分成功,Excel 尚未生成
处理:
- 保留已成功创建的账号;
- 任务状态设为“部分成功”;
- 记录成功数量;
- 支持“重新生成 Excel”;
- 不重新开号;
- 重新生成内容只包含已成功账号。
场景二:Excel 生成成功,但首次下载失败
处理:
- 文件继续保留至过期时间;
- 管理员重新下载;
- 不重新生成账号;
- 不重新生成密码。
场景三:文件生成失败且明文密码仍在任务内存
处理:
- 重试 Excel 生成;
- 重试次数受控;
- 不重复插入账号;
- 成功生成文件后更新任务状态。
场景四:进程异常导致明文密码丢失
处理:
- 如果数据库仅保存哈希,无法恢复原始密码;
- 系统必须将任务标记为“密码文件不可恢复”;
- 不得尝试从哈希反推密码;
- 不得生成错误密码文件;
- 如业务必须补卡,只能重新创建新的账号和密码。
3.6 明文清理
以下内容必须清理:
- 任务进程内的明文密码集合;
- 临时 Excel 文件;
- 过期文件;
- 临时目录;
- 任务失败产生的临时中间文件。
注意:
- 应用日志不得输出密码;
- 异常堆栈不得包含整行账号密码数据;
- 任务监控标签不得拼接密码;
- 失败重试不得把明文密码写入消息内容。
4. 明细导出和筛选改造
4.1 去除 200 条导出上限
后端改造
排查并移除以下逻辑:
page = 1
size = 200
以及类似逻辑:
size = Math.min(request.size, 200)
导出接口不应复用只用于页面展示的固定分页参数。
建议新增独立导出接口或明确增加导出模式:
GET /admin/organizations/{org_id}/parent-seats/export
导出实现:
- 使用游标分页;
- 或使用数据库主键范围分页;
- 每次读取固定数量,例如 1,000~5,000 条;
- 边读取边写入 Excel;
- 不将全部结果一次性加载到内存;
- 最终行数应与筛选结果总数一致。
可能的坑
- 直接去掉
LIMIT 可能导致数据库和应用内存压力过大;
- 直接使用大 OFFSET 会越翻越慢;
- 页面查询接口和导出接口字段不一致;
- 导出查询条件漏传状态或时间参数;
- 数据导出过程中新增数据导致结果不稳定。
建议导出开始时确定查询边界,例如记录导出开始时间或使用主键游标,保证一次导出的结果相对稳定。
4.2 新增绑定时间字段
后端查询增加:
seat_bound_at
返回字段建议统一命名:
{
"account": "0000012610",
"seat_bound_at": "2026-10-20 10:30:00"
}
Excel 映射为:
绑定时间
注意:
- SQL 查询字段必须显式列出,不能只依赖旧的
SELECT *;
- 明细页 DTO、导出 DTO、Excel 映射都要同步增加;
- 未绑定时返回空值;
- 不能使用
updated_at 代替;
- 不能使用最后登录时间代替。
4.3 时间筛选参数链路
前端
前端明细弹窗或明细页必须将筛选条件传入接口:
{
"time_type": "bound",
"start_time": "2026-10-01 00:00:00",
"end_time": "2026-10-31 23:59:59"
}
不能只在前端修改显示条件,而不修改请求参数。
后端路由
修改 org_detail 路由或对应明细接口,使其能够接收:
time_type
start_time
end_time
并将参数传递到 Service、Repository、SQL 查询层。
SQL 条件映射
不能直接把前端传入的字段名拼接进 SQL。应使用白名单映射:
switch (timeType) {
case OPENED:
column = "opened_at";
break;
case BOUND:
column = "seat_bound_at";
break;
case EXPIRED:
column = "expired_at";
break;
default:
throw new InvalidParameterException();
}
然后使用参数化查询传入时间值。
时间边界
推荐统一转换为:
[start_time, end_time]
或使用:
>= start_time
< end_time + 1 秒
避免前端传入日期 2026-10-31 时遗漏当天数据。
4.4 状态多选
前端参数
将原单值参数改为数组或逗号分隔值,例如:
{
"status": ["pending", "using"]
}
或:
status=pending,using
项目内部应统一一种格式,推荐数组。
后端处理
先校验状态值只能来自固定集合:
pending
using
expired
再构造集合条件:
status IN (:status_list)
不要将原始字符串直接拼接到 SQL。
状态计算规则
建议在查询层统一计算或使用已有状态字段:
若 expired_at <= now:
expired
否则若 seat_bound_at IS NULL:
pending
否则:
using
如果数据库已有状态字段,也必须确认状态更新逻辑与上述规则一致,特别是:
- 已绑定但已到期,必须为已到期;
- 未绑定且已到期,必须为已到期;
- 已绑定且未到期,必须为使用中;
- 不增加“已绑定”第四态。
页面与导出一致性
状态计算逻辑必须由公共查询服务复用,避免:
- 页面认为某账号是使用中;
- Excel 认为同一账号是已绑定;
- 页面支持多选但导出仍只取单状态;
- 页面筛选为空时导出错误地默认只导出第一页。
4.5 关联索引建议
为保证新筛选和导出性能,应根据实际数据库和数据规模检查索引,至少评估:
(organization_id, opened_at)
(organization_id, seat_bound_at)
(organization_id, expired_at)
(organization_id, status)
(account)
不建议盲目建立所有组合索引,应结合现有查询计划确认。
重点关注:
- 机构条件是否始终命中索引;
- 时间范围筛选是否走索引;
- 状态多选是否造成全表扫描;
- 全量导出是否使用游标;
- 大机构导出是否影响线上明细查询。
4.6 监控与日志
应增加以下监控指标:
- 批量开号任务总数;
- 成功、失败、部分成功任务数;
- 单任务耗时;
- 单批 INSERT 耗时;
- Excel 生成耗时;
- 文件下载次数;
- 文件过期删除失败次数;
- 当月已分配新账号数量;
- 当月剩余可用序号;
- 导出记录数;
- 导出失败次数;
- 导出耗时和文件大小。
日志中必须记录:
- 任务 ID;
- 操作管理员;
- 所属机构;
- 请求数量;
- 成功数量;
- 失败原因;
- 文件生成状态;
- 文件过期时间。
日志中禁止记录:
- 初始密码明文;
- 完整账号密码对;
- Excel 内容;
- 密码哈希原文。
4.7 发布前重点回归
上线前必须重点验证:
- 老格式账号登录不受影响;
- 新格式账号登录不受影响;
- 10 位账号不会被前端截断;
- 月度序号并发生成不重复;
- 月末和跨月生成逻辑正确;
- 单次 50,000 条任务不超时;
- 中途失败后不重复开号;
- Excel 账号与密码一一对应;
- Excel 过期后不可下载;
- 有效期从激活日计算;
seat_bound_at 正确写入并展示;
- 时间筛选实际影响后端查询;
- 开通、绑定、到期三种时间筛选均有效;
- 状态多选结果正确;
- 已绑定账号显示为使用中;
- 超过 200 条导出不丢失;
- 大数据量导出不导致应用内存溢出;
- 失败日志不泄露密码;
- 老账号不被错误识别为新账号;
- 当月超过 999,999 个账号时能够明确阻断并提示。
补充定案(2026-09-18 沟通确认)
以下为立项文档初稿之后、与长官逐条沟通确认的补充决策,与前文一并生效:
1. 开号门槛
- 单次开号 ≤ 10 个:页面即时显示账号密码(店家手动开少量家长号的现有用法不变,可直接复制)。
- 单次开号 > 10 个:转后台生成 + Excel 下载(礼品卡批量场景)。
- 实现补充:11 个到数百个可同步秒生成直接给 Excel;"生成中进度条"仅在数量较大(如上千)时出现;对用户口径统一为"超过 10 个走 Excel"。
2. 批量开号记录(下载记录)
- 入口:加在「导出明细」按钮旁边。
- 列表仅收录以 Excel 交付的批次(单次 > 10 个);≤10 个的即时开通不进此记录。
- 列:开号时间 / 机构 / 数量 / 状态(生成中·已完成)/ 操作(重新下载 Excel)。
- 提示文案:仅显示以 Excel 交付的批量开号记录(单次超过 10 个)。10 个及以内为页面即时开通,不在此列。
3. Excel 永久保存 + 仅 admin 下载
- 账号密码明文 Excel 永久保存、可随时重新下载(不再是 48 小时临时文件)。
- 仅 admin 可下载。
- 🔴 安全补强:永久保存明文登录凭证有风险,至少做访问控制(仅 admin)+ 轻量存储加密;不采用 KMS/AES-GCM 等重方案(与前文"密码简单交付"一致)。
4. Excel 格式
- 列:账号 / 密码 / 机构名称 / 开通日期(4 列,机构名称保留为列)。
- 文件名带机构名 + 日期,例:
红胜利视光中心_子席位账号_20250108.xlsx(文件名与列双保险:文件名一眼识别,列排序处理不丢)。
5. 效果图定稿
- 主流程 4 屏(开通弹窗·生成态·完成态·明细页):subseat-mockup-v3-20260918.html
- 批量开号记录页 + Excel 格式样式:subseat-records-mockup-20260918.html