## 10:50 — AI 调用记录方案评估

**话题：** 记录 AI 调用（session_id, tokens, 返回结果），复用操作日志机制

**评估结论：方案 A — 扩展 OperLog**
- `sys_oper_log` 新增 5 字段：`ai_session_id`, `ai_model`, `prompt_tokens`, `completion_tokens`, `total_tokens`
- `module` 复用为 `"AI识别"` / `"AI对话"` 区分业务
- 复用现有 OperLog 全部基础设施（service/mapper/controller/分页/查询）
- 工作量最小，约 20min
- 方案已确认，待实现

## 10:50 — Git 约定提醒

提示 commit 时必须附带 commit message，让用户可直接复制运行。

## 11:08~11:18 — AI 调用记录实现 ✅

**改动点：**
1. `scripts/postgresql/10-system.sql` — DDL 新增 `ai_session_id`, `ai_model`, `prompt_tokens`, `completion_tokens`, `total_tokens`
2. `scripts/postgresql/fix-ai-oper-log.sql` — 迁移脚本（新增）
3. `OperLog.java` — 实体添加 5 个对应字段
4. `AiServiceImpl.java` — 注入 `OperLogService`，在 `chatText()`/`chatWithImages()` 调用前后记录日志（含 sessionId, tokens, 耗时, 输入/输出摘要）
5. `AiChatResponseDTO` — 新增 `sessionId` 字段
6. `DeepSeekChatRequestDTO` — 新增 `sessionId` 字段（`@JsonIgnore`，仅用于日志传递）

**执行迁移：** 本地库（localhost:31983）跑 `fix-ai-oper-log.sql`

**测试结果：** ✅ `Tests run: 2, Failures: 0, Errors: 0` — 日志成功写入 sys_oper_log（ai_model=qwen3.5-plus 等字段正确）
## 13:00~13:06 — 测试库执行 AI 迁移 ✅

- 本地 WSL Docker 可连接测试库（`docker exec -i tiny-erp-db psql -U postgres -d tinyerp`）
- 执行 `fix-ai-oper-log.sql`，5 个 AI 字段（`ai_session_id`, `ai_model`, `prompt_tokens`, `completion_tokens`, `total_tokens`）已添加
- 验证本地库（localhost:31983）和测试库（Docker db:5432）字段均存在 ✅

## 13:08~13:10 — 日志文件冲突问题 → 方案A

**问题：** IDEA 和 Docker 都在 WSL 同一机器运行，`./logs/` 会互相覆盖

**方案A：** docker-compose 挂载路径改为 `./logs-docker:/app/logs`
- `.gitignore` 添加 `logs-docker/`
- `Dockerfile` 保留 root 运行（无 `USER appuser`）
- 已实施 ✅

## 13:11~13:13 — deploy-test.sh 脚本

创建 `deploy-test.sh` 一键部署脚本：
1. `mvn clean install -DskipTests`
2. `docker compose stop/rm/build/up app`
3. `curl health` 自动验证（失败重试 15s）

## 13:14~13:22 — 测试目录规范化

**问题：** 大部分测试包路径缺少 `module` 层级，与源码不一致

- 移动所有测试到 `com.witsoft.erp.module.*` 包下
- 统一先删旧包（git rm）、再建新包、再写入
- 运行全量测试通过 ✅
- `test.base.BaseControllerTest` 保留在 `com.witsoft.erp.test.base`（基建归 test，不归 module）

## 13:38~14:11 — docs/ 目录全面清理

**整理方向：**

| 操作 | 说明 |
|------|------|
| ✅ 合并 API 规范到项目规范.md | 删除 `docs/API/` |
| ✅ 删除 `docs/数据库/` | 以 SQL 脚本为唯一来源 |
| ✅ 文档按模块移入对应子目录 | `06-AI单据识别需求.md` → `需求/AI模块`，`物料信息填充公共服务总结.md` → `设计/主数据模块` |
| ✅ 按模块独立编号 | 全局连续 → 每个模块 01- 起编 |
| ✅ 测试文件重排序 | 按 登录→首页→物料→字典→入库出库→库存盘点→操作日志 排列 |
| ✅ README.md 同步 | 目录结构、版本历史逐一同步，标注"（预留）"空目录 |

**编号规则变更：** 跨模块连续编号（旧）→ 按模块独立编号（新），新增文档只加模块内序号，不碰其他模块。

## 14:14 — UserContext 使用指南.md 文件缺失

文档中引用 `设计/系统模块/UserContext 使用指南.md`，但实际文件不存在（被 `yangxuan/` → `docs/` 重命名遗漏）。已补充。
## 10:50 — AI 调用记录方案评估

**话题：** 记录 AI 调用（session_id, tokens, 返回结果），复用操作日志机制

**评估结论：方案 A — 扩展 OperLog**
- `sys_oper_log` 新增 5 字段：`ai_session_id`, `ai_model`, `prompt_tokens`, `completion_tokens`, `total_tokens`
- `module` 复用为 `"AI识别"` / `"AI对话"` 区分业务
- 复用现有 OperLog 全部基础设施（service/mapper/controller/分页/查询）
- 工作量最小，约 20min
- 方案已确认，待实现

## 10:50 — Git 约定提醒

提示 commit 时必须附带 commit message，让用户可直接复制运行。

## 11:08~11:18 — AI 调用记录实现 ✅

**改动点：**
1. `scripts/postgresql/10-system.sql` — DDL 新增 `ai_session_id`, `ai_model`, `prompt_tokens`, `completion_tokens`, `total_tokens`
2. `scripts/postgresql/fix-ai-oper-log.sql` — 迁移脚本（新增）
3. `OperLog.java` — 实体添加 5 个对应字段
4. `AiServiceImpl.java` — 注入 `OperLogService`，在 `chatText()`/`chatWithImages()` 调用前后记录日志（含 sessionId, tokens, 耗时, 输入/输出摘要）
5. `AiChatResponseDTO` — 新增 `sessionId` 字段
6. `DeepSeekChatRequestDTO` — 新增 `sessionId` 字段（`@JsonIgnore`，仅用于日志传递）

**执行迁移：** 本地库（localhost:31983）跑 `fix-ai-oper-log.sql`

**测试结果：** ✅ `Tests run: 2, Failures: 0, Errors: 0` — 日志成功写入 sys_oper_log（ai_model=qwen3.5-plus 等字段正确）
## 13:00~13:06 — 测试库执行 AI 迁移 ✅

- 本地 WSL Docker 可连接测试库（`docker exec -i tiny-erp-db psql -U postgres -d tinyerp`）
- 执行 `fix-ai-oper-log.sql`，5 个 AI 字段（`ai_session_id`, `ai_model`, `prompt_tokens`, `completion_tokens`, `total_tokens`）已添加
- 验证本地库（localhost:31983）和测试库（Docker db:5432）字段均存在 ✅

## 13:08~13:10 — 日志文件冲突问题 → 方案A

**问题：** IDEA 和 Docker 都在 WSL 同一机器运行，`./logs/` 会互相覆盖

**方案A：** docker-compose 挂载路径改为 `./logs-docker:/app/logs`
- `.gitignore` 添加 `logs-docker/`
- `Dockerfile` 保留 root 运行（无 `USER appuser`）
- 已实施 ✅

## 13:11~13:13 — deploy-test.sh 脚本

创建 `deploy-test.sh` 一键部署脚本：
1. `mvn clean install -DskipTests`
2. `docker compose stop/rm/build/up app`
3. `curl health` 自动验证（失败重试 15s）

## 13:14~13:22 — 测试目录规范化

**问题：** 大部分测试包路径缺少 `module` 层级，与源码不一致

- 移动所有测试到 `com.witsoft.erp.module.*` 包下
- 统一先删旧包（git rm）、再建新包、再写入
- 运行全量测试通过 ✅
- `test.base.BaseControllerTest` 保留在 `com.witsoft.erp.test.base`（基建归 test，不归 module）

## 13:38~14:11 — docs/ 目录全面清理

**整理方向：**

| 操作 | 说明 |
|------|------|
| ✅ 合并 API 规范到项目规范.md | 删除 `docs/API/` |
| ✅ 删除 `docs/数据库/` | 以 SQL 脚本为唯一来源 |
| ✅ 文档按模块移入对应子目录 | `06-AI单据识别需求.md` → `需求/AI模块`，`物料信息填充公共服务总结.md` → `设计/主数据模块` |
| ✅ 按模块独立编号 | 全局连续 → 每个模块 01- 起编 |
| ✅ 测试文件重排序 | 按 登录→首页→物料→字典→入库出库→库存盘点→操作日志 排列 |
| ✅ README.md 同步 | 目录结构、版本历史逐一同步，标注"（预留）"空目录 |

**编号规则变更：** 跨模块连续编号（旧）→ 按模块独立编号（新），新增文档只加模块内序号，不碰其他模块。

## 14:14 — UserContext 使用指南.md 文件缺失

文档中引用 `设计/系统模块/UserContext 使用指南.md`，但实际文件不存在（被 `yangxuan/` → `docs/` 重命名遗漏）。已补充。

## 14:18 — docs/测试 HTTP 文件重排序 ✅

按执行依赖逻辑重新编号：

| 序号 | 文件 | 说明 |
|------|------|------|
| 00 | 登录登出 | 先拿 token |
| 01 | 首页 API 测试 | 仪表盘概览 |
| 02 | 物料分页 | 基础数据查询 |
| 03 | 数据字典与映射 | 字典+翻译 |
| 04 | 入库出库 | 核心业务流程 |
| 05 | 实时库存&盘点 | 库存状态 |
| 06 | 操作日志 | 审计兜底 |

同步更新了 `docs/README.md`。

## 14:24 — scripts/postgresql 目录组织讨论

**话题：** 杨轩问脚本目录是否可以按 ddl/dml/init 或按模块区分。

**分析结果：** 当前脚本目录的问题：
1. 编号混乱（00/03/10/20/21/22...99，无一致规则）
2. DDL/DML 混在一起（如 10-system.sql 既建表又插数据）
3. fix- 前缀不统一（26-material-last-price.sql 也是 ALTER 但没用 fix- 前缀）

**方案A（用户选中）：** 二级目录 `ddl/`、`dml/`、`migration/`、`mock/`，根目录保留一个 `init.sql` 通过 `\i` 引用子目录文件（因为 `docker-entrypoint-initdb.d` 只处理扁平 .sql 文件）。

**待实现：** [not yet started — compaction happened before implementation]
## 10:50 — AI 调用记录方案评估

**话题：** 记录 AI 调用（session_id, tokens, 返回结果），复用操作日志机制

**评估结论：方案 A — 扩展 OperLog**
- `sys_oper_log` 新增 5 字段：`ai_session_id`, `ai_model`, `prompt_tokens`, `completion_tokens`, `total_tokens`
- `module` 复用为 `"AI识别"` / `"AI对话"` 区分业务
- 复用现有 OperLog 全部基础设施（service/mapper/controller/分页/查询）
- 工作量最小，约 20min
- 方案已确认，待实现

## 10:50 — Git 约定提醒

提示 commit 时必须附带 commit message，让用户可直接复制运行。

## 11:08~11:18 — AI 调用记录实现 ✅

**改动点：**
1. `scripts/postgresql/10-system.sql` — DDL 新增 `ai_session_id`, `ai_model`, `prompt_tokens`, `completion_tokens`, `total_tokens`
2. `scripts/postgresql/fix-ai-oper-log.sql` — 迁移脚本（新增）
3. `OperLog.java` — 实体添加 5 个对应字段
4. `AiServiceImpl.java` — 注入 `OperLogService`，在 `chatText()`/`chatWithImages()` 调用前后记录日志（含 sessionId, tokens, 耗时, 输入/输出摘要）
5. `AiChatResponseDTO` — 新增 `sessionId` 字段
6. `DeepSeekChatRequestDTO` — 新增 `sessionId` 字段（`@JsonIgnore`，仅用于日志传递）

**执行迁移：** 本地库（localhost:31983）跑 `fix-ai-oper-log.sql`

**测试结果：** ✅ `Tests run: 2, Failures: 0, Errors: 0` — 日志成功写入 sys_oper_log（ai_model=qwen3.5-plus 等字段正确）
## 13:00~13:06 — 测试库执行 AI 迁移 ✅

- 本地 WSL Docker 可连接测试库（`docker exec -i tiny-erp-db psql -U postgres -d tinyerp`）
- 执行 `fix-ai-oper-log.sql`，5 个 AI 字段（`ai_session_id`, `ai_model`, `prompt_tokens`, `completion_tokens`, `total_tokens`）已添加
- 验证本地库（localhost:31983）和测试库（Docker db:5432）字段均存在 ✅

## 13:08~13:10 — 日志文件冲突问题 → 方案A

**问题：** IDEA 和 Docker 都在 WSL 同一机器运行，`./logs/` 会互相覆盖

**方案A：** docker-compose 挂载路径改为 `./logs-docker:/app/logs`
- `.gitignore` 添加 `logs-docker/`
- `Dockerfile` 保留 root 运行（无 `USER appuser`）
- 已实施 ✅

## 13:11~13:13 — deploy-test.sh 脚本

创建 `deploy-test.sh` 一键部署脚本：
1. `mvn clean install -DskipTests`
2. `docker compose stop/rm/build/up app`
3. `curl health` 自动验证（失败重试 15s）

## 13:14~13:22 — 测试目录规范化

**问题：** 大部分测试包路径缺少 `module` 层级，与源码不一致

- 移动所有测试到 `com.witsoft.erp.module.*` 包下
- 统一先删旧包（git rm）、再建新包、再写入
- 运行全量测试通过 ✅
- `test.base.BaseControllerTest` 保留在 `com.witsoft.erp.test.base`（基建归 test，不归 module）

## 13:38~14:11 — docs/ 目录全面清理

**整理方向：**

| 操作 | 说明 |
|------|------|
| ✅ 合并 API 规范到项目规范.md | 删除 `docs/API/` |
| ✅ 删除 `docs/数据库/` | 以 SQL 脚本为唯一来源 |
| ✅ 文档按模块移入对应子目录 | `06-AI单据识别需求.md` → `需求/AI模块`，`物料信息填充公共服务总结.md` → `设计/主数据模块` |
| ✅ 按模块独立编号 | 全局连续 → 每个模块 01- 起编 |
| ✅ 测试文件重排序 | 按 登录→首页→物料→字典→入库出库→库存盘点→操作日志 排列 |
| ✅ README.md 同步 | 目录结构、版本历史逐一同步，标注"（预留）"空目录 |

**编号规则变更：** 跨模块连续编号（旧）→ 按模块独立编号（新），新增文档只加模块内序号，不碰其他模块。

## 14:14 — UserContext 使用指南.md 文件缺失

文档中引用 `设计/系统模块/UserContext 使用指南.md`，但实际文件不存在（被 `yangxuan/` → `docs/` 重命名遗漏）。已补充。

## 14:18 — docs/测试 HTTP 文件重排序 ✅

按执行依赖逻辑重新编号：

| 序号 | 文件 | 说明 |
|------|------|------|
| 00 | 登录登出 | 先拿 token |
| 01 | 首页 API 测试 | 仪表盘概览 |
| 02 | 物料分页 | 基础数据查询 |
| 03 | 数据字典与映射 | 字典+翻译 |
| 04 | 入库出库 | 核心业务流程 |
| 05 | 实时库存&盘点 | 库存状态 |
| 06 | 操作日志 | 审计兜底 |

同步更新了 `docs/README.md`。

## 14:24 — scripts/postgresql 目录组织讨论

**话题：** 杨轩问脚本目录是否可以按 ddl/dml/init 或按模块区分。

**分析结果：** 当前脚本目录的问题：
1. 编号混乱（00/03/10/20/21/22...99，无一致规则）
2. DDL/DML 混在一起（如 10-system.sql 既建表又插数据）
3. fix- 前缀不统一（26-material-last-price.sql 也是 ALTER 但没用 fix- 前缀）

**方案A（用户选中）：** 二级目录 `ddl/`、`dml/`、`migration/`、`mock/`，根目录保留一个 `init.sql` 通过 `\i` 引用子目录文件（因为 `docker-entrypoint-initdb.d` 只处理扁平 .sql 文件）。

**待实现：** [not yet started — compaction happened before implementation]

## 14:47~15:30 — AI 接口文档 + 测试文件 ✅

**AI 接口文档：** `docs/需求/AI模块/02-AI单据识别接口文档.md`
**AI 测试文件：** `docs/测试/07-AI单据识别.http`
- 解释了 exactMatch=false 仍有 materialId 的语义：近似匹配自动选最佳，前端标黄提示
- `docs/README.md` 同步更新

**提交策略：** 用户自行处理敏感配置文件的版本管理（`application.yml`、`application-local.yml`、`scripts/idea/http-client.env.json`）

## 15:30~16:20 — 物料选择器 excludedMaterialIds 实现 ✅

**需求：** 出库/入库选择器支持排除已选物料

**改动点：**
1. `MaterialSelectorReqDTO` — 新增 `excludedMaterialIds`（`List<Long>`）
2. `MaterialServiceImpl` — 4 个分页/计数方法加 `excludedMaterialIds` 参数
3. `StockMapper.xml` — 4 个 SQL 加 `<if test="excludedMaterialIds != null and excludedMaterialIds.size() > 0"> AND s.material_id NOT IN ...</if>`
4. `MaterialSelectorServiceImpl` — 新增 `filterExcludedIds()` 私有方法做内存过滤 + queryByIds 方法加上排除逻辑

**MyBatis <if> 行为验证：**
- `null` → false，跳过 NOT IN
- `[]`（空列表）→ `size() > 0` 为 false，跳过 NOT IN
- `[1, 2]` → 进 NOT IN，WHERE 过滤

**测试覆盖：** 集成测试三类场景全部通过 ✅
- `testMaterialSelector_empty`（传 null）✅
- `testMaterialSelector_emptyExcludeList`（传 `[]`）✅
- `testMaterialSelector_withExcludedIds`（传实际 ID，验证排除正确）✅

## 15:55 — 测试编号冲突修复

**问题：** 新增物料选择器测试插入 `@Order(4)/@Order(5)` 后，原有的 `@Order(4)`（新增出库单）和 `@Order(5)`（明细为空）未重编号，导致 `@Order(4)/@Order(5)/@Order(7)` 重复

**修复：** 将所有 `@Order(n)` 增加 2（n>=4 的改成 n+2），最后 2 个测试增加 2。涉及约 20 个测试方法。

## 16:05 — 测试 bug 修复

**问题：** 排除测试中 `assertFalse(list.stream().anyMatch(s -> s.getId().equals(excludedId)))` 失败
**原因：** `StockPageDTO.getId()` 返回库存记录 ID（`s.id`），而非物料 ID（`s.material_id`）。SQL NOT IN 排除的是 `material_id`，但测试用 `getId()` 取了库存记录 ID
**修复：** 改用 `getMaterialId()` 验证排除