Skip to content

About

活动签到与参与统计 · 鸿蒙 PC 开源应用:CSV 导入 / 三档判重 / 参与统计 / 一键导出,全程本地处理

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

活动签到与参与统计 · HarmonyOS 应用

verify License: Apache-2.0

一个面向 鸿蒙 PC(HarmonyOS NEXT / 2in1) 的开源示例应用:导入签到结果 → 自动检查重复记录 → 统计参与情况 → 导出汇总报表。

全程本地处理,数据不出设备。


这个 demo 解决什么问题

活动签到最常见的三个坑,这个应用一次处理掉:

坑 表现 本应用的做法
重复导入 同一份签到文件导入两次,或扫码后手工又补录一次,同一个人在同一秒出现多条 自动识别「完全重复」(同人+同活动+同时间),标红并给出清理建议
多次签到 一天里进出会场多次,或签退被当成签到记进来 标为「多次签到」提醒人工确认,不直接当成有效出席
脏数据 姓名缺了、时间写成「待定」、活动 ID 是空的 单独归为「数据异常」,保留行号方便回原表修正

同时给出数据质量分和出席率,让组织者一眼判断"这份数据能不能直接用"。


功能一览

1. 导入签到结果

  • 粘贴 CSV 文本即可导入,列名自动识别(含常见别名)
  • 表头别名兼容:活动ID / 活动编号 / 场次ID;参与者ID / 工号 / 学号 / 手机号;签到时间 / 打卡时间 / 创建时间
  • 时间格式兼容:2026-10-14 09:30:00、2026/10/14 9:30、2026年10月14日 09:30、10/13 位时间戳
  • 分隔符自动嗅探:逗号 / 分号 / Tab / 竖线
  • 正确处理引号转义、字段内换行、UTF-8 BOM
  • 内置两套演示数据(12 行迷你示例 / 379 行完整数据),一键加载即可体验

2. 重复记录检查

三级判重规则,逐条可追溯到原始行号:

类型 判定条件 建议处置
完全重复 同一人 + 同一活动 + 同一时间 直接清理
多次签到 同一人 + 同一活动 + 不同时间 人工确认是否有效出席
数据异常 关键字段缺失 / 时间无法解析 回原表修正

每个「人 + 活动」组合保留时间最早的一条作为有效出席,因此重复记录不会污染实到人数。

3. 参与情况统计

  • 活动维度:应到 / 实到 / 出席率 / 签到条数 / 重复数 / 异常数
  • 参与者维度:跨活动累计有效签到次数、首次与末次签到时间、重复条数
  • 签到时间分布:按小时聚合的柱状图,一眼看出签到高峰与异常集中的批量导入

4. 一键导出汇总

三类 CSV,带 UTF-8 BOM,Excel 双击直接打开不乱码:

  • 参与明细_时间戳.csv —— 每位参与者的有效签到次数、重复条数、时间区间
  • 活动汇总_时间戳.csv —— 每场活动的实到/应到与出席率
  • 重复记录清单_时间戳.csv —— 逐条列出重复记录、原始行号与处置建议

优先写入应用沙箱的 Download 目录(用户可在「文件管理」中直接找到);失败时自动落到沙箱 files/exports,路径在界面上可一键复制。

本应用不申请任何动态权限,因此不存在「拒绝权限后导出不可用」的情况。


界面结构

五个页签,PC 宽屏与窄屏自适应(断点 840vp):

┌──────────────────────────────────────────────────────────┐
│  活动签到与参与统计          [加载演示数据] [导入签到结果] │
├──────────────────────────────────────────────────────────┤
│  数据源:当前 379 条原始记录 · 3 场活动 · 118 位参与者     │
├──────────────────────────────────────────────────────────┤
│  总览 │ 签到明细 │ 重复检查 │ 参与情况 │ 导出汇总          │
├──────────────────────────────────────────────────────────┤
│  ┌─ 指标卡 ─┬─ 指标卡 ─┬─ 指标卡 ─┬─ 指标卡 ─┐            │
│  ├─ 数据质量检查 ────────┬─ 活动出席情况 ────┤            │
│  ├─ 签到时间分布 ─────────────────────────────┤            │
└──────────────────────────────────────────────────────────┘

环境要求

项目 版本
DevEco Studio 5.0.0 及以上(支持 2in1 设备)
HarmonyOS SDK API 12(5.0.0)
目标设备 2in1(鸿蒙 PC)/ 平板 / 手机

工程已声明 deviceTypes: ["2in1", "tablet", "phone"],鸿蒙 PC 上安装即为桌面窗口应用。


快速开始

1. 克隆并打开

git clone https://github.com/Hunshow/harmony-checkin.git
cd harmony-checkin

同一仓库镜像(开放原子 AtomGit,国内访问更快):

git clone https://atomgit.com/gcw_husamPlM/harmony-checkin.git

用 DevEco Studio 打开工程根目录(含 oh-package.json5 的那一层),等待 Sync 完成。

2. 生成图标资源(可选)

图标已随仓库提供。如需重新生成(或改成自己的配色):

node tools/gen-icons.mjs

脚本手写 PNG 编码,不依赖任何图形库,在纯 Node 环境即可运行。

3. 运行

  • 连接鸿蒙 PC / 模拟器,点击 Run 'entry'
  • 首次运行建议先点「加载演示数据」,可立即看到完整统计效果

4. 换包名(Fork 后建议修改)

AppScope/app.json5 里的 bundleName 默认是 com.example.activitycheckin, 改成你自己的域名倒写形式(如 com.yourname.checkin),避免与他人冲突。 改完后在 DevEco Studio 里重新 Sync 一次即可。


项目结构

harmony-checkin/
├── AppScope/
│   ├── app.json5                     # 应用级配置(包名/版本/图标)
│   └── resources/base/
│       ├── element/string.json       # 应用名称
│       └── media/app_icon.png        # 应用图标(PNG 由脚本生成)
├── hvigor/
│   └── hvigor-config.json5           # hvigor 构建配置(modelVersion 需与 oh-package.json5 一致)
├── entry/
│   ├── build-profile.json5
│   ├── oh-package.json5
│   └── src/main/
│       ├── module.json5              # 模块配置(设备类型/权限/Ability)
│       ├── ets/
│       │   ├── entryability/
│       │   │   └── EntryAbility.ets  # 应用入口,处理 PC 窗口尺寸
│       │   ├── model/
│       │   │   └── Types.ets         # 领域模型定义
│       │   ├── utils/
│       │   │   ├── CsvParser.ets     # CSV 解析 + 列名别名 + 时间解析
│       │   │   ├── Statistics.ets    # 判重与统计引擎(纯函数)
│       │   │   ├── Exporter.ets      # CSV 导出
│       │   │   ├── MockData.ets      # 演示数据生成(固定种子,可复现)
│       │   │   └── Logger.ets        # 日志
│       │   ├── common/
│       │   │   ├── Tokens.ets        # 设计令牌(颜色/间距/字号)
│       │   │   └── AppStore.ets      # 全局状态
│       │   ├── viewmodel/
│       │   │   ├── AppData.ets       # 可观测状态
│       │   │   └── AppActions.ets    # 业务动作编排
│       │   ├── components/           # 纯展示组件
│       │   │   ├── SectionCard.ets
│       │   │   ├── StatTile.ets
│       │   │   ├── ActivityRow.ets
│       │   │   ├── RecordTable.ets
│       │   │   ├── ParticipantList.ets
│       │   │   ├── QualityPanel.ets
│       │   │   └── TimeDistributionChart.ets
│       │   └── pages/
│       │       └── Index.ets         # 主页面(页签 + 布局)
│       └── resources/                # 字符串/颜色/多语言
├── docs/
│   ├── sample-checkin.csv            # 379 行示例数据(可直接导入)
│   └── baseline.txt                  # 示例数据的统计基线
└── tools/
    ├── gen-icons.mjs                 # 生成图标 PNG
    └── verify.mjs                    # 核心算法自检(30 条断言)

分层设计

pages/Index          ← 只负责布局与事件绑定
    ↓
viewmodel/           ← 状态(AppData)+ 动作(AppActions)
    ↓
utils/Statistics     ← 纯函数统计引擎(无 UI 依赖,可独立测试)
utils/CsvParser      ← 纯函数解析器
    ↓
model/Types          ← 纯数据定义

统计引擎是纯函数:analyze(records, roster?) → AnalysisResult,不碰任何系统能力。 所以它可以脱离设备、在 Node 里跑测试 —— 这正是 tools/verify.mjs 能成立的原因。


验证统计逻辑

node tools/verify.mjs

会在控制台输出结果,同时写入 verify-report.txt(UTF-8,避免 Windows 控制台中文乱码)。

覆盖 30 条断言,包括:

  • CSV 解析:别名识别、分隔符嗅探、引号转义、字段内换行、BOM、空行
  • 时间解析:多种格式一致性、非法值、时间戳
  • 判重:完全重复 / 多次签到 / 异常的边界,同一秒多条,时间乱序
  • 统计:实名去重口径、出席率计算、跨活动累计
  • 鲁棒性:空输入、只有表头、2000 行以上的大数据集、结果可复现

tools/verify.mjs 是 Statistics.ets / CsvParser.ets 的 JS 同构移植。 修改 ArkTS 侧算法后,请同步更新该文件,否则验证会失真。

示例数据基线

对 docs/sample-checkin.csv(120 人名册 × 3 场活动)的统计结果:

数据行(不含表头): 379
完全重复: 34
多次签到: 16
异常: 9

ACT-2026-001  名册 120 人 / 出席 111 人 / 出席率 92.5%
ACT-2026-002  名册 120 人 / 出席  90 人 / 出席率 75.0%
ACT-2026-003  名册 120 人 / 出席 119 人 / 出席率 99.2%

这份数据刻意埋入了三类脏数据,适合用来验证你的统计口径是否正确。


输入 CSV 格式

最小可用格式(前两列必填):

活动ID,参与者ID,姓名,部门,签到时间,签到方式
ACT-2026-001,E1001,王伟,研发部,2026-10-14 09:05:12,扫码签到
字段 必填 说明 可识别的列名
活动 ID 是 唯一标识一场活动 活动ID、活动编号、场次ID、会议ID
参与者 ID 是 稳定唯一标识(工号/学号/手机号) 参与者ID、工号、学号、手机号、用户ID
姓名 否 展示用 姓名、参与者姓名、员工姓名
部门/分组 否 分组统计用 部门、班级、分组、所属部门
签到时间 否 用于判重与排序,缺失记为异常 签到时间、打卡时间、创建时间
签到方式 否 渠道统计用 签到方式、渠道、来源

为什么参与者 ID 必须稳定唯一? 判重的核心是「同一个人」的识别。如果 ID 是每次签到随机生成的流水号,同一人就无法被聚合, 重复检测会失效。建议用工号、学号或手机号。


统计口径

理解这几个定义,才能看懂报表数字:

实到人数 = 同一活动下,去重后的唯一参与者数量

张三签到了 3 次,实到人数只 +1。这是「出席人数」而不是「签到次数」。

有效出席记录 = 每个「人 + 活动」组合中时间最早的那一条

后续的重复记录被标记但不重复计数,避免虚高。

出席率 = 实到人数 / 名册人数

未导入名册时显示「未提供名册」,而不是显示 0% —— 0% 会被误读成"没人来"。

数据质量分(0~100)= 100 − 扣分占比 × 100

扣分权重:异常记录 ×10、完全重复 ×3、多次签到 ×1。 分数低不代表程序出错,而是提示这份数据需要先清洗。


隐私说明

  • 所有解析、统计、导出均在本机内存与沙箱中完成
  • 不发起任何网络请求,不上传任何数据
  • 导出的 CSV 只在用户主动点击导出时写入本地文件
  • 不申请任何权限:导出走应用自己的沙箱 Download 目录,无需授权即可工作

二次开发指引

想做什么 改哪里
接入新签到系统的列名 CsvParser.ets 的 COLUMN_ALIASES
调整判重规则 Statistics.ets 的 analyze() 第二趟循环
改质量分权重 Statistics.ets 的 calcQualityScore()
加新的导出报表 Statistics.ets 加 buildXxxCsvRows,Index.ets 的 exportTab 加卡片
换主题配色 common/Tokens.ets(UI 层禁止硬编码色值)
接真实数据源(HTTP / 数据库) 在 AppActions.importFromText 前加一个拉取函数,复用下游全部逻辑

已知限制

  • 名册目前由演示数据内置生成,尚未提供名册导入 UI(AppActions.importFromText 已预留 roster 参数)
  • 导出依赖系统「下载目录」权限,部分设备上会只落到应用沙箱
  • CSV 解析按 RFC 4180 实现,未支持 sep= 声明式分隔符头
  • 仅提供浅色主题(EntryAbility 中强制浅色,保证令牌一致性)

常见问题

导入后数字和我手工数的不一样? 先看「重复检查」页签。你的原始文件里很可能有重复行,应用的实到人数是去重后的结果。

为什么有 34 条「完全重复」? 演示数据刻意注入了 10% 的重复率来展示查重能力。用你自己的数据时这个数字取决于导出方式。

能处理多大的文件? 解析与分析都是单趟线性复杂度。实测 2000+ 行无压力。首次跑起来后如需支持十万行级,建议改为分片解析。

签名怎么配? build-profile.json5 的 signingConfigs 为空,DevEco Studio 首次运行时会引导你自动签名。


参与贡献

欢迎提 Issue 和 PR。提交前请确保:

  1. node tools/verify.mjs 全部通过
  2. 若改了 Statistics.ets / CsvParser.ets,已同步更新 tools/verify.mjs
  3. 未引入新的第三方依赖(刻意保持零依赖,降低上手成本)

许可证

Apache License 2.0

About

活动签到与参与统计 · 鸿蒙 PC 开源应用:CSV 导入 / 三档判重 / 参与统计 / 一键导出,全程本地处理

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages