Files
dingding_hrm/docs/04-architecture.md

4.9 KiB
Raw Permalink Blame History

架构设计

本文讲系统结构、职责划分、数据模型、技术难点、开发顺序。具体技术选型见 技术栈。

一、系统结构

浏览器
  |
  | 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,保持小模块

五、推荐开发顺序

  1. 初始化 Go module、Gin、健康检查、静态文件托管。
  2. 建立 SQLite 连接、迁移和基础 repository。
  3. 实现站点 CRUD API。
  4. 实现钉钉 token、部门、用户 API client。
  5. 实现全量同步入库和同步日志。
  6. 实现部门、用户、导出 API。
  7. 实现原生前端页面。
  8. 完整验收和运行文档。

六、项目结构建议

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 或业务缓存文件。