Files

3.5 KiB

会员用户分批导出

会员列表、男号管理和女号管理共用导出入口。导出取点击时搜索表单的快照,忽略分页和表格勾选,导出全部匹配用户。后端列表、导出计数和导出批次复用 MemberUserMapper.buildUserQuery,保留列表全部筛选条件。

  • POST /admin-api/member/user/export-async:查询参数与 /member/user/page 相同,包含索引格式的标签数组和时间范围;返回任务 ID。
  • GET /admin-api/member/user/export-progress?taskId=...:返回等待、处理中、成功或失败状态、条数和百分比。
  • GET /admin-api/member/user/export-download?taskId=...:鉴权后从磁盘流式下载 Excel。

三个接口沿用 member:user:query 权限,任务按登录人和租户隔离。文件不上传到公开文件存储。每个用户同一时间只运行一个任务,重复提交返回已有任务,并由前端明确提示本次条件未重新提交。前端每两秒查询一次,查询失败后可重试;刷新页面可通过 sessionStorage 中按租户及用户隔离的任务 ID 恢复。

默认每批 500 人,按 ID 倒序游标读取;开始处理时固定最大 ID,后续新增用户不进入本次任务。地址、标签只按本批用户批量查询。EasyExcel 使用磁盘临时文件,每个 Sheet 最多 100 万行,超出自动新建 Sheet。Excel 不导出内部“用户编号”,保留 App 展示的“用户ID”。手机号和用户 ID 使用文本单元格,保留前导零。零结果生成包含列头的空表。文件关闭并就绪后进度才置为 100%。

导出列为:用户ID、性别、手机号、昵称、用户地址、微信号、用户标签、是否禁用、登录时间、注册时间。头像、头像地址和微信二维码均不导出;导出查询不读取图片字段,也不下载、解码或打包任何图片。数据以批次写入 Excel,每批更新进度;最后生成文件期间保持 99%,文件可下载后变为 100%。数据行高为 24 pt。

配置项均有默认值,无需修改现有业务配置:

yudao:
  member:
    user-export:
      directory: /data/member-user-export
      batch-size: 500
      rows-per-sheet: 1000000
      retention-hours: 24
      workers: 2
      queue-capacity: 8

单实例默认使用 JVM 临时目录下的 yudao-member-user-export。多实例部署必须将 directory 配为所有实例可读写的同一个共享目录,确保负载均衡后的下载请求可读取文件;任务进度、用户锁和实例心跳使用现有 Redis。为该目录预留大文件和 Excel 临时文件所需磁盘空间。任务默认 24 小时过期,超期文件每小时清理一次;临时目录也可能被系统或容器清理,因此前端提示及时下载,不承诺 24 小时内始终可用。服务重启后,失去心跳的任务在两分钟后可被标记为失败并重试。

导出过程中用户资料、标签或地址被修改时,后续批次按当时数据判断条件;预计总数可能变化,完成时更新为实际导出条数。没有开启长事务或全量数据快照。

相关验证命令:

# 后端项目根目录,包含 256 MB 堆下十万行纯文本导出与 SAX 行数校验
mvn -pl yudao-module-member/yudao-module-member-biz -am -Dtest=MemberUserMapperTest,MemberUserExportWriterTest,MemberUserExportServiceTest,MemberUserExportControllerTest -Dsurefire.failIfNoSpecifiedTests=false -DargLine=-Xmx256m -Dflatten.skip=true test

# Friendship-and-chat-pc 项目根目录
node --test tests/member-user-export.test.cjs
npm run build:prod