4.9 KiB
4.9 KiB
架构设计
本文讲系统结构、职责划分、数据模型、技术难点、开发顺序。具体技术选型见 技术栈。
一、系统结构
浏览器
|
| GET /, /static/*
| fetch /api/*
v
Go + Gin 单体服务
| |
| +-- 托管 web/ 静态前端文件
|
+-- REST handlers
+-- services: 钉钉 API、同步、导出
+-- repositories: SQLite 读写
| |
| +-- SQLite 数据库
|
+-- 钉钉 OpenAPI
二、职责划分
原生前端
- 渲染站点选择、同步状态、部门树、人员表格、筛选表单。
- 使用
fetch调用同源/api/*。 - 只保存当前页面交互状态,不保存密钥和 token。
- 不直接访问钉钉 OpenAPI。
Gin 后端
- 托管
web/静态文件。 - 提供 REST API。
- 负责请求参数校验、错误响应、分页与导出。
- 封装钉钉 OpenAPI 调用、token 获取与复用。
- 执行同步流程并记录同步日志。
SQLite / repository
- 持久化站点配置、access token、部门、人员、同步日志。
- 保存钉钉返回的关键字段和必要原始 JSON 字段。
- 提供按站点、部门、关键词、状态分页查询。
三、数据模型
3.1 站点与 token
CREATE TABLE sites (
id INTEGER PRIMARY KEY AUTOINCREMENT,
code TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
app_key TEXT NOT NULL,
app_secret TEXT NOT NULL,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
);
CREATE TABLE access_tokens (
id INTEGER PRIMARY KEY AUTOINCREMENT,
site_id INTEGER NOT NULL REFERENCES sites(id) ON DELETE CASCADE,
token TEXT NOT NULL,
expires_in INTEGER NOT NULL,
fetched_at INTEGER NOT NULL,
created_at TEXT NOT NULL
);
3.2 部门
CREATE TABLE departments (
site_id INTEGER NOT NULL REFERENCES sites(id) ON DELETE CASCADE,
dept_id INTEGER NOT NULL,
parent_id INTEGER NOT NULL,
name TEXT NOT NULL,
raw_json TEXT,
updated_at TEXT NOT NULL,
PRIMARY KEY (site_id, dept_id)
);
CREATE INDEX idx_departments_site_parent ON departments(site_id, parent_id);
3.3 人员
CREATE TABLE users (
site_id INTEGER NOT NULL REFERENCES sites(id) ON DELETE CASCADE,
userid TEXT NOT NULL,
name TEXT,
mobile TEXT,
title TEXT,
job_number TEXT,
active INTEGER,
avatar TEXT,
unionid TEXT,
dept_id INTEGER,
dept_id_list TEXT,
dept_name TEXT,
hired_date INTEGER,
raw_json TEXT,
updated_at TEXT NOT NULL,
PRIMARY KEY (site_id, userid)
);
CREATE INDEX idx_users_site_name ON users(site_id, name);
CREATE INDEX idx_users_site_mobile ON users(site_id, mobile);
CREATE INDEX idx_users_site_job_number ON users(site_id, job_number);
CREATE INDEX idx_users_site_dept ON users(site_id, dept_id);
3.4 同步日志
CREATE TABLE sync_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
site_id INTEGER REFERENCES sites(id) ON DELETE SET NULL,
sync_type TEXT NOT NULL,
status TEXT NOT NULL,
message TEXT,
started_at TEXT NOT NULL,
finished_at TEXT
);
四、关键技术难点
| 难点 | 说明 | 应对 |
|---|---|---|
| token 过期 | access_token 有有效期,接口失败可能由 token 过期导致 | 按需获取;过期前刷新;失败日志可见 |
| 部门递归 | 需要从根部门 dept_id=1 拉完整树 |
先实现 BFS/队列同步,并测试重复和空子部门 |
| 用户多部门 | 用户可能属于多个部门,单 dept_id 不足 |
保留 dept_id_list 原始 JSON,MVP 用主部门筛选 |
| 密钥安全 | app_secret 需要存储但不能泄露 | 列表和日志不返回密钥;导出不包含密钥 |
| 静态前端复杂度 | 原生 JS 容易膨胀 | 按页面拆分 web/assets/*.js,保持小模块 |
五、推荐开发顺序
- 初始化 Go module、Gin、健康检查、静态文件托管。
- 建立 SQLite 连接、迁移和基础 repository。
- 实现站点 CRUD API。
- 实现钉钉 token、部门、用户 API client。
- 实现全量同步入库和同步日志。
- 实现部门、用户、导出 API。
- 实现原生前端页面。
- 完整验收和运行文档。
六、项目结构建议
dingding_hrm/
├── docs/
├── main.go
├── go.mod
├── internal/
│ ├── config/
│ ├── db/
│ ├── dingtalk/
│ ├── handler/
│ ├── model/
│ ├── repository/
│ └── service/
├── web/
│ ├── index.html
│ └── assets/
│ ├── app.js
│ └── style.css
├── data/
│ └── dingding_hrm.db
├── AGENTS.md
└── CLAUDE.md
七、架构纪律
handler不直接拼 SQL,不直接调用钉钉 OpenAPI。service编排业务流程,但不处理 HTTP 细节。repository只负责 SQLite 读写和事务。dingtalk包只负责 OpenAPI 请求、响应解析和错误归一。- 前端只调用
/api/*,不保存密钥、token 或业务缓存文件。