# wecom_timesheet **Repository Path**: RabitLogic/wecom_timesheet ## Basic Information - **Project Name**: wecom_timesheet - **Description**: 对接企业微信的员工工时管理系统 - **Primary Language**: Unknown - **License**: MulanPSL-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-08-21 - **Last Updated**: 2026-08-21 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # wecom-timesheet 企业微信打卡考勤工时系统(WeCom Timesheet)—— 使用 MoonBit 编写,数据存储采用 PostgreSQL(通过 libpq FFI 访问)。 ## 功能 - 拉取企业微信打卡数据并同步到 PostgreSQL - 拉取企业微信审批数据(请假 / 加班 / 外勤 / 出差)并同步 - 根据标准工时规则(上下班时间、午休、工作日)计算每日/区间工时 - 汇总生成员工 Timesheet(出勤、标准工时、加班、缺勤) - 提供 CLI 子命令:`migrate`、`sync`、`timesheet` - 提供 Web 看板:Rabbita 前端 + Mocket 后端 - 登录 / 会话(Token)认证 - RBAC 权限管理(角色 → 权限,按权限控制前端页面与 API 访问) - SCSS 样式、员工工时总览 / Timesheet 明细 ## 目录结构 ``` wecom-timesheet/ ├── moon.mod # 模块定义(依赖) ├── docker-compose.yml # PostgreSQL(容器,端口 5433) ├── Makefile # 常用任务(含前端构建 + SCSS 编译) ├── README.md ├── .env.example # 环境变量样例 ├── src/ │ ├── main.mbt # 程序入口(CLI 与初始化) │ ├── auth/ # 认证:用户/密码哈希/Token │ ├── config/ # 配置结构体与加载(含 PG DSN) │ ├── model/ # 领域实体与值对象 │ ├── client/ # HTTP 客户端 + 企业微信 API 封装 │ │ └── wecom/ # AccessToken 缓存、打卡/审批 API、DTO │ ├── service/ # 业务流程、工时计算引擎、考勤规则、登录/RBAC │ ├── repository/ # Repository trait + 文件实现 + PostgreSQL 实现 │ │ └── postgres/ # 迁移(含 RBAC 表)、auth/timesheet 仓库 │ ├── db/ # PostgreSQL 底层(基于 libpq FFI) │ ├── util/ # 时间、重试、JSON 辅助 │ └── error/ # 统一错误类型 ├── web/ # Rabbita 前端(js target,编译为浏览器 JS) │ ├── moon.pkg │ ├── main.mbt # 入口:启动 TEA 应用 + 会话恢复 │ ├── types.mbt # API DTO / 认证模型 / 应用状态 │ ├── update.mbt # 消息与状态更新(TEA update) │ ├── view.mbt # 登录页 / 看板 / 明细视图 │ ├── api.mbt # HTTP 请求与响应解析 │ ├── util.mbt # localStorage 存取 / 格式化工具 │ ├── styles.scss # SCSS 源码(编译为 styles.css,含亮/暗主题) │ ├── pico.min.css # Pico CSS v2 UI 库(本地 vendor,含暗色模式) │ ├── package.json # sass 依赖 + build:css 脚本 │ └── index.html # 页面外壳 └── server/ # Mocket Web 后端(native target) ├── moon.pkg ├── main.mbt # 启动引导 + 路由表 ├── routes.mbt # API 处理函数(登录 / 汇总 / 明细 / 员工) ├── http.mbt # 响应构造 / 路径参数 / 日期区间 └── auth.mbt # 认证与 RBAC(统一 authorize 鉴权) ``` ## 环境要求 - MoonBit 工具链(native target) - PostgreSQL 服务端/客户端(或 Docker) - **libpq 开发头文件**(构建/链接 FFI 需要): ```bash # Debian / Ubuntu sudo apt-get install libpq-dev postgresql # macOS brew install libpq ``` 前端 SCSS 编译需要 Node.js(npm): ```bash cd web && npm install # 安装 sass ``` ## 数据库(Docker,推荐) 项目自带 `docker-compose.yml`,一条命令即可启动 PostgreSQL(端口 **5433**,避免与本机 5432 冲突): ```bash docker compose up -d ``` - 容器名:`wecom-timesheet-pg`(镜像 postgres:17-alpine) - 端口:`5433:5432` - 数据库:`wecom_timesheet`,用户/密码:`postgres` / `postgres` ```bash docker compose logs -f # 查看启动日志 docker compose down # 停止(保留数据卷) ``` ## 快速开始 1. 启动数据库(Docker 或本机 PostgreSQL),并准备数据库: ```sql CREATE DATABASE wecom_timesheet; ``` 2. 配置环境变量(复制 `.env.example` 为 `.env` 并填写): ```bash PG_HOST=127.0.0.1 PG_PORT=5433 PG_DATABASE=wecom_timesheet PG_USER=postgres PG_PASSWORD=postgres PG_SSLMODE=disable ``` 3. 初始化表结构 + RBAC 数据(角色/权限/管理员): ```bash moon run src migrate ``` > 迁移会自动创建 RBAC 表,并初始化权限、角色(admin/hr/employee)与默认管理员 > 账号 **admin / admin123**(首次登录后请修改密码)。 4. 同步企业微信数据(默认同步最近 7 天): ```bash moon run src sync # 指定区间:moon run src sync 2026-08-01 2026-08-31 ``` 5. 生成员工 Timesheet: ```bash moon run src timesheet 1 2026-08-01 2026-08-31 ``` 6. 运行测试: ```bash moon test ``` ## Web 前端看板(Rabbita + Mocket) 前端基于 [Rabbita](https://github.com/moonbit-community/rabbita)(TEA 架构,MoonBit 编译为 JS), 后端基于 [Mocket](https://github.com/oboard/mocket) 框架,复用现有 `model` / `repository` / `service` 与 PostgreSQL 数据。样式使用 **SCSS**(`web/styles.scss` → `styles.css`)+ **Pico CSS v2**(`web/pico.min.css`,本地 vendor,纯 CSS 无 JS 依赖)。 ### IT 风格 UI / 主题 - **Pico CSS v2**:轻量语义化 UI 库(按钮/表单/表格/卡片/排版),纯 CSS、零 JS 依赖,与 Rabbita 渲染的 HTML 完全兼容。 - **扁平克制的企业 IT 风格**:纯色表面 + 简单边框 + 单一主色,无渐变 / 无毛玻璃 / 无辉光;等宽字体展示工时等数字数据,终端风格登录卡片(红黄绿窗口圆点 + `wecom-timesheet :: auth` 标题)、扁平状态徽章、首字头像、简洁进度条、吸顶表头。 - **亮/暗双主题**:基于 `` 切换,`web/styles.scss` 用 CSS 变量定义双主题并覆盖 Pico 变量保持配色一致(亮色近似 GitHub Light、暗色近似 GitHub Dark);登录页与看板头部均有 **🌙/☀️ 切换按钮**,偏好持久化到 `localStorage`,未设置时跟随系统 `prefers-color-scheme`。 - 吸顶顶栏(`● ONLINE` 状态)、汇总卡片(图标 + 强调色)、行 hover / 选中高亮、明细面板卡片化,全部随主题切换。 ### 交互增强 - **快捷日期区间**:今天 / 近7天 / 本周 / 本月 / 上月(由 JS 按本地时区预计算,`web/util.mbt` 的 `js_quick_ranges` FFI)。 - **搜索**:按员工姓名 / 部门实时过滤(客户端)。 - **筛选**:部门下拉、状态下拉(完整 / 缺卡 / 待审批),结果计数实时显示。 - **排序**:点击表头按 员工 / 部门 / 应出勤 / 实际出勤 / 缺勤 / 总工时 / 标准 / 加班 排序,支持升序/降序切换(↑/↓ 指示)。 - **CSV 导出**:按当前筛选 + 排序结果导出 `timesheet_summary.csv`(带 UTF-8 BOM,Excel 可直接打开)。 - **明细面板**:员工考勤详情卡片(头像、状态徽章、总/标准/加班工时、出勤/缺勤统计),打卡记录时间格式化为 `HH:MM`,审批记录带状态徽章。 - 登录后默认加载「本月」区间;加载中 / 空数据 / 出错均有对应状态提示。 - **左侧菜单栏**:考勤看板 / 工时规则 两个页面入口(工时规则仅 admin/hr 可见)。 - **工时规则设置**(`timesheet.manage`):在页面中设定 上班时间 / 下班时间 / 午休开始 / 午休结束(时间选择器)与 工作日(周一~周日 勾选),保存后写入 `settings` 表并立即生效——工时统计实时按新规则计算。未设置时回退环境变量 `RULE_*` 默认值。 > 说明:前端通过 `@json` 反序列化后端响应。按 MoonBit 约定,后端在 > `to_json` 中将 `Int64` 字段(员工 id、分钟数等)序列化为 **字符串**, > 前端 DTO 相应以 `String` 接收(`web/types.mbt`),详见 `src/model/*.mbt`。 ```bash # 一键构建前端(JS 包 + SCSS): make build-frontend # 或手动: # 1. 构建前端 JS 包(输出 _build/js/debug/build/web/web.js) moon build web --target js # 2. 编译 SCSS → CSS cd web && npm run build:css # 3. 启动后端(监听 http://127.0.0.1:8080/,服务静态页面 + JSON API) moon run server --target native ``` 浏览器打开 http://127.0.0.1:8080/ 即可。首次使用请用 **admin / admin123** 登录。 > 提示:后端启动时会连接 PostgreSQL(复用 `PG_*` 环境变量)。请先完成 > `moon run src migrate`,并同步过数据(`moon run src sync`),看板才有数据展示。 ### 登录与会话(JWT) - 未登录访问时前端显示登录页;登录成功后签发 **JWT(HS256,`RabitLogic/mjwt`)** 存入 `localStorage`,刷新页面自动恢复会话。 - JWT 无状态:token 内嵌 `sub/uid/display_name/permissions/iat/exp`,服务端不存储会话(无 sessions 表),鉴权时只校验签名与有效期。 - 所有业务 API 均需在 URL 路径中携带 Token(`/api/summary//...`)。 - 退出登录清除本地 token 即可(服务端无状态,无需作废)。 - 签名密钥通过 `JWT_SECRET` 环境变量配置(生产环境务必改为强随机值,见 `.env.example`)。 ### RBAC 权限 | 权限码 | 说明 | 角色 | | --- | --- | --- | | `timesheet.view` | 查看考勤工时看板 | admin / hr / employee | | `timesheet.manage` | 管理考勤数据 | admin / hr | | `employee.manage` | 管理员工 | admin / hr | | `user.manage` | 用户与角色管理 | admin | 前端按权限隐藏功能(如无 `timesheet.view` 则显示“无权限”);后端在受保护接口上 `guard_permission` 校验,未授权返回 401/403。 ### HTTP API > 说明:Mocket 会剥离 query string,因此 Token 与参数均通过 URL 路径传递。 | 接口 | 说明 | | --- | --- | | `POST /api/login` | 登录(body: `{"username","password"}`),返回 `{token,user}` | | `GET /api/me/` | 当前用户信息与权限 | | `GET /api/logout/` | 退出登录(作废会话) | | `GET /api/summary///` | 全部员工区间工时汇总(日期传 `0` 或空表示缺省) | | `GET /api/timesheet////` | 单个员工 Timesheet 明细 | | `GET /api/employees/` | 员工列表 || `GET /api/rules/:token` | 获取工时规则设置(`timesheet.manage`) | | `POST /api/rules/:token` | 保存工时规则设置(`timesheet.manage`) || `GET /` | 前端页面(`web/index.html`) | | `GET /web.js` | 前端 JS 包 | | `GET /styles.css` | 前端样式(SCSS 编译产物) | | `GET /pico.min.css` | Pico CSS UI 库 | ## 数据库设计 迁移(`src/repository/postgres/migration.mbt`)创建业务表与 RBAC 表: ``` 业务表:employees / checkins / approvals / timesheets RBAC: users 用户(username, password_hash, display_name, status) roles 角色(code, name) permissions 权限(code, name) user_roles 用户-角色 关联 role_permissions 角色-权限 关联 ``` - `users.password_hash` 格式:`salt:sha256(salt:password)`(加盐哈希,见 `src/auth/password.mbt`) - 认证:**无状态 JWT**(`src/auth/token.mbt`,基于 `RabitLogic/mjwt`),HS256 签名,默认 7 天有效 ## 前后端整合与 WASM 可行性 前后端都是 MoonBit:前端 `web/` 编译为 **JS**(Rabbita),后端 `server/` + `src/` 编译为 **native**(Mocket + Postgres)。 - **共享领域层**:`src/util/time_helper.mbt` 已做平台条件化(native 用 C FFI、js 用浏览器时钟),`src/model` 已可同时编译 **native + js**——前端可直接复用领域 DTO,避免重复定义。 - **JWT**:`RabitLogic/mjwt` 是纯 MoonBit,已验证可编译 **native / js / wasm-gc**。 - **完整后端编译为 wasm 跑在浏览器不可行**:Postgres(libpq)、Mocket(native server)、企业微信密钥与 CORS 均为硬限制;浏览器端最多承载纯计算(工时引擎、JWT 校验)与数据展示,数据仍需 native 服务提供。 ## 企业微信配置 | 环境变量 | 说明 | | --- | --- | | `WECOM_CORP_ID` | 企业 ID | | `WECOM_AGENT_ID` | 应用 AgentId | | `WECOM_SECRET` | 应用 Secret | | `WECOM_REDIRECT_URI` | (预留)OAuth 回调地址 | `WECOM_CORP_ID` / `WECOM_SECRET` 用于获取 `access_token`(见 `src/client/wecom/token.mbt`),打卡与审批接口均需携带该 token。 ## 企业微信配置 | 环境变量 | 说明 | | --- | --- | | `WECOM_CORP_ID` | 企业 ID | | `WECOM_AGENT_ID` | 应用 AgentId | | `WECOM_SECRET` | 应用 Secret | | `WECOM_REDIRECT_URI` | (预留)OAuth 回调地址 | `WECOM_CORP_ID` / `WECOM_SECRET` 用于获取 `access_token`(见 `src/client/wecom/token.mbt`),打卡与审批接口均需携带该 token。 ## 标准工时规则 通过以下环境变量配置(见 `src/config/config.mbt`): | 环境变量 | 默认值 | 说明 | | --- | --- | --- | | `RULE_WORK_START` | `09:00` | 上班时间 | | `RULE_WORK_END` | `18:00` | 下班时间 | | `RULE_LUNCH_START` | `12:00` | 午休开始 | | `RULE_LUNCH_END` | `13:00` | 午休结束 | | `RULE_WORKDAYS` | `1,2,3,4,5` | 工作日(1=周一 … 7=周日) | 工时计算规则(`src/service/calculator.mbt`): - 实际工时 = 下班 - 上班 - 午休重叠时长 - 标准工时 = 实际工时 与 规则期望工时 的较小值 - 加班工时 = 实际工时 - 标准工时(不小于 0) - 缺勤天数 = 区间内应出勤工作日 - 有打卡记录的天数 ## License Apache-2.0