# 学员与证书业务模型

## 1. 核心关系

- 学员是长期主体，证书是某一次学习成果的签发记录。
- 一个学员可以拥有多张证书，证书可以来自不同项目、不同课程、不同模板和不同时间段。
- 证书签发后保存当次课程、阶段、日期和模板代码，不依赖项目默认值继续变化。
- 项目只负责归档和提供默认值，不能代替证书模板定义字段。

## 2. 学员身份规则

- 系统使用标准化后的手机号定位学员，姓名用于一致性校验和业务确认。
- 输入手机号会去除空格、短横线及中国大陆区号，最终保存为 11 位手机号。
- 手机号不存在时，手工签发和证书批量导入都会自动创建学员。
- 手机号已存在且姓名一致时，复用原学员。
- 手机号已存在但姓名不一致时，必须报冲突，不能自动改名或把证书挂到错误学员。
- 确需更名时，应在学员管理中人工修改，并保留姓名历史。

## 3. 模板字段规则

`app/services/certificate_templates.py` 是模板字段的唯一规则源。每个字段定义：

- `key`：后端和前端使用的稳定字段键。
- `label`：页面和 Excel 使用的中文名称。
- `field_type`：文本或日期。
- `required`：该模板签发证书时是否必填。
- `example`：Excel 填写示例。
- `description`：后台提示和 Excel 填写说明。

新增模板时，应先在这里声明字段。手工创建表单、Excel 表头、Excel 校验和后端签发校验都会读取同一份定义。

## 4. 导入流程

### 证书导入

1. 先选择证书模板。
2. 下载该模板专用 Excel，不能跨模板混用。
3. 上传后只做校验，不立即写入正式证书。
4. 管理员确认后调用统一签发服务。
5. 学员不存在时自动创建；重复证书跳过；身份冲突或字段错误进入失败记录。

证书导入的公共业务列是姓名、手机号和项目代码。其余列由所选证书模板决定。

### 学员导入

- 姓名和手机号必填，学员编号和备注选填。
- 只维护学员主数据，不创建证书。
- 已有学员姓名手机号一致时可补充学员编号和备注。
- 冲突行单独返回，不修改已有学员姓名。

## 5. 签发入口

手工创建和证书导入统一调用 `app/services/certificate_issuance.py`，由该服务负责：

- 校验项目和模板；
- 解析或创建学员；
- 校验模板字段和课程日期；
- 判断重复证书；
- 创建证书编号；
- 创建公开链接和二维码令牌。

路由层只处理权限、HTTP 参数、事务提交和操作日志，不应再次复制签发规则。
