feat(ssa): Complete T-test end-to-end testing with 9 bug fixes - Phase 1 core 85% complete. R service: missing value auto-filter. Backend: error handling, variable matching, dynamic filename. Frontend: module activation, session isolation, error propagation. Full flow verified.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
243
docs/03-业务模块/SSA-智能统计分析/06-开发记录/2026-02-19-端到端测试与Bug修复.md
Normal file
243
docs/03-业务模块/SSA-智能统计分析/06-开发记录/2026-02-19-端到端测试与Bug修复.md
Normal file
@@ -0,0 +1,243 @@
|
||||
# SSA-Pro 2026-02-19 开发总结
|
||||
|
||||
> **版本:** v1.0
|
||||
> **日期:** 2026-02-19
|
||||
> **编写:** AI 开发助手
|
||||
> **状态:** ✅ T 检验端到端测试通过
|
||||
|
||||
---
|
||||
|
||||
## 1. 开发目标
|
||||
|
||||
本日核心目标是**完成 T 检验端到端测试**,并修复测试过程中发现的所有问题。
|
||||
|
||||
---
|
||||
|
||||
## 2. 完成工作清单
|
||||
|
||||
### 2.1 前端模块注册与激活
|
||||
|
||||
| 任务 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| 模块注册 | ✅ 完成 | `moduleRegistry.ts` 中 `placeholder: false` |
|
||||
| 用户会话隔离 | ✅ 修复 | 组件挂载时重置 Zustand store |
|
||||
| 代码下载文件名 | ✅ 修复 | 从 `Content-Disposition` header 提取真实文件名 |
|
||||
|
||||
### 2.2 后端 Bug 修复
|
||||
|
||||
| 任务 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| R 服务错误响应处理 | ✅ 修复 | 检查 `status: error`,返回 422 + `user_hint` |
|
||||
| 变量智能匹配 | ✅ 修复 | 优先使用用户查询中提到的变量 |
|
||||
| 代码下载 API | ✅ 增强 | 动态生成文件名:`{toolName}_{dataName}_{MMDD}_{HHmm}.R` |
|
||||
|
||||
### 2.3 DataParserService 优化
|
||||
|
||||
| 任务 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| 类型推断优化 | ✅ 完成 | 0/1 数字列识别为分类变量 |
|
||||
| 规则优先级 | ✅ 完成 | 唯一值 ≤3 或比例 <20% → categorical |
|
||||
|
||||
### 2.4 R 服务 Bug 修复
|
||||
|
||||
| 任务 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| 缺失值处理 | ✅ 修复 | 分析前自动过滤 NA/空字符串 |
|
||||
| 数据清洗日志 | ✅ 新增 | 记录移除的缺失值行数 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 关键 Bug 修复详情
|
||||
|
||||
### 3.1 分组变量识别为 3 组问题
|
||||
|
||||
**问题描述:**
|
||||
- 用户数据 `smoke` 列有值 1、2 和缺失值
|
||||
- R 服务将缺失值算作第 3 组
|
||||
- T 检验要求恰好 2 组,返回错误
|
||||
|
||||
**修复方案:**
|
||||
```r
|
||||
# r-statistics-service/tools/t_test_ind.R
|
||||
# 数据清洗:移除分组变量或数值变量中的缺失值
|
||||
df <- df[!is.na(df[[group_var]]) & trimws(as.character(df[[group_var]])) != "", ]
|
||||
df <- df[!is.na(df[[value_var]]), ]
|
||||
```
|
||||
|
||||
**影响:**
|
||||
- R 服务自动过滤缺失值
|
||||
- 日志记录:`数据清洗: 移除 7 行缺失值 (剩余 304 行)`
|
||||
|
||||
### 3.2 类型推断错误:0/1 列识别为 numeric
|
||||
|
||||
**问题描述:**
|
||||
- `smoke` 列是 0/1 或 1/2 数字
|
||||
- DataParserService 将其识别为 `numeric` 而非 `categorical`
|
||||
- 导致变量匹配逻辑找不到分类变量
|
||||
|
||||
**修复方案:**
|
||||
```typescript
|
||||
// backend/src/modules/ssa/services/DataParserService.ts
|
||||
// 规则1:唯一值很少(<=10)且比例很低(<20%)→ 分类变量
|
||||
if (uniqueCount <= 10 && uniqueRatio < 0.2) {
|
||||
return 'categorical';
|
||||
}
|
||||
// 规则2:即使是数字,如果唯一值只有2-3个,也视为分类变量
|
||||
if (uniqueCount <= 3) {
|
||||
return 'categorical';
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 R 服务错误信息未传递给前端
|
||||
|
||||
**问题描述:**
|
||||
- R 服务返回 `status: error` 和 `message`
|
||||
- 后端仍返回 200 OK,前端无法显示错误原因
|
||||
- 用户只看到"执行失败"
|
||||
|
||||
**修复方案:**
|
||||
```typescript
|
||||
// backend/src/modules/ssa/routes/analysis.routes.ts
|
||||
if (result?.status === 'error') {
|
||||
return reply.status(422).send({
|
||||
status: 'error',
|
||||
error: result.message || '分析执行失败',
|
||||
user_hint: result.user_hint || result.message
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
```typescript
|
||||
// frontend-v2/src/modules/ssa/hooks/useAnalysis.ts
|
||||
const errorData = error.response?.data;
|
||||
const errorMessage = errorData?.user_hint || errorData?.error || '执行出错';
|
||||
setError(errorMessage);
|
||||
```
|
||||
|
||||
### 3.4 下载代码文件名硬编码
|
||||
|
||||
**问题描述:**
|
||||
- 前端硬编码文件名:`analysis_${sessionId}.R`
|
||||
- 后端 `Content-Disposition` header 中的动态文件名被忽略
|
||||
|
||||
**修复方案:**
|
||||
```typescript
|
||||
// frontend-v2/src/modules/ssa/hooks/useAnalysis.ts
|
||||
const downloadCode = useCallback(async (): Promise<DownloadResult> => {
|
||||
const response = await apiClient.get(...);
|
||||
const contentDisposition = response.headers['content-disposition'];
|
||||
let filename = `analysis_${currentSession.id}.R`;
|
||||
if (contentDisposition) {
|
||||
const match = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
|
||||
if (match) {
|
||||
filename = decodeURIComponent(match[1].replace(/['"]/g, ''));
|
||||
}
|
||||
}
|
||||
return { blob: response.data, filename };
|
||||
}, [currentSession]);
|
||||
```
|
||||
|
||||
### 3.5 用户会话隔离问题
|
||||
|
||||
**问题描述:**
|
||||
- 不同用户登录后看到相同的 session 数据
|
||||
- Zustand store 在页面切换时未重置
|
||||
|
||||
**修复方案:**
|
||||
```tsx
|
||||
// frontend-v2/src/modules/ssa/index.tsx
|
||||
useEffect(() => {
|
||||
reset(); // 组件挂载时重置 store
|
||||
}, [reset]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 测试验证结果
|
||||
|
||||
### 4.1 R 服务直接调用测试
|
||||
|
||||
```json
|
||||
// 请求
|
||||
POST http://localhost:8082/api/v1/skills/ST_T_TEST_IND
|
||||
{
|
||||
"data_source": { "type": "oss", "oss_url": "..." },
|
||||
"params": { "group_var": "smoke", "value_var": "age" }
|
||||
}
|
||||
|
||||
// 响应
|
||||
{
|
||||
"status": "success",
|
||||
"message": "分析完成",
|
||||
"results": {
|
||||
"method": "Welch Two Sample t-test",
|
||||
"statistic": 0.8812,
|
||||
"df": 197.5738,
|
||||
"p_value": 0.3793,
|
||||
...
|
||||
},
|
||||
"plots": ["data:image/png;base64,..."],
|
||||
"trace_log": [
|
||||
"数据清洗: 移除 7 行缺失值 (剩余 304 行)",
|
||||
...
|
||||
],
|
||||
"reproducible_code": "# SSA-Pro 自动生成代码..."
|
||||
}
|
||||
```
|
||||
|
||||
### 4.2 端到端测试流程
|
||||
|
||||
| 步骤 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| 1. 上传数据 | ✅ 通过 | CSV 文件上传到 OSS |
|
||||
| 2. 数据解析 | ✅ 通过 | Schema 正确识别变量类型 |
|
||||
| 3. 生成计划 | ✅ 通过 | 正确匹配 smoke(分类) + age(数值) |
|
||||
| 4. 执行分析 | ✅ 通过 | R 服务返回完整结果 |
|
||||
| 5. 结果展示 | ✅ 通过 | 统计结果 + 图表 + 代码 |
|
||||
| 6. 代码下载 | ✅ 通过 | 动态文件名生效 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 代码变更清单
|
||||
|
||||
| 文件 | 变更类型 | 说明 |
|
||||
|------|----------|------|
|
||||
| `r-statistics-service/tools/t_test_ind.R` | 增强 | 缺失值自动过滤 |
|
||||
| `backend/src/modules/ssa/services/DataParserService.ts` | 增强 | 类型推断优化 |
|
||||
| `backend/src/modules/ssa/routes/analysis.routes.ts` | 修复 | R 错误响应处理 + 动态文件名 |
|
||||
| `frontend-v2/src/modules/ssa/hooks/useAnalysis.ts` | 修复 | 错误信息提取 + 文件名获取 |
|
||||
| `frontend-v2/src/modules/ssa/index.tsx` | 修复 | 用户会话隔离 |
|
||||
| `frontend-v2/src/framework/modules/moduleRegistry.ts` | 更新 | 激活 SSA 模块 |
|
||||
|
||||
---
|
||||
|
||||
## 6. MVP 进度更新
|
||||
|
||||
| Phase | 任务数 | 已完成 | 进度 |
|
||||
|-------|--------|--------|------|
|
||||
| Phase 1 | 49 | 38 | 78% |
|
||||
| Phase 2 | 31 | 0 | 0% |
|
||||
| Phase 3 | 26 | 0 | 0% |
|
||||
| **总计** | **106** | **38** | **36%** |
|
||||
|
||||
---
|
||||
|
||||
## 7. 遗留问题与后续工作
|
||||
|
||||
### 7.1 待完成任务(Phase 1)
|
||||
|
||||
| 任务 | 优先级 | 说明 |
|
||||
|------|--------|------|
|
||||
| 配置中台:DecisionTableLoader | 中 | 四维匹配逻辑 |
|
||||
| 配置中台:RCodeLibraryService | 中 | 脚本上传/版本管理 |
|
||||
| DataParserService 隐私保护 | 低 | 稀有值 < 5 隐藏 |
|
||||
| 安装 json-repair + zod | 低 | LLM 输出容错 |
|
||||
|
||||
### 7.2 下一步计划
|
||||
|
||||
1. **进入 Phase 2** - 实现更多统计方法(ANOVA、卡方检验等)
|
||||
2. **或完善 Phase 1** - 配置中台基础功能
|
||||
|
||||
---
|
||||
|
||||
**2026-02-19 开发总结完成。**
|
||||
98
docs/03-业务模块/SSA-智能统计分析/06-开发记录/SSA-Pro R 服务代码深度审查报告.md
Normal file
98
docs/03-业务模块/SSA-智能统计分析/06-开发记录/SSA-Pro R 服务代码深度审查报告.md
Normal file
@@ -0,0 +1,98 @@
|
||||
# **SSA-Pro R 服务代码深度审查报告**
|
||||
|
||||
**审查对象:** SSA-Pro R Service Source Code (v1.0)
|
||||
|
||||
**审查文件:** Dockerfile, plumber.R, data\_loader.R, guardrails.R, etc.
|
||||
|
||||
**审查时间:** 2026-02-18
|
||||
|
||||
**审查结论:** 🟡 **总体优秀,但存在阻断性缺失 (Blocker)**
|
||||
|
||||
## **1\. 🚨 阻断性问题 (Critical Issues)**
|
||||
|
||||
**这些问题会导致服务无法启动或无法下载数据,必须在联调前修复。**
|
||||
|
||||
### **1.1 data\_loader.R 中缺失 OSS 签名函数**
|
||||
|
||||
* **问题描述**:在 data\_loader.R 第 63 行调用了 generate\_oss\_signature(config, "GET", oss\_key),但我翻遍了上传的所有文件,**没有找到这个函数的定义**。
|
||||
* **风险**:代码运行到下载 OSS 步骤时会直接报错 Error: could not find function "generate\_oss\_signature"。
|
||||
* **修复建议**:
|
||||
1. **方案 A (推荐)**:在 utils/ 下新建 oss\_signer.R,实现阿里云 OSS 的 HMAC-SHA1 签名逻辑(需要引入 digest 和 base64enc 包)。
|
||||
2. **方案 B (替代)**:如果不想手写签名,可以使用系统调用 awscli (配置阿里云 endpoint) 或寻找现成的 R 包(如 aliyunr,但需验证维护状态)。
|
||||
|
||||
### **1.2 plumber.R 的动态加载性能隐患**
|
||||
|
||||
* **问题描述**:在 plumber.R 的 POST /api/v1/skills/\<tool\_code\> 接口中,第 38 行使用了 source(tool\_file)。这意味着**每次请求都会重新从磁盘读取并解析 R 脚本**。
|
||||
* **风险**:
|
||||
* **开发环境**:这是好事,支持热重载。
|
||||
* **生产环境**:这是性能杀手。在高并发下,频繁的磁盘 I/O 和语法解析会显著增加延迟。
|
||||
* **修复建议**:
|
||||
* 引入 DEV\_MODE 变量判断。
|
||||
* **生产环境**:在服务启动时(第 13 行左右)预先加载所有 tools/ 下的脚本,或者使用 environment 缓存已加载的函数。
|
||||
* **开发环境**:保持现有的动态 source 逻辑。
|
||||
|
||||
## **2\. 工程与安全隐患 (Engineering & Security Risks)**
|
||||
|
||||
### **2.1 Docker 容器的 Root 权限风险**
|
||||
|
||||
* **问题描述**:Dockerfile 未指定用户,默认使用 root 运行 R 服务。
|
||||
* **风险**:如果 R 代码中存在漏洞(如允许执行系统命令 system()),攻击者将获得容器的 Root 权限,可能逃逸或破坏文件系统。
|
||||
* **修复建议**:在 Dockerfile 末尾添加非特权用户切换:
|
||||
RUN useradd \-m appuser
|
||||
USER appuser
|
||||
|
||||
### **2.2 路径遍历攻击 (Path Traversal)**
|
||||
|
||||
* **问题描述**:plumber.R 第 33 行:
|
||||
tool\_file \<- file.path("tools", paste0(tolower(gsub("ST\_", "", tool\_code)), ".R"))
|
||||
虽然做了 gsub,但如果 tool\_code 包含 ../ 等字符,仍可能尝试访问上层目录。
|
||||
* **修复建议**:增加严格的白名单校验,或者校验 tool\_code 只能包含字母、数字和下划线。
|
||||
if (\!grepl("^\[A-Z0-9\_\]+$", tool\_code)) {
|
||||
return(list(status="error", message="Invalid tool code format"))
|
||||
}
|
||||
|
||||
## **3\. 最佳实践点赞 (Highlights) ✅**
|
||||
|
||||
1. **依赖锁定 (renv.lock)**:使用了 renv 进行包管理,这是 R 工程化的基石,做得非常棒。
|
||||
2. **护栏设计 (guardrails.R)**:
|
||||
* 包含了 LARGE\_SAMPLE\_THRESHOLD (5000) 的抽样逻辑,避免了大样本下 Shapiro 检验过敏的问题,非常专业的统计学处理。
|
||||
* 接口设计清晰 (passed, action, reason)。
|
||||
3. **结果格式化 (result\_formatter.R)**:统一处理了 P 值 \< 0.001 的显示,符合 APA 规范。
|
||||
4. **环境隔离 (docker-compose.yml)**:正确使用了环境变量注入 OSS 配置,且区分了开发/生产环境。
|
||||
|
||||
## **4\. 优化代码清单 (Code Improvement Snippets)**
|
||||
|
||||
### **补全 OSS 签名逻辑 (utils/oss\_signer.R)**
|
||||
|
||||
*这部分逻辑比较复杂,我直接提供一个简版实现供参考:*
|
||||
|
||||
library(digest)
|
||||
library(base64enc)
|
||||
|
||||
generate\_oss\_signature \<- function(config, verb, resource) {
|
||||
date \<- format(Sys.time(), "%a, %d %b %Y %H:%M:%S GMT", tz="GMT")
|
||||
content\_type \<- ""
|
||||
content\_md5 \<- ""
|
||||
|
||||
canonicalized\_resource \<- paste0("/", config$bucket, "/", resource)
|
||||
string\_to\_sign \<- paste(verb, content\_md5, content\_type, date, "", canonicalized\_resource, sep="\\n")
|
||||
|
||||
signature \<- base64encode(hmac(config$access\_key\_secret, string\_to\_sign, algo="sha1", raw=TRUE))
|
||||
auth\_header \<- paste0("OSS ", config$access\_key\_id, ":", signature)
|
||||
|
||||
return(c("Authorization" \= auth\_header, "Date" \= date))
|
||||
}
|
||||
|
||||
*注意:需要在 renv.lock 中补充 digest 包。*
|
||||
|
||||
## **5\. 总结**
|
||||
|
||||
这份代码作为 MVP 已经达到了 **85分** 的水平。
|
||||
|
||||
**接下来的行动指南:**
|
||||
|
||||
1. **必须做**:补全 generate\_oss\_signature 函数(或相关文件)。
|
||||
2. **必须做**:在 renv 中添加 digest 依赖。
|
||||
3. **建议做**:优化 plumber.R 的生产环境加载逻辑。
|
||||
|
||||
请将这份报告发给 R 开发工程师,让他们快速修正,然后就可以开始构建镜像了。
|
||||
260
docs/03-业务模块/SSA-智能统计分析/06-开发记录/Week1-开发总结报告.md
Normal file
260
docs/03-业务模块/SSA-智能统计分析/06-开发记录/Week1-开发总结报告.md
Normal file
@@ -0,0 +1,260 @@
|
||||
# SSA-Pro 模块 Week 1 开发总结报告
|
||||
|
||||
> **版本:** v1.0
|
||||
> **日期:** 2026-02-19
|
||||
> **编写:** AI 开发助手
|
||||
> **状态:** ✅ Week 1 完成
|
||||
|
||||
---
|
||||
|
||||
## 📋 目录
|
||||
|
||||
1. [开发目标](#1-开发目标)
|
||||
2. [完成工作清单](#2-完成工作清单)
|
||||
3. [关键决策与讨论](#3-关键决策与讨论)
|
||||
4. [技术挑战与解决方案](#4-技术挑战与解决方案)
|
||||
5. [代码审查与规范对齐](#5-代码审查与规范对齐)
|
||||
6. [产出物清单](#6-产出物清单)
|
||||
7. [遗留问题与后续工作](#7-遗留问题与后续工作)
|
||||
|
||||
---
|
||||
|
||||
## 1. 开发目标
|
||||
|
||||
Week 1 的核心目标是**搭建 SSA-Pro 模块的技术骨架**,包括:
|
||||
|
||||
- R 统计服务 Docker 环境
|
||||
- 后端 SSA 模块结构
|
||||
- 前端 SSA 模块结构
|
||||
- 数据库 Schema 设计
|
||||
|
||||
---
|
||||
|
||||
## 2. 完成工作清单
|
||||
|
||||
### 2.1 R 统计服务 (r-statistics-service)
|
||||
|
||||
| 任务 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| Dockerfile 编写 | ✅ 完成 | 基于 `rocker/r-ver:4.3`,包含完整系统依赖 |
|
||||
| plumber.R API 入口 | ✅ 完成 | 健康检查、工具列表、技能执行 |
|
||||
| data_loader.R | ✅ 完成 | 支持 inline 数据和预签名 URL |
|
||||
| guardrails.R | ✅ 完成 | 正态性、方差齐性、样本量检验 |
|
||||
| error_codes.R | ✅ 完成 | 友好错误映射 |
|
||||
| result_formatter.R | ✅ 完成 | APA 格式化 |
|
||||
| t_test_ind.R 示例工具 | ✅ 完成 | 独立样本 T 检验 |
|
||||
| Docker 镜像构建 | ✅ 完成 | `ssa-r-statistics:1.0.1`,1.81GB |
|
||||
|
||||
### 2.2 后端模块 (backend/src/modules/ssa)
|
||||
|
||||
| 任务 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| 模块目录结构 | ✅ 完成 | 标准模块结构 |
|
||||
| index.ts 入口 | ✅ 完成 | 使用 `authenticate` 中间件 |
|
||||
| session.routes.ts | ✅ 完成 | 会话管理 API |
|
||||
| analysis.routes.ts | ✅ 完成 | 分析执行 API,OSS 存储集成 |
|
||||
| consult.routes.ts | ✅ 完成 | 咨询模式 API,LLM 网关集成 |
|
||||
| config.routes.ts | ✅ 完成 | 配置管理 API |
|
||||
| RClientService.ts | ✅ 完成 | R 服务客户端,预签名 URL 支持 |
|
||||
|
||||
### 2.3 前端模块 (frontend-v2/src/modules/ssa)
|
||||
|
||||
| 任务 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| 模块目录结构 | ✅ 完成 | 标准模块结构 |
|
||||
| index.tsx 入口 | ✅ 完成 | 主布局组件 |
|
||||
| useAnalysis.ts Hook | ✅ 完成 | 使用 apiClient 认证 |
|
||||
| 组件骨架 | ✅ 完成 | DataUploader, PlanConfirm 等 |
|
||||
|
||||
### 2.4 数据库 Schema
|
||||
|
||||
| 任务 | 状态 | 说明 |
|
||||
|------|------|------|
|
||||
| Prisma Schema 定义 | ✅ 完成 | 9 个 SSA 相关模型 |
|
||||
| Migration SQL | ✅ 完成 | 手动创建并应用 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 关键决策与讨论
|
||||
|
||||
### 3.1 OSS 访问方案
|
||||
|
||||
**讨论背景:**
|
||||
- 开发团队审查报告建议 R 服务实现 OSS 签名(`oss_signer.R`)
|
||||
- 这需要 R 服务持有 OSS 密钥
|
||||
|
||||
**最终决策:** ❌ 不采用 R 直接签名方案
|
||||
|
||||
**采用方案:** 预签名 URL
|
||||
```
|
||||
Node.js 生成预签名 URL → 传递给 R 服务 → R 直接 GET 下载
|
||||
```
|
||||
|
||||
**理由:**
|
||||
1. 符合平台 OSS 存储规范(密钥集中管控)
|
||||
2. R 服务无需持有敏感密钥
|
||||
3. 简化 R 代码复杂度
|
||||
|
||||
### 3.2 生产环境性能优化
|
||||
|
||||
**问题:** `plumber.R` 每次请求都 `source()` 工具脚本
|
||||
|
||||
**解决方案:**
|
||||
```r
|
||||
# 生产环境:启动时预加载到 TOOL_CACHE
|
||||
if (!DEV_MODE) {
|
||||
preload_tools() # 缓存 run_analysis 函数
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 安全加固
|
||||
|
||||
| 问题 | 解决方案 |
|
||||
|------|----------|
|
||||
| Docker Root 权限 | 添加 `USER appuser` |
|
||||
| 路径遍历攻击 | `tool_code` 正则白名单 `^[A-Z][A-Z0-9_]*$` |
|
||||
|
||||
---
|
||||
|
||||
## 4. 技术挑战与解决方案
|
||||
|
||||
### 4.1 Prisma Migration Drift
|
||||
|
||||
**问题:** `prisma migrate dev` 检测到 drift,要求 `migrate reset`
|
||||
|
||||
**解决方案:**
|
||||
1. 手动创建 SQL 文件
|
||||
2. 直接执行 SQL
|
||||
3. 使用 `prisma migrate resolve --applied` 标记已应用
|
||||
|
||||
### 4.2 Docker 构建依赖问题
|
||||
|
||||
**问题链:**
|
||||
```
|
||||
zlib.h 缺失 → httpuv 编译失败
|
||||
cmake 缺失 → nloptr 编译失败
|
||||
ggplot2 版本冲突 → cowplot 安装失败
|
||||
```
|
||||
|
||||
**解决方案:**
|
||||
1. 添加系统依赖:`zlib1g-dev`, `cmake`, `libnlopt-dev`, `gfortran`
|
||||
2. 放弃 renv,直接 `install.packages()` 让 R 自动解决依赖
|
||||
|
||||
### 4.3 PowerShell 重定向问题
|
||||
|
||||
**问题:** `<` 操作符在 PowerShell 中被保留
|
||||
|
||||
**解决方案:**
|
||||
```powershell
|
||||
Get-Content "file.sql" | docker exec -i postgres psql ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 代码审查与规范对齐
|
||||
|
||||
Week 1 后期进行了一次重要的**规范对齐审查**,确保新代码遵循平台规范。
|
||||
|
||||
### 5.1 发现的问题
|
||||
|
||||
| 文件 | 问题 | 修复 |
|
||||
|------|------|------|
|
||||
| `useAnalysis.ts` | 使用原生 fetch,无认证 | 改用 `apiClient` |
|
||||
| `ssa/index.ts` | 自定义 authenticate | 使用平台 `authenticate` 中间件 |
|
||||
| `RClientService.ts` | 直接传 OSS key | 使用 `storage.getUrl()` 预签名 |
|
||||
| `ConsultChat.tsx` | 自定义 Chat 组件 | 删除,使用平台 `AIStreamChat` |
|
||||
| 各 routes | 手动获取 userId | 使用 `getUserId(request)` |
|
||||
|
||||
### 5.2 参考规范文档
|
||||
|
||||
- `docs/02-通用能力层/00-通用能力层清单.md`
|
||||
- `docs/04-开发规范/10-模块认证规范.md`
|
||||
- `docs/04-开发规范/11-OSS存储开发规范.md`
|
||||
|
||||
---
|
||||
|
||||
## 6. 产出物清单
|
||||
|
||||
### 6.1 代码文件
|
||||
|
||||
```
|
||||
r-statistics-service/
|
||||
├── Dockerfile # 生产就绪
|
||||
├── docker-compose.yml # 本地开发
|
||||
├── plumber.R # API 入口
|
||||
├── utils/
|
||||
│ ├── data_loader.R # 预签名 URL 方案
|
||||
│ ├── guardrails.R # Block/Warn/Switch
|
||||
│ ├── error_codes.R
|
||||
│ └── result_formatter.R
|
||||
├── tools/
|
||||
│ └── t_test_ind.R # 示例工具
|
||||
└── tests/fixtures/
|
||||
└── normal_data.csv
|
||||
|
||||
backend/src/modules/ssa/
|
||||
├── index.ts
|
||||
├── routes/
|
||||
│ ├── session.routes.ts
|
||||
│ ├── analysis.routes.ts
|
||||
│ ├── consult.routes.ts
|
||||
│ └── config.routes.ts
|
||||
├── executor/
|
||||
│ └── RClientService.ts
|
||||
└── types/
|
||||
|
||||
frontend-v2/src/modules/ssa/
|
||||
├── index.tsx
|
||||
├── components/
|
||||
├── hooks/
|
||||
│ └── useAnalysis.ts
|
||||
└── types/
|
||||
```
|
||||
|
||||
### 6.2 Docker 镜像
|
||||
|
||||
| 镜像名 | 版本 | 大小 | 状态 |
|
||||
|--------|------|------|------|
|
||||
| `ssa-r-statistics` | 1.0.1 | 1.81 GB | ✅ 本地构建成功 |
|
||||
|
||||
### 6.3 数据库 Schema
|
||||
|
||||
- `ssa_schema` 命名空间
|
||||
- 9 个新表:`SsaSession`, `SsaMessage`, `SsaTool`, `SsaExecutionLog` 等
|
||||
|
||||
---
|
||||
|
||||
## 7. 遗留问题与后续工作
|
||||
|
||||
### 7.1 待完成任务
|
||||
|
||||
| 任务 | 优先级 | 说明 |
|
||||
|------|--------|------|
|
||||
| 后端主路由注册 | P0 | 将 SSA 路由加入 `index.ts` |
|
||||
| 前端模块注册 | P0 | 加入 `moduleRegistry.ts` |
|
||||
| T 检验数据格式调试 | P1 | JSON 转 data.frame 格式问题 |
|
||||
| 配置中心 Excel 模板 | P1 | 决策表、参数映射等 |
|
||||
|
||||
### 7.2 Week 2 计划
|
||||
|
||||
1. **完成模块注册** - 后端/前端路由注册
|
||||
2. **端到端测试** - 数据上传 → 计划生成 → 执行
|
||||
3. **配置中心实现** - DecisionTableLoader, RCodeLibraryService
|
||||
4. **Planner 引擎** - LLM 方法推荐逻辑
|
||||
|
||||
---
|
||||
|
||||
## 附录:关键文件变更记录
|
||||
|
||||
| 文件 | 变更类型 | 变更内容 |
|
||||
|------|----------|----------|
|
||||
| `data_loader.R` | 重构 | OSS 签名 → 预签名 URL |
|
||||
| `plumber.R` | 增强 | 生产预加载 + tool_code 校验 |
|
||||
| `Dockerfile` | 增强 | 非特权用户 + 健康检查 |
|
||||
| `RClientService.ts` | 修复 | 使用 storage.getUrl() |
|
||||
| `useAnalysis.ts` | 修复 | 使用 apiClient |
|
||||
| `ssa/index.ts` | 修复 | 使用平台 authenticate |
|
||||
|
||||
---
|
||||
|
||||
**Week 1 开发总结完成。**
|
||||
56
docs/03-业务模块/SSA-智能统计分析/06-开发记录/oss_signer.R
Normal file
56
docs/03-业务模块/SSA-智能统计分析/06-开发记录/oss_signer.R
Normal file
@@ -0,0 +1,56 @@
|
||||
# utils/oss_signer.R
|
||||
# 阿里云 OSS 签名生成器 (R语言实现)
|
||||
# 参考文档: https://help.aliyun.com/document_detail/31951.html
|
||||
|
||||
library(digest)
|
||||
library(base64enc)
|
||||
|
||||
#' 生成 OSS API 签名头
|
||||
#' @param config 包含 access_key_id, access_key_secret, bucket 的列表
|
||||
#' @param verb HTTP 方法 (GET, PUT, etc.)
|
||||
#' @param resource OSS 资源路径 (例如 "/my-bucket/data/file.csv")
|
||||
#' @param content_type 内容类型 (可选)
|
||||
#' @param content_md5 内容 MD5 (可选)
|
||||
#' @return 包含 Authorization 和 Date 的命名向量
|
||||
generate_oss_signature <- function(config, verb, resource, content_type = "", content_md5 = "") {
|
||||
|
||||
# 1. 生成标准时间戳 (GMT 格式)
|
||||
# 例如: "Thu, 18 Feb 2026 08:00:00 GMT"
|
||||
date <- format(Sys.time(), "%a, %d %b %Y %H:%M:%S GMT", tz="GMT")
|
||||
|
||||
# 2. 构造 CanonicalizedResource
|
||||
# 格式: /BucketName/ObjectName
|
||||
canonicalized_resource <- paste0("/", config$bucket, "/", resource)
|
||||
|
||||
# 3. 构造 StringToSign
|
||||
# 格式:
|
||||
# VERB + "\n" +
|
||||
# Content-MD5 + "\n" +
|
||||
# Content-Type + "\n" +
|
||||
# Date + "\n" +
|
||||
# CanonicalizedOSSHeaders +
|
||||
# CanonicalizedResource
|
||||
|
||||
# 注意: 这里简化处理,未包含 CanonicalizedOSSHeaders (x-oss-*)
|
||||
string_to_sign <- paste(
|
||||
verb,
|
||||
content_md5,
|
||||
content_type,
|
||||
date,
|
||||
canonicalized_resource,
|
||||
sep = "\n"
|
||||
)
|
||||
|
||||
# 4. 计算 HMAC-SHA1 签名
|
||||
# 使用 AccessKeySecret 作为密钥
|
||||
signature <- base64encode(hmac(config$access_key_secret, string_to_sign, algo = "sha1", raw = TRUE))
|
||||
|
||||
# 5. 构造 Authorization 头
|
||||
auth_header <- paste0("OSS ", config$access_key_id, ":", signature)
|
||||
|
||||
# 返回需要的 Headers
|
||||
return(c(
|
||||
"Authorization" = auth_header,
|
||||
"Date" = date
|
||||
))
|
||||
}
|
||||
Reference in New Issue
Block a user