时地方
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
|
||||
我已经仔细阅读了这两个项目。让我为你总结一下:
|
||||
📁 fumamxCLI - 孚盟MX 贸易数据 CLI 工具
|
||||
类型: Python 命令行工具
|
||||
技术栈: Python 3.11+, Click, Rich, Requests, Playwright
|
||||
核心功能
|
||||
公司搜索 - 搜索全球贸易企业
|
||||
进口数据分析 - 获取进口企业数据、统计
|
||||
出口数据分析 - 获取出口企业数据、统计
|
||||
产品分析 - HS 编码分析、产品聚合、对比分析
|
||||
国家分析 - 贸易来源国家、出口国家分析
|
||||
贸易记录 - 查询完整贸易详情
|
||||
企业详情 - 公司信息、联系人、电话
|
||||
项目结构
|
||||
fumamxCLI/
|
||||
├── fumamx # 可执行入口
|
||||
├── src/
|
||||
│ ├── cli.py # 命令行接口 (21+ 命令)
|
||||
│ ├── client.py # API 客户端
|
||||
│ ├── config.py # 认证配置
|
||||
│ ├── auth.py # 自动登录/Token 刷新
|
||||
│ └── dynamic_api.py # 动态 API 调用
|
||||
├── scripts/
|
||||
│ ├── capture_api.js # 浏览器抓取脚本
|
||||
│ └── setup_apis.py # API 代码生成器
|
||||
└── fumamx_apis.json # 抓取的 API 数据
|
||||
认证方式
|
||||
使用浏览器 Cookie + Token
|
||||
支持 Playwright 自动登录刷新
|
||||
|
||||
---
|
||||
Token
|
||||
Token 缓存机制
|
||||
📁 fumamxCRM - 客户关系管理系统
|
||||
类型: Web 应用 (FastAPI + React)
|
||||
技术栈:
|
||||
后端: FastAPI, SQLAlchemy, SQLite
|
||||
前端: React, Vite, Tailwind CSS
|
||||
端口: 后端 10001, 前端 10000
|
||||
核心功能
|
||||
客户管理
|
||||
本地客户存储
|
||||
潜在客户导入(从 fumamxCLI 数据源)
|
||||
联系人管理
|
||||
数据发现
|
||||
关键词搜索企业
|
||||
分页导入潜在客户
|
||||
后台异步导入任务
|
||||
深度分析
|
||||
AI 驱动的企业商业画布分析
|
||||
生成 Markdown 分析报告
|
||||
支持多种大模型
|
||||
系统设置
|
||||
Cookie/Token 管理
|
||||
系统状态监控
|
||||
数据库模型
|
||||
表名 说明
|
||||
users 管理员用户
|
||||
customers 本地客户
|
||||
countries 国家信息
|
||||
import_tasks 导入任务
|
||||
potential_customers 潜在客户
|
||||
potential_contacts 潜在客户联系人
|
||||
deep_analyses 深度分析记录
|
||||
system_settings 系统设置
|
||||
项目结构
|
||||
```
|
||||
fumamxCRM/
|
||||
├── backend/
|
||||
│ ├── app.py # FastAPI 主应用
|
||||
│ ├── models.py # SQLAlchemy 模型
|
||||
│ ├── schemas.py # Pydantic 模型
|
||||
│ ├── database.py # 数据库配置
|
||||
│ ├── auth.py # 认证逻辑
|
||||
│ ├── import_service.py # 导入服务
|
||||
│ ├── fumamx_cli.py # CLI 包装器
|
||||
│ └── routers/
|
||||
│ ├── auth.py
|
||||
│ ├── customers.py
|
||||
│ ├── discover.py
|
||||
│ ├── import_tasks.py
|
||||
│ ├── potential_customers.py
|
||||
│ ├── deep_analysis.py
|
||||
│ └── settings
|
||||
|
||||
.py
|
||||
└── frontend/
|
||||
├── src/
|
||||
│ ├── App.jsx
|
||||
│ ├── api.js
|
||||
│ ├── components/
|
||||
│ └── pages/
|
||||
└── node_modules/
|
||||
🔗 两个项目的关系
|
||||
fumamxCLI (数据获取层)
|
||||
↓
|
||||
fumamxCRM/backend/fumamx_cli.py (包装器)
|
||||
↓
|
||||
fumamxCRM (数据管理层 + Web UI)
|
||||
```
|
||||
fumamxCLI 负责从孚盟MX平台抓取贸易数据
|
||||
fumamxCRM 提供 Web 界面管理这些数据,支持批量导入、深度分析
|
||||
蔡叔叔,我已经了解了这两个项目的全貌。需要我:
|
||||
继续查看某个具体文件?
|
||||
运行/测试某个功能?
|
||||
开发新功能?
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
搞定了!现在 fumamxCRM 也做成了 systemd 服务,**随系统启动自动运行**。
|
||||
|
||||
---
|
||||
|
||||
**创建的两个服务**
|
||||
|
||||
| 服务 | 端口 | 状态 |
|
||||
|------|------|------|
|
||||
| `fumamx-crm-backend.service` | 10001 | active (running) |
|
||||
| `fumamx-crm-frontend.service` | 10000 | active (running) |
|
||||
|
||||
---
|
||||
|
||||
**常用命令**
|
||||
|
||||
```
|
||||
# 查看状态
|
||||
sudo systemctl status fumamx-crm-backend
|
||||
sudo systemctl status fumamx-crm-frontend
|
||||
|
||||
# 重启
|
||||
sudo systemctl restart fumamx-crm-backend
|
||||
sudo systemctl restart fumamx-crm-frontend
|
||||
|
||||
# 停止
|
||||
sudo systemctl stop fumamx-crm-backend
|
||||
sudo systemctl stop fumamx-crm-frontend
|
||||
|
||||
# 查看日志
|
||||
sudo journalctl -u fumamx-crm-backend -f
|
||||
sudo journalctl -u fumamx-crm-frontend -f
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**验证**
|
||||
|
||||
- 前端 `http://129.211.173.87:10000` → HTTP 200 ✓
|
||||
- 后端 `http://129.211.173.87:10001/health` → `{"status":"healthy"}` ✓
|
||||
|
||||
现在服务器重启后,fumamxCRM 会自动启动,不需要再手动跑 `start.sh`。
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,627 @@
|
||||
|
||||
我们开始新对话。
|
||||
你使用OpenCLI(https://github.com/jackwener/OpenCLI)帮我生成网站https://www.fumamx.com的fumamxCLI
|
||||
|
||||
我在/home/ubuntu目录放一个网站的cookie文件名是fumamx_cookies.txt 。
|
||||
|
||||
我是通过URL: https://www.fumamx.com/#/login
|
||||
账号和密码 showmin.zhou@163.com Kingc.83695910
|
||||
然后获取到cookie的。
|
||||
|
||||
登录进来后,url 是:https://www.fumamx.com/#/main/workbench
|
||||
|
||||
在/home/ubuntu/nanocode目录建立一个fumamxCLI 做这个事情。
|
||||
|
||||
---
|
||||
|
||||
文件在:
|
||||
|
||||
```
|
||||
/home/ubuntu/nanocode/fumamxCLI/scripts/capture_api.js
|
||||
```
|
||||
|
||||
你可以直接复制使用。完整内容如下,**直接在浏览器控制台粘贴**:
|
||||
|
||||
```javascript
|
||||
/**
|
||||
* 孚盟MX API 抓取脚本
|
||||
* 使用方法:
|
||||
* 1. 登录 https://www.fumamx.com/#/login
|
||||
* 2. 按 F12 打开开发者工具
|
||||
* 3. 切换到 Console 面板
|
||||
* 4. 粘贴此脚本并回车
|
||||
* 5. 在页面上正常操作(点客户、邮件等模块)
|
||||
* 6. 所有 API 请求会被记录并导出
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
window.fumamxAPIs = window.fumamxAPIs || [];
|
||||
|
||||
// 拦截 fetch
|
||||
const originalFetch = window.fetch;
|
||||
window.fetch = function(...args) {
|
||||
const url = args[0];
|
||||
const options = args[1] || {};
|
||||
|
||||
if (typeof url === 'string' && (url.includes('/api/') || url.includes('/rest/') || url.includes('/v1/'))) {
|
||||
const record = {
|
||||
method: options.method || 'GET',
|
||||
url: url,
|
||||
headers: options.headers,
|
||||
body: options.body,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
window.fumamxAPIs.push(record);
|
||||
console.log('📡 API:', record.method, url);
|
||||
}
|
||||
|
||||
return originalFetch.apply(this, args);
|
||||
};
|
||||
|
||||
// 拦截 XMLHttpRequest
|
||||
const originalOpen = XMLHttpRequest.prototype.open;
|
||||
const originalSend = XMLHttpRequest.prototype.send;
|
||||
|
||||
XMLHttpRequest.prototype.open = function(method, url, ...args) {
|
||||
this._fumamxMethod = method;
|
||||
this._fumamxUrl = url;
|
||||
return originalOpen.call(this, method, url, ...args);
|
||||
};
|
||||
|
||||
XMLHttpRequest.prototype.send = function(body) {
|
||||
const url = this._fumamxUrl;
|
||||
if (typeof url === 'string' && (url.includes('/api/') || url.includes('/rest/') || url.includes('/v1/'))) {
|
||||
const record = {
|
||||
method: this._fumamxMethod,
|
||||
url: url,
|
||||
body: body,
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
window.fumamxAPIs.push(record);
|
||||
console.log('📡 API:', record.method, url);
|
||||
}
|
||||
return originalSend.call(this, body);
|
||||
};
|
||||
|
||||
// 导出功能
|
||||
window.exportFumamxAPIs = function() {
|
||||
const data = JSON.stringify(window.fumamxAPIs, null, 2);
|
||||
const blob = new Blob([data], {type: 'application/json'});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'fumamx_apis.json';
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
console.log('✅ 已导出 ' + window.fumamxAPIs.length + ' 个 API 端点到 fumamx_apis.json');
|
||||
};
|
||||
|
||||
console.log('✅ 孚盟MX API 抓取器已启动');
|
||||
console.log('💡 操作页面后,在控制台运行 exportFumamxAPIs() 导出结果');
|
||||
})();
|
||||
```
|
||||
|
||||
**操作步骤**:
|
||||
1. 登录孚盟 → F12 → Console
|
||||
2. 粘贴上面的代码,回车
|
||||
3. 点击「客户」「邮件」等模块
|
||||
4. 看到 `📡 API: GET /api/xxx` 说明正在记录
|
||||
5. 运行 `exportFumamxAPIs()` 下载 JSON
|
||||
6. 把 `fumamx_apis.json` 上传到服务器
|
||||
7. 运行 `python3 scripts/setup_apis.py fumamx_apis.json` 生成 CLI 代码
|
||||
|
||||
---
|
||||
|
||||
## ✅ 完成!fumamxCLI 已生成并通过验证
|
||||
|
||||
### 生成的文件
|
||||
|
||||
| 文件 | 描述 |
|
||||
|------|------|
|
||||
| `src/client.py` | API 客户端,20+ 个方法 |
|
||||
| `src/cli.py` | 命令行接口,21 个命令 |
|
||||
| `src/config.py` | 认证配置 (token) |
|
||||
| `README.md` | 完整使用文档 |
|
||||
|
||||
### 可用命令 (21 个)
|
||||
|
||||
```
|
||||
系统: status, cookies, call
|
||||
公司: company search <keyword>
|
||||
进口: import- enterprise/stats <name>
|
||||
出口: export enterprise/stats <name>
|
||||
产品: product hs-code/agg/analysis/compare-lose/compare-new
|
||||
国家: country list/export/data <name>
|
||||
贸易: trade detail <no>
|
||||
```
|
||||
|
||||
### 验证结果
|
||||
|
||||
| 测试项 | 结果 |
|
||||
|--------|------|
|
||||
| API 连接 | ✅ 正常 |
|
||||
| 用户认证 | ✅ showmin.zhou@163.com |
|
||||
| 公司搜索 | ✅ 返回数据 |
|
||||
| 国家列表 | ✅ 200+ 国家 |
|
||||
| 通用调用 | ✅ 灵活调用任意端点 |
|
||||
|
||||
### 快速使用
|
||||
|
||||
```bash
|
||||
cd /home/ubuntu/nanocode/fumamxCLI
|
||||
|
||||
# 检查状态
|
||||
./fumamx status
|
||||
|
||||
# 搜索公司
|
||||
./fumamx company search "Apple"
|
||||
|
||||
# 查看所有命令
|
||||
./fumamx list-apis
|
||||
|
||||
# 调用任意 API
|
||||
./fumamx call GET /pcapi/tradeData/api/V2/Account/GetTradeSourceCountrys
|
||||
```
|
||||
|
||||
### 注意
|
||||
|
||||
Token 存储在 `src/config.py`,如果 API 返回 401,需要重新从浏览器抓取更新 token。
|
||||
|
||||
---
|
||||
|
||||
|
||||
## 我们继续下一个任务,创建一个web项目,通过fumamxCLI对https://www.fumamx.com网站书籍进行管理。
|
||||
### 项目目录
|
||||
在/home/ubuntu/nanocode目录创建一个项目fumamxCRM(我已经创建了目录) 。
|
||||
### Web项目风格
|
||||
~/nanocode/personal-blog 这个项目的设计风格是新粗野风格,~/nanocode/personal-blog/DESIGN.md 是你总结的设计风格。我们使用这个风格来设计我们的项目。
|
||||
### 技术栈
|
||||
相同于~/nanocode/personal-blog
|
||||
|
||||
### 详细需求
|
||||
#### 登录界面
|
||||
是我们自己的fumamxCRM管理登录,类似于personal-blog的admin/abcd1234管理账号登录。但是,fumamxCRM没有前台浏览页面,登录页面是第一个页面,用户必须登录才能访问后台管理界面。
|
||||
#### 后台管理主界面
|
||||
后台管理主界面,类似于~/nanocode/personal-blog后台管理界面。
|
||||
后台分几个模块:
|
||||
1. 客户管理:客户信息是从哪里来的?是从fumamxCLI搜索并添加而来的。
|
||||
2. 发现:通过fumamxCLI的search命令,搜索到客户信息列表。
|
||||
客户详情:点击某个客户,进入客户详情
|
||||
添加客户:可以将某个搜索到的客户,添加到本地数据库。
|
||||
3. 设置:
|
||||
修改管理员密码;
|
||||
设置cookie, 因为cookie会过期。设置界面有地方输入cookie的文本内容,后台存储到文件fumamx_cookies.txt,然后提取信息和AccessToken
|
||||
|
||||
我们先实现这些基本功能,后续逐步添加更多功能。
|
||||
|
||||
**以上内容,你是否清晰,如果有问题请反问我。知道你清楚为止。
|
||||
然后,你提出的技术方案,和实现计划,分步骤完成任务。**
|
||||
|
||||
|
||||
![[Pasted image 20260414203040.png]]
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
**问题根源:浏览器导出时缺少 `user` 和 `pass` Cookie**
|
||||
|
||||
这是**浏览器导出机制的问题**,不是 CRM 的问题。你需要确认浏览器中确实存在这些 Cookie。
|
||||
|
||||
---
|
||||
|
||||
**排查步骤:**
|
||||
|
||||
### 1. 浏览器中验证 Cookie 是否存在
|
||||
打开 https://www.fumamx.com 并登录后,按 **F12** → **Application** → **Cookies** → **https://www.fumamx.com**,检查是否有以下字段:
|
||||
|
||||
| Cookie 名 | 值示例 |
|
||||
|-----------|--------|
|
||||
| `user` | `showmin.zhou@163.com` |
|
||||
| `pass` | `U2FsdGVkX1+fFGAO5Ib25ulin0hAOBVqhGjgBkmNMW4=` |
|
||||
| `rememberPassword` | `true` |
|
||||
|
||||
### 2. 如果浏览器中没有这些 Cookie
|
||||
说明你没有勾选**"记住密码"**或**"保持登录"**。请:
|
||||
1. 退出登录
|
||||
2. 重新登录时勾选 **"记住密码"** 或 **"保持登录状态"**
|
||||
3. 再次检查 Cookies
|
||||
|
||||
### 3. 如果浏览器中有,但导出时丢失
|
||||
尝试使用 **EditThisCookie** 扩展的 **"Export"** 功能时,选择 **"Export as JSON"** 而不是其他格式。
|
||||
|
||||
或者手动复制:在 EditThisCookie 中点击 `user` 和 `pass` 这两个 cookie,确认它们存在,然后手动编辑 JSON 添加进去:
|
||||
|
||||
```json
|
||||
[
|
||||
// ... 你导出的4个 ...
|
||||
{
|
||||
"domain": "www.fumamx.com",
|
||||
"name": "user",
|
||||
"value": "showmin.zhou@163.com",
|
||||
"path": "/",
|
||||
"secure": false,
|
||||
"httpOnly": false,
|
||||
"session": false,
|
||||
"hostOnly": true,
|
||||
"expirationDate": 1776821773,
|
||||
"sameSite": "unspecified"
|
||||
},
|
||||
{
|
||||
"domain": "www.fumamx.com",
|
||||
"name": "pass",
|
||||
"value": "U2FsdGVkX1+fFGAO5Ib25ulin0hAOBVqhGjgBkmNMW4=",
|
||||
"path": "/",
|
||||
"secure": false,
|
||||
"httpOnly": false,
|
||||
"session": false,
|
||||
"hostOnly": true,
|
||||
"expirationDate": 1776821773,
|
||||
"sameSite": "unspecified"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**关键问题:fumamx API 需要 `user` + `pass` 来验证身份**,仅 `_t` token 不够。请检查浏览器中是否有这两个 cookie。
|
||||
|
||||
|
||||
---
|
||||
|
||||
**还是不行** —— API 仍然返回 `"登陆令牌已失效"` (code: -2)
|
||||
|
||||
---
|
||||
|
||||
**问题分析:**
|
||||
|
||||
虽然 7 个 cookies 都正确加载了,但 `_t` 令牌本身已经**过期或无效**。
|
||||
|
||||
```
|
||||
_t: ZjtDL09DbYW0mzah ← 这个 token 已经失效
|
||||
```
|
||||
|
||||
`_t` 是一个 **session cookie**,它的有效期很短:
|
||||
- 当你**关闭浏览器**时失效
|
||||
- 或者**一段时间不操作**后过期
|
||||
- 或者**从其他设备/浏览器登录**时旧 token 被吊销
|
||||
|
||||
---
|
||||
|
||||
**必须重新获取:**
|
||||
|
||||
1. 打开 https://www.fumamx.com
|
||||
2. **如果已登录,先退出再重新登录**(强制刷新 token)
|
||||
3. 登录后立即导出 cookies(**不要关闭浏览器**)
|
||||
4. 将新的 JSON 发给我
|
||||
|
||||
**注意:** `_t` 是 session 级别的,**每次重新登录都会变**。必须刚登录完就导出。
|
||||
|
||||
---
|
||||
|
||||
|
||||
**找到根本原因了!**
|
||||
|
||||
问题不在 Cookie,而在 **`config.py` 中的硬编码 token 已过期**:
|
||||
|
||||
```python
|
||||
ACCESS_TOKEN = "Iiw4o3RoqltwY5EW"
|
||||
INDIVIDUAL_ACCESS_TOKEN = "lAInwlJfiKx5FOnF"
|
||||
```
|
||||
|
||||
fumamx API 使用**双层认证**:
|
||||
1. Cookie `_t` ✅ (你从浏览器获取的,是对的)
|
||||
2. URL 参数 `accessToken` + `individualAccessToken` ❌ (硬编码的,已过期)
|
||||
|
||||
---
|
||||
|
||||
**需要你在浏览器中获取这些动态 token:**
|
||||
|
||||
1. 打开 https://www.fumamx.com 并登录
|
||||
2. 按 **F12** → **Network/网络** 标签
|
||||
3. 在页面中随便搜索一个公司(比如输入 "Apple" 搜索)
|
||||
4. 找到请求如 `company` 或 `ImpEnterprise`
|
||||
5. 查看请求的 **URL 参数**,找到:
|
||||
- `accessToken=xxxxx`
|
||||
- `individualAccessToken=xxxxx`
|
||||
|
||||
或者:
|
||||
|
||||
1. 在 Console 中执行:
|
||||
```javascript
|
||||
localStorage.getItem('accessToken')
|
||||
localStorage.getItem('individualAccessToken')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**能否帮我获取这两个 token?** 这样我就能更新 config.py 并验证连接。
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
**之前是通过浏览器脚本抓取的**:
|
||||
|
||||
1. 在浏览器控制台粘贴 `scripts/capture_api.js`
|
||||
2. 操作页面(搜索公司等)
|
||||
3. 运行 `exportFumamxAPIs()` 导出 API 请求
|
||||
4. 从导出的 JSON 中提取 URL 参数里的 token
|
||||
|
||||
---
|
||||
|
||||
**但现在有更简单的方法**,直接在浏览器控制台执行:
|
||||
|
||||
```javascript
|
||||
// 方法 1: 从 localStorage 获取
|
||||
localStorage.getItem('accessToken')
|
||||
localStorage.getItem('individualAccessToken')
|
||||
|
||||
// 方法 2: 从当前 URL 获取(如果你在 fumamx 页面)
|
||||
new URLSearchParams(window.location.search).get('accessToken')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**或者手动复制**:
|
||||
|
||||
1. 按 **F12** → **Network**
|
||||
2. 随便操作一下(比如搜索 "Apple")
|
||||
3. 找一个请求,比如 `company` 或 `ImpEnterprise`
|
||||
4. 查看请求的 **URL**,找到:
|
||||
```
|
||||
https://www.fumamx.com/pcapi/...?accessToken=xxxx&individualAccessToken=yyyy&...
|
||||
```
|
||||
5. 复制 `accessToken` 和 `individualAccessToken` 的值给我
|
||||
|
||||
---
|
||||
|
||||
**最快方式**:直接在浏览器控制台输入 `localStorage.getItem('accessToken')` 回车,把结果发给我。
|
||||
|
||||
|
||||
---
|
||||
|
||||
试试这些方式获取 token:
|
||||
|
||||
**1. sessionStorage**
|
||||
```javascript
|
||||
sessionStorage.getItem('accessToken')
|
||||
sessionStorage.getItem('individualAccessToken')
|
||||
```
|
||||
|
||||
**2. 查看当前 URL**
|
||||
```javascript
|
||||
window.location.href
|
||||
```
|
||||
|
||||
**3. Network 面板抓取(最可靠)**
|
||||
- F12 → Network
|
||||
- 在页面中搜索一个公司(比如输入 "Apple")
|
||||
- 找到任意一个 API 请求(如 `company`、`ImpEnterprise`)
|
||||
- 点击它 → Headers → Request URL
|
||||
- 复制里面 `accessToken=` 和 `individualAccessToken=` 后面的值
|
||||
|
||||
**4. 查看所有 cookie**
|
||||
```javascript
|
||||
document.cookie
|
||||
```
|
||||
|
||||
请试一下方法 2 或 3,把获取到的值发给我。
|
||||
|
||||
|
||||
---
|
||||
sessionStorage.getItem('accessToken')
|
||||
|
||||
'{"ownerId":11011595,"scope":"11011595","value":"71uRBqAwJWWLxWyp","createTime":1776217804534,"expireTime":1776304204534,"referURL":null,"agent":null,"agentVersion":null,"readOnly":0,"versionIteration":1,"companyTag":null,"platform":"pc","cid":1067743}'
|
||||
sessionStorage.getItem('individualAccessToken')
|
||||
|
||||
'{"ownerId":10444522,"scope":"independent","value":"CmA5vs7xmGEYcsuD","createTime":1776217804069,"expireTime":1776304204069,"referURL":null,"agent":null,"agentVersion":null,"readOnly":0,"versionIteration":null,"companyTag":null,"platform":"pc","cid":null}'
|
||||
|
||||
---
|
||||
|
||||
**✅ 成功!API 连接正常,认证有效!**
|
||||
|
||||
```
|
||||
✓ 已加载 7 个 cookies
|
||||
✓ API 连接正常,认证有效
|
||||
用户: showmin.zhou@163.com
|
||||
公司ID: 1067743
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Token 已更新**:
|
||||
- `ACCESS_TOKEN`: `71uRBqAwJWWLxWyp`
|
||||
- `INDIVIDUAL_ACCESS_TOKEN`: `CmA5vs7xmGEYcsuD`
|
||||
|
||||
现在可以正常使用 fumamxCRM 了。让我也重启一下 CRM 后端服务:
|
||||
|
||||
---
|
||||
|
||||
从你提供的 `sessionStorage` 中提取的:
|
||||
|
||||
```javascript
|
||||
sessionStorage.getItem('accessToken')
|
||||
'{"ownerId":11011595,"scope":"11011595","value":"71uRBqAwJWWLxWyp",...}'
|
||||
↑↑↑↑↑↑↑↑↑↑
|
||||
这就是 ACCESS_TOKEN
|
||||
```
|
||||
|
||||
我提取了 JSON 中 `value` 字段的值:
|
||||
- `ACCESS_TOKEN` = `"71uRBqAwJWWLxWyp"`
|
||||
- `INDIVIDUAL_ACCESS_TOKEN` = `"CmA5vs7xmGEYcsuD"` (来自 `individualAccessToken` 的 value)
|
||||
|
||||
---
|
||||
|
||||
**这两个 token 的区别**:
|
||||
|
||||
| Token | 来源 | 用途 |
|
||||
|-------|------|------|
|
||||
| `ACCESS_TOKEN` | `accessToken.value` | 公司/企业级别认证 |
|
||||
| `INDIVIDUAL_ACCESS_TOKEN` | `individualAccessToken.value` | 个人用户级别认证 |
|
||||
|
||||
fumamx API 请求时需要同时提供这两个 token 作为 URL 参数。
|
||||
|
||||
|
||||
对,**cookie 里没有 `ACCESS_TOKEN`**。
|
||||
|
||||
当前 cookie 文件里只有这 7 个:
|
||||
| Cookie | 值 |
|
||||
|--------|-----|
|
||||
| `_t` | `CmA5vs7xmGEYcsuD` |
|
||||
| `user` | `showmin.zhou@163.com` |
|
||||
| `pass` | `U2FsdGVkX1...` |
|
||||
| `language` | `zh-cn` |
|
||||
| `timezone` | `Asia/Shanghai` |
|
||||
| `AiImageEdit` | `false` |
|
||||
| `rememberPassword` | `true` |
|
||||
|
||||
**注意**:`_t` 的值恰好和 `INDIVIDUAL_ACCESS_TOKEN` 相同(`CmA5vs7xmGEYcsuD`),但 `ACCESS_TOKEN`(`71uRBqAwJWWLxWyp`)**完全不在 cookie 中**。
|
||||
|
||||
---
|
||||
|
||||
**fumamx 的三层认证**:
|
||||
|
||||
| 认证层 | 存储位置 | 获取方式 |
|
||||
|--------|----------|----------|
|
||||
| Cookie `_t` | Cookie | 浏览器导出 |
|
||||
| `INDIVIDUAL_ACCESS_TOKEN` | Cookie `_t` / sessionStorage | 相同 |
|
||||
| `ACCESS_TOKEN` | **sessionStorage** | `sessionStorage.getItem('accessToken').value` |
|
||||
|
||||
所以之前只更新 cookie 是不够的,还必须从 `sessionStorage` 获取 `ACCESS_TOKEN`。
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
根据代码梳理,给你完整说明:
|
||||
|
||||
---
|
||||
|
||||
## 1️⃣ 现在有几个表?
|
||||
|
||||
**共 7 个表**(SQLite):
|
||||
|
||||
| 表名 | 用途 |
|
||||
|------|------|
|
||||
| `users` | 管理员用户(登录) |
|
||||
| `customers` | 本地客户(早期表,现在用下面的) |
|
||||
| `countries` | 国家信息(274条数据) |
|
||||
| `import_tasks` | 潜在客户导入任务(后台任务) |
|
||||
| `potential_customers` | **潜在客户主表**(核心) |
|
||||
| `potential_contacts` | 潜在客户联系人 |
|
||||
| `deep_analyses` | 深度分析记录 |
|
||||
| `system_settings` | 系统设置(API Key等) |
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ 潜在客户列表加载逻辑
|
||||
|
||||
**前端请求** → **后端查询** → **直接返回**(无级联):
|
||||
|
||||
```python
|
||||
# 1. 前端请求
|
||||
GET /potential-customers/?keyword=xxx&country=US&page=1&page_size=20
|
||||
|
||||
# 2. 后端查询(单次查询,无JOIN)
|
||||
query = db.query(PotentialCustomer)
|
||||
if keyword:
|
||||
query = query.filter(name包含 OR domain包含 OR description包含)
|
||||
if country:
|
||||
query = query.filter(country == country)
|
||||
|
||||
# 3. 排序分页
|
||||
customers = query.order_by(source_score DESC).offset().limit().all()
|
||||
|
||||
# 4. 直接返回(反范式设计,状态内联在客户数据中)
|
||||
return {
|
||||
"results": [c.to_dict() for c in customers] # 包含 analysis_status
|
||||
}
|
||||
```
|
||||
|
||||
**关键点**:
|
||||
- `analysis_status` 直接存储在 `potential_customers` 表(冗余字段)
|
||||
- **无需 JOIN** `deep_analyses` 表
|
||||
- **无需二次查询** 来判断"是否已分析"
|
||||
|
||||
---
|
||||
|
||||
## 3️⃣ 深度分析过程
|
||||
|
||||
**队列式后台处理**:
|
||||
|
||||
```
|
||||
用户点击"深度分析"
|
||||
↓
|
||||
创建 DeepAnalysis 记录 (status=pending)
|
||||
↓
|
||||
更新 customer.analysis_status = "pending" ← 反范式同步
|
||||
↓
|
||||
加入内存队列 (analysis_queue)
|
||||
↓
|
||||
后台线程取出任务
|
||||
↓
|
||||
更新 analysis.status = "processing"
|
||||
更新 customer.analysis_status = "processing"
|
||||
↓
|
||||
调用 LLM API (DeepSeek/Moonshot)
|
||||
↓
|
||||
生成报告文件 → 保存到 analysis_reports/
|
||||
↓
|
||||
更新 analysis.status = "completed"
|
||||
更新 customer.analysis_status = "completed"
|
||||
更新 customer.analysis_count += 1
|
||||
```
|
||||
|
||||
**状态流转**:
|
||||
```
|
||||
none → pending → processing → completed
|
||||
↘ failed
|
||||
```
|
||||
|
||||
**反范式设计核心**:
|
||||
- 分析状态同时存两份:
|
||||
1. `deep_analyses.status` —— 详细记录
|
||||
2. `potential_customers.analysis_status` —— 冗余字段(列表页直接用)
|
||||
|
||||
这样列表页查询时**只需查一张表**,性能提升明显。
|
||||
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user