diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..be9f0cd --- /dev/null +++ b/docs/README.md @@ -0,0 +1,769 @@ +# 经销商管理系统 + +## 项目简介 + +经销商管理系统是一款面向企业渠道管理的 CRM 系统,旨在解决渠道管理中的核心问题——**客户报备冲突**与**保护期归属**。系统通过唯一有效报备机制与自动到期释放机制,确保同一客户在同一时间只能被一个经销商跟进,并在保护期结束后自动回归可报备池,避免长期占坑,提升渠道效率与公平性。 + +### 核心价值 + +- **防撞单机制**:同一客户同一时间只能有一个有效报备 +- **保护期管理**:自动计算和跟踪保护期,保护期到期自动释放 +- **公平竞争**:避免经销商长期占用客户资源 +- **全程可追溯**:所有报备记录永久保留,支持审计 + +--- + +## 技术架构 + +### 后端技术栈 + +- **框架**: Spring Boot 2.7 +- **ORM**: MyBatis +- **数据库**: MySQL 8 +- **安全**: Spring Security + JWT + BCrypt +- **API 文档**: Swagger +- **构建工具**: Maven + +### 前端技术栈 + +- **框架**: Vue 3 + TypeScript +- **UI 组件**: Element Plus +- **路由**: Vue Router +- **状态管理**: Pinia +- **HTTP 客户端**: Axios +- **构建工具**: Vite +- **代码规范**: ESLint + Prettier + +### 架构模式 + +- **前后端分离**: RESTful API 架构 +- **MVC 分层**: Controller → Service → Mapper → Entity +- **数据库设计**: 遵循范式,合理使用索引 +- **安全设计**: 前端 SHA-256 哈希 + 后端 BCrypt 双重加密 + +--- + +## 用户角色与权限 + +### 1. 管理员 + +**角色标识**: `role = 0` + +**权限范围**: +- ✅ 客户管理(全部客户的查看、新增、编辑、删除) +- ✅ 报备管理(全部报备的查看、审核、撤回) +- ✅ 经销商管理(经销商的增删改查、重置密码) +- ✅ 系统配置(保护期天数等参数配置) +- ✅ 数据报表(经销商活跃度、客户转化率等) +- ✅ 查看所有运营数据 + +**主要职责**: +- 维护客户信息 +- 审核报备申请 +- 管理经销商账号 +- 配置系统参数 +- 监控渠道数据 + +### 2. 经销商用户 + +**角色标识**: `role = 1` + +**权限范围**: +- ✅ 查看自己的客户列表 +- ✅ 提交报备申请(选择客户进行报备) +- ✅ 查看自己的报备记录 +- ✅ 撤回待审核的报备 +- ✅ 查看报备审核状态 +- ✅ 修改个人密码 + +**主要职责**: +- 报备意向客户 +- 跟进保护期内的客户 +- 维护客户信息 +- 管理报备记录 + +### 权限矩阵 + +| 功能模块 | 管理员 | 经销商 | +|---------|--------|--------| +| 客户管理 | ✅ 全部 | ✅ 仅查看 | +| 报备管理 | ✅ 全部 | ✅ 自己的报备 | +| 报备审核 | ✅ | ❌ | +| 经销商管理 | ✅ | ❌ | +| 系统配置 | ✅ | ❌ | +| 数据报表 | ✅ | ❌ | +| 密码管理 | ✅ 自己的 | ✅ 自己的 | + +--- + +## 功能清单 + +### 1. 认证与授权 + +#### 1.1 用户登录 +- **路径**: `POST /api/auth/login` +- **描述**: 用户登录系统,获取 JWT Token +- **安全**: 前端 SHA-256 哈希 + 后端 BCrypt 加密 +- **返回**: Token + 用户基本信息 + +**默认账号**: +- 管理员: `admin` / `admin123` +- 经销商1: `user001` / `admin123` +- 经销商2: `user002` / `admin123` +- 经销商3: `user003` / `admin123` + +#### 1.2 修改密码 +- **路径**: `POST /api/auth/change-password` +- **权限**: 所有用户 +- **功能**: 用户修改自己的登录密码 +- **验证**: 需要输入原密码验证 +- **安全**: 密码 6-20 位,传输前哈希加密 + +#### 1.3 重置密码 +- **路径**: `POST /api/auth/reset-password` +- **权限**: 仅管理员 +- **功能**: 管理员重置经销商用户的密码 +- **限制**: 不能重置管理员账号的密码 + +#### 1.4 退出登录 +- **路径**: `POST /api/auth/logout` +- **功能**: 清除客户端 Token + +--- + +### 2. 客户管理 + +#### 2.1 客户列表 +- **路径**: `GET /api/customer/page` +- **权限**: 管理员查看全部,经销商查看全部 +- **功能**: 分页查询客户信息 +- **搜索字段**: 客户名称、所属行业、状态 +- **状态说明**: + - `0 - 可报备`: 未被保护,可报备 + - `1 - 保护中`: 正在保护期内 + - `2 - 已失效`: 保护期已过 + +#### 2.2 新增客户 +- **路径**: `POST /api/customer` +- **权限**: 仅管理员 +- **功能**: 创建新客户信息 +- **必填字段**: 客户名称 +- **可选字段**: 联系电话、地址、所属行业 +- **学校联动**: 支持从学校库自动完成选择 + +#### 2.3 编辑客户 +- **路径**: `PUT /api/customer/{id}` +- **权限**: 仅管理员 +- **功能**: 修改客户信息 + +#### 2.4 删除客户 +- **路径**: `DELETE /api/customer/{id}` +- **权限**: 仅管理员 +- **功能**: 删除客户记录 +- **限制**: 有报备记录的客户需谨慎处理 + +#### 2.5 客户搜索 +- **路径**: `GET /api/customer/search` +- **功能**: 模糊搜索客户名称(用于报备时的客户选择) + +--- + +### 3. 报备管理 + +#### 3.1 报备列表 +- **路径**: `GET /api/report/page` +- **权限**: 管理员查看全部,经销商查看自己的 +- **功能**: 分页查询报备记录 +- **状态说明**: + - `0 - 待审核`: 等待管理员审核 + - `1 - 已通过`: 审核通过,保护期内 + - `2 - 已驳回`: 审核未通过 + - `3 - 已失效`: 保护期已过 + +#### 3.2 提交报备 +- **路径**: `POST /api/report` +- **权限**: 经销商 +- **功能**: 经销商报备意向客户 +- **必填字段**: 客户ID、报备说明 +- **限制**: + - 同一客户同一时间只能有一个有效报备 + - 已保护的客户无法再次报备 + - 待审核的报备可撤回 + +#### 3.3 撤回报备 +- **路径**: `DELETE /api/report/{id}` +- **权限**: 报备所属经销商 +- **功能**: 撤回待审核的报备申请 +- **限制**: 已通过和已驳回的报备无法撤回 + +#### 3.4 审核报备 +- **路径**: `PUT /api/report/{id}/audit` +- **权限**: 仅管理员 +- **功能**: 审核报备申请 +- **操作**: + - 通过:设置保护期(默认90天),客户状态改为"保护中" + - 驳回:填写驳回原因,客户保持可报备状态 + +#### 3.5 报备详情 +- **路径**: `GET /api/report/{id}` +- **权限**: 根据权限查看 +- **功能**: 查看报备详细信息 + +--- + +### 4. 经销商管理 + +#### 4.1 经销商列表 +- **路径**: `GET /api/dealer/list` +- **权限**: 仅管理员 +- **功能**: 查询所有经销商信息 +- **状态**: 启用/禁用 + +#### 4.2 新增经销商 +- **路径**: `POST /api/dealer` +- **权限**: 仅管理员 +- **必填字段**: 经销商名称、经销商账号(用作登录用户名)、联系人、联系电话 +- **可选字段**: 邮箱、初始密码 +- **自动创建**: + - 创建经销商记录 + - 创建对应的登录用户 + - 用户名 = 经销商账号 + - 默认密码 = 123456(可自定义) + +#### 4.3 编辑经销商 +- **路径**: `PUT /api/dealer/{id}` +- **权限**: 仅管理员 +- **功能**: 修改经销商信息 + +#### 4.4 删除经销商 +- **路径**: `DELETE /api/dealer/{id}` +- **权限**: 仅管理员 +- **功能**: 删除经销商及其关联用户 + +#### 4.5 重置密码 +- **路径**: `POST /api/auth/reset-password` +- **权限**: 仅管理员 +- **功能**: 重置经销商用户的登录密码 + +--- + +### 5. 学校管理 + +#### 5.1 学校列表 +- **路径**: `GET /api/school/list` +- **权限**: 所有用户 +- **功能**: 查询所有学校信息 + +#### 5.2 学校搜索 +- **路径**: `GET /api/school/search?keyword=xxx` +- **权限**: 所有用户 +- **功能**: 模糊搜索学校名称(用于客户名称自动完成) +- **应用场景**: 新增客户时,输入学校名称,自动匹配学校库 + +#### 5.3 批量导入学校 +- **路径**: `POST /api/school/import` +- **权限**: 仅管理员 +- **功能**: 上传 Excel 文件批量导入学校数据 +- **文件格式**: `.xls` 或 `.xlsx` + +#### 5.4 从 docs 目录导入 +- **路径**: `POST /api/school/import-from-docs` +- **权限**: 仅管理员 +- **功能**: 直接导入项目 docs 目录下的 `school.xls` 文件 + +**学校数据结构**: +- 学校标识码 +- 学校名称 +- 所在地(省份 + 城市) + +--- + +### 6. 系统配置 + +#### 6.1 保护期配置 +- **配置项**: `report.protect.days` +- **默认值**: `90` 天 +- **说明**: 控制报备通过后的保护期时长 +- **影响**: + - 保护期计算 + - 自动失效定时任务 + +#### 6.2 报备冲突控制 +- **配置项**: `report.allow-overlap` +- **默认值**: `false` +- **说明**: 是否允许同一客户的报备重叠(仅用于测试) +- **生产**: 必须为 `false` + +--- + +## 业务流程 + +### 报备生命周期 + +``` +┌─────────────────────────────────────────────────────────────┐ +│ 报备完整流程 │ +└─────────────────────────────────────────────────────────────┘ + +1. 经销商提交报备 + ├─ 选择客户(只能选择"可报备"状态的客户) + ├─ 填写报备说明 + └─ 提交审核 + ↓ + 状态:待审核(0) + 客户状态:可报备(0) + +2. 管理员审核 + ├─ 通过 → 设置保护期(如90天) + │ ├─ 报备状态:已通过(1) + │ └─ 客户状态:保护中(1) + │ + └─ 驳回 → 填写驳回原因 + ├─ 报备状态:已驳回(2) + └─ 客户状态:可报备(0) + +3. 保护期内 + ├─ 客户被锁定,其他经销商无法报备 + ├─ 原报备经销商可跟进客户 + └─ 剩余保护天数倒计时 + +4. 保护期到期(定时任务) + └─ 每天凌晨1点自动执行 + ├─ 报备状态:已通过 → 已失效(3) + └─ 客户状态:保护中 → 可报备(0) + +5. 客户释放 + └─ 所有经销商可再次报备该客户 +``` + +### 客户状态流转 + +``` +可报备(0) ──[提交报备]──> 锁定(其他经销商不可报备) + │ + [管理员审核通过] + ↓ + 保护中(1) ──[90天后]──> 可报备(0) + ↑ + [报备被驳回] + │ + [管理员驳回] + ↓ + 可报备(0) +``` + +--- + +## 数据库设计 + +### 核心表结构 + +#### 1. 客户表 (crm_customer) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | BIGINT | 客户ID(主键) | +| name | VARCHAR(200) | 客户名称 | +| phone | VARCHAR(20) | 联系电话 | +| address | VARCHAR(500) | 地址 | +| industry | VARCHAR(50) | 所属行业 | +| status | TINYINT | 状态:0-可报备 1-保护中 2-已失效 | +| current_dealer_id | BIGINT | 当前报备经销商ID | +| protect_end_date | DATE | 保护期结束日期 | +| created_at | DATETIME | 创建时间 | +| updated_at | DATETIME | 更新时间 | + +#### 2. 报备表 (crm_report) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | BIGINT | 报备ID(主键) | +| dealer_id | BIGINT | 经销商ID | +| customer_id | BIGINT | 客户ID | +| description | VARCHAR(500) | 报备说明 | +| status | TINYINT | 状态:0-待审核 1-已通过 2-已驳回 3-已失效 | +| reject_reason | VARCHAR(255) | 驳回原因 | +| protect_start_date | DATE | 保护期开始日期 | +| protect_end_date | DATE | 保护期结束日期 | +| created_at | DATETIME | 创建时间 | +| updated_at | DATETIME | 更新时间 | + +**唯一索引**: `uk_customer_valid (customer_id, status)` - 同一客户只能有一个有效报备 + +#### 3. 经销商表 (crm_dealer) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | BIGINT | 经销商ID(主键) | +| name | VARCHAR(200) | 经销商名称 | +| code | VARCHAR(50) | 经销商账号(唯一) | +| contact_person | VARCHAR(50) | 联系人 | +| contact_phone | VARCHAR(20) | 联系电话 | +| email | VARCHAR(100) | 邮箱 | +| status | TINYINT | 状态:0-禁用 1-启用 | +| created_at | DATETIME | 创建时间 | +| updated_at | DATETIME | 更新时间 | + +#### 4. 用户表 (crm_user) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | BIGINT | 用户ID(主键) | +| username | VARCHAR(50) | 用户名(登录账号) | +| password | VARCHAR(255) | 密码(BCrypt加密) | +| real_name | VARCHAR(50) | 真实姓名 | +| dealer_id | BIGINT | 关联经销商ID(管理员为NULL) | +| role | TINYINT | 角色:0-管理员 1-经销商用户 | +| status | TINYINT | 状态:0-禁用 1-启用 | +| created_at | DATETIME | 创建时间 | +| updated_at | DATETIME | 更新时间 | + +#### 5. 学校表 (crm_school) + +| 字段 | 类型 | 说明 | +|------|------|------| +| id | BIGINT | 学校ID(主键) | +| school_code | VARCHAR(50) | 学校标识码(唯一) | +| school_name | VARCHAR(200) | 学校名称 | +| location | VARCHAR(255) | 所在地(省份+城市) | +| created_at | DATETIME | 创建时间 | +| updated_at | DATETIME | 更新时间 | + +--- + +## 定时任务 + +### 报备过期处理 + +**执行时间**: 每天凌晨 1 点 +**Cron 表达式**: `0 0 1 * * ?` + +**功能**: +1. 查询所有保护期已到的报备(`protect_end_date <= 今天` 且 `status = 1`) +2. 批量更新报备状态为"已失效"(`status = 3`) +3. 批量更新关联客户状态为"可报备"(`status = 0`) +4. 保证渠道资源公平分配 + +--- + +## 安全机制 + +### 1. 认证与授权 + +#### JWT Token 认证 +- 用户登录后获取 JWT Token +- Token 包含:用户ID、用户名、角色、经销商ID +- Token 存储在客户端 `localStorage` +- 每次请求在 Header 中携带:`Authorization: Bearer {token}` + +#### 权限拦截 +- 前端路由守卫:未登录重定向到登录页 +- 后端拦截器:验证 Token 有效性 +- 接口权限注解:`@AuthRequired` +- 数据权限:经销商只能查看自己的报备 + +### 2. 密码安全 + +#### 双重加密 +``` +用户输入 → [前端 SHA-256 哈希] → HTTPS 传输 → [后端 BCrypt 加密] → 存储 +``` + +#### 密码规则 +- 长度:6-20 位 +- 加密:BCrypt(自动加盐) +- 传输:SHA-256 哈希后传输 +- 修改密码:需验证原密码 + +### 3. 数据安全 + +#### SQL 注入防护 +- 使用 MyBatis 参数化查询 +- 所有用户输入都经过参数绑定 + +#### XSS 防护 +- 前端输出自动转义 +- 后端返回 JSON 格式 + +#### CSRF 防护 +- 使用 JWT 无状态认证 +- 不依赖 Session Cookie + +--- + +## 项目结构 + +``` +by-crm/ +├── backend/ # 后端工程 +│ ├── src/main/java/com/bycrm/ +│ │ ├── annotations/ # 自定义注解(权限等) +│ │ ├── common/ # 公共类(Result、Constants等) +│ │ ├── config/ # 配置类(CORS、MyBatis、Swagger等) +│ │ ├── controller/ # 控制器层 +│ │ │ ├── AuthController.java +│ │ │ ├── CustomerController.java +│ │ │ ├── ReportController.java +│ │ │ ├── DealerController.java +│ │ │ └── SchoolController.java +│ │ ├── dto/ # 数据传输对象 +│ │ ├── entity/ # 实体类 +│ │ ├── exception/ # 异常处理 +│ │ ├── mapper/ # MyBatis Mapper 接口 +│ │ ├── service/ # 业务逻辑层 +│ │ ├── task/ # 定时任务 +│ │ ├── util/ # 工具类(JWT、加密等) +│ │ ├── vo/ # 视图对象 +│ │ └── ByCrmApplication.java +│ ├── src/main/resources/ +│ │ ├── mapper/*.xml # MyBatis SQL 映射 +│ │ └── application.yml # 配置文件 +│ └── pom.xml # Maven 依赖 +│ +├── frontend/ # 前端工程 +│ ├── src/ +│ │ ├── api/ # API 接口封装 +│ │ ├── assets/ # 静态资源 +│ │ ├── components/ # 通用组件 +│ │ ├── router/ # Vue Router 配置 +│ │ ├── stores/ # Pinia 状态管理 +│ │ ├── types/ # TypeScript 类型定义 +│ │ ├── utils/ # 工具函数(请求、加密等) +│ │ ├── views/ # 页面组件 +│ │ │ ├── Login.vue # 登录页 +│ │ │ ├── Layout.vue # 主布局 +│ │ │ ├── Dashboard.vue # 首页 +│ │ │ ├── Customer.vue # 客户管理 +│ │ │ ├── Report.vue # 报备管理 +│ │ │ └── Dealer.vue # 经销商管理 +│ │ ├── App.vue # 根组件 +│ │ └── main.ts # 入口文件 +│ ├── vite.config.ts # Vite 配置 +│ ├── package.json # 依赖配置 +│ └── tsconfig.json # TS 配置 +│ +├── sql/ # 数据库脚本 +│ ├── init.sql # 初始化脚本 +│ ├── school_table.sql # 学校表 +│ └── *.sql # 其他 SQL 脚本 +│ +└── docs/ # 文档目录 + ├── api.md # API 文档(Swagger 导出) + └── password-security.md # 密码安全说明 +``` + +--- + +## 环境配置 + +### 后端配置 (application.yml) + +```yaml +server: + port: 8080 + +spring: + datasource: + url: jdbc:mysql://localhost:3306/by_crm + username: root + password: your_password + driver-class-name: com.mysql.cj.jdbc.Driver + +mybatis: + mapper-locations: classpath:mapper/*.xml + type-aliases-package: com.bycrm.entity + +# JWT 配置 +jwt: + secret: your-secret-key + expiration: 86400000 # 24小时(毫秒) + +# 保护期配置 +crm: + report: + ttl-days: 90 # 保护期天数(默认90天) + allow-overlap: false # 是否允许报备重叠(测试用,生产必须false) +``` + +### 前端配置 + +```typescript +// API 基础地址 +const BASE_URL = 'http://localhost:8080/api' + +// Token 存储 +const TOKEN_KEY = 'token' + +// 路由模式:history 或 hash +const routerMode = 'history' +``` + +--- + +## 部署说明 + +### 开发环境启动 + +**后端**: +```bash +cd backend +mvn spring-boot:run +# 访问: http://localhost:8080 +# Swagger: http://localhost:8080/swagger-ui.html +``` + +**前端**: +```bash +cd frontend +pnpm install +pnpm dev +# 访问: http://localhost:5173 +``` + +### 生产环境部署 + +**后端**: +```bash +# 编译打包 +mvn clean package + +# 运行 JAR +java -jar target/bycrm-0.0.1-SNAPSHOT.jar +``` + +**前端**: +```bash +# 编译打包 +pnpm build + +# 部署 dist 目录到 Web 服务器 +``` + +### 数据库初始化 + +```bash +# 创建数据库 +mysql -u root -p -e "CREATE DATABASE by_crm CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci" + +# 导入初始化脚本 +mysql -u root -p by_crm < sql/init.sql +``` + +--- + +## API 接口清单 + +### 认证接口 +- `POST /api/auth/login` - 用户登录 +- `GET /api/auth/user/info` - 获取当前用户信息 +- `POST /api/auth/logout` - 用户退出 +- `POST /api/auth/change-password` - 修改密码 +- `POST /api/auth/reset-password` - 重置密码 + +### 客户接口 +- `GET /api/customer/page` - 分页查询客户 +- `GET /api/customer/{id}` - 获取客户详情 +- `POST /api/customer` - 创建客户 +- `PUT /api/customer/{id}` - 更新客户 +- `DELETE /api/customer/{id}` - 删除客户 +- `GET /api/customer/search?keyword=xxx` - 搜索客户 + +### 报备接口 +- `GET /api/report/page` - 分页查询报备 +- `GET /api/report/{id}` - 获取报备详情 +- `POST /api/report` - 创建报备 +- `DELETE /api/report/{id}` - 撤回报备 +- `PUT /api/report/{id}/audit` - 审核报备 + +### 经销商接口(仅管理员) +- `GET /api/dealer/list` - 查询所有经销商 +- `POST /api/dealer` - 创建经销商 +- `PUT /api/dealer/{id}` - 更新经销商 +- `DELETE /api/dealer/{id}` - 删除经销商 + +### 学校接口 +- `GET /api/school/list` - 查询所有学校 +- `GET /api/school/search?keyword=xxx` - 搜索学校 +- `POST /api/school` - 创建学校 +- `POST /api/school/import` - 文件上传导入 +- `POST /api/school/import-from-docs` - 从 docs 目录导入 + +--- + +## 版本历史 + +### v1.0.0 (当前版本) + +**新增功能**: +- ✅ 客户管理(增删改查) +- ✅ 报备管理(提交、审核、撤回) +- ✅ 经销商管理(增删改查、重置密码) +- ✅ 学校管理(查询、搜索、导入) +- ✅ 用户认证(登录、密码管理) +- ✅ 自动保护期到期处理 +- ✅ 学校数据自动完成功能 +- ✅ 密码传输加密(SHA-256 + BCrypt 双重加密) + +**安全特性**: +- ✅ JWT Token 认证 +- ✅ BCrypt 密码加密存储 +- ✅ SHA-256 前端哈希传输 +- ✅ 权限注解拦截 +- ✅ SQL 注入防护 +- ✅ XSS 防护 + +--- + +## 常见问题 + +### 1. 默认密码是什么? +- 新增经销商时,如果不设置初始密码,默认为 `123456` +- 管理员账号:`admin` / `admin123` +- 经销商账号:`user001~003` / `admin123` + +### 2. 保护期可以修改吗? +- 可以,通过修改系统配置 `report.protect.days` +- 默认为 90 天,可根据业务需求调整 + +### 3. 同一客户可以同时被多个经销商报备吗? +- **不可以**,这是系统的核心规则 +- 同一时间只能有一个"已通过"的报备 +- 其他经销商报备该客户会被拒绝 + +### 4. 保护期到期后会发生什么? +- 定时任务每天凌晨1点自动执行 +- 将过期报备状态改为"已失效" +- 客户状态自动变为"可报备" +- 其他经销商可以再次报备 + +### 5. 如何确保传输安全? +- 前端:使用 Web Crypto API 进行 SHA-256 哈希 +- 传输:HTTPS + 哈希后的密码 +- 后端:BCrypt 二次加密存储 +- 三重安全保障 + +### 6. 管理员可以重置其他管理员密码吗? +- **不可以**,只能重置经销商用户的密码 +- 管理员修改自己的密码通过"修改密码"功能 + +### 7. 学校数据从哪里来? +- 存放在 `docs/school.xls` 文件中 +- 包含:学校标识码、学校名称、所在地 +- 可通过 API 导入到数据库 + +--- + +## 技术支持 + +### 开发团队 +- 后端开发:Spring Boot + MyBatis + MySQL +- 前端开发:Vue 3 + TypeScript + Element Plus +- 项目管理:Maven + pnpm + +### 联系方式 +- 问题反馈:提交 Issue 到项目仓库 +- 技术文档:详见 `docs/` 目录 + +--- + +**版权声明**: 本系统为企业内部使用,未经授权不得复制或传播。 diff --git a/docs/school.xls b/docs/school.xls new file mode 100644 index 0000000..e6a707d Binary files /dev/null and b/docs/school.xls differ