daShangDao_kfz_goods_pricing/交接文档.md
97694732@qq.com e26d7a027e first commit
2026-06-11 16:06:08 +08:00

375 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 商品定价服务kfz-goods-pricing项目交接文档
> 生成时间2026-05-27
> 项目路径D:\work\newProject\kfz-goods-pricing
---
## 一、项目概述
**项目名称**商品定价服务kfz-goods-pricing
**技术栈**Go 1.25 + SQLite + HTTP API + 原生 GUI无 Vue/Wails纯 Go
**输出可执行文件**kfz-goods-pricing.exe
**核心功能**:接收商品 ISBN 查询请求,通过孔网搜索 API 获取市场定价,计算最优价格并回调通知下游系统
> ⚠️ **重要区别**:本项目与之前生成的 kfz-move-kfz / xy-verify-price / kfz-verify-price 项目完全不同——不是 Wails 桌面应用,而是纯 Go HTTP 服务 + 原生 GUI 窗口,没有前端框架,没有子进程 B 程序。
### 目录结构
`
kfz-goods-pricing/
├── cmd/
│ ├── server/
│ │ ├── main.go # 程序入口HTTP服务 + GUI窗口 + 定时器)
│ │ └── gui_windows.go # GUI 窗口实现
├── internal/
│ ├── config/ # 配置加载YAML + 全局单例)
│ │ └── config.go
│ ├── database/ # SQLite 数据库初始化
│ │ └── db.go
│ ├── handler/ # HTTP 处理器4个文件
│ │ ├── goods_handler.go # /api/goods/query — 商品查询
│ │ ├── token_handler.go # /api/token/* — Token 管理(增删改查/启用)
│ │ ├── kfz_handler.go # /api/kfz/login — 孔网登录
│ │ └── config_handler.go # /api/config/price/* — 价格配置
│ ├── model/
│ │ └── book.go # BookInfo 图书信息结构体
│ ├── repository/ # 数据访问层
│ │ ├── token_repository.go # Token CRUD + 启用状态
│ │ ├── goods_repository.go # 商品记录 CRUD + 失败计数
│ │ └── config_repository.go # kfz_config 表(永远只有 ID=1 一条)
│ └── service/
│ └── goods_service.go # 核心业务逻辑(定时器/限速/爬虫/回调)
├── pkg/ # 公共库目录(当前为空)
├── config/
│ └── config.yaml # 程序配置文件
├── data/
│ └── goods_pricing.db # SQLite 数据库文件
├── go.mod / go.sum
├── kfz-goods-pricing.exe # 编译后的可执行文件
└── README.md
`
---
## 二、技术栈
| 组件 | 依赖 | 版本 | 说明 |
|------|------|------|------|
| HTTP 服务 | Go 标准库 | 内置 |
et/http无框架裸serveMux路由 |
| 数据库 | modernc.org/sqlite | v1.50.0 | 纯 Go SQLite无 CGO |
| HTTP 客户端 | github.com/parnurzeal/gorequest | v0.3.0 | 孔网 API 调用 |
| 配置解析 | gopkg.in/yaml.v3 | v3.0.1 | config.yaml 读取 |
| 日志 | log | 内置 | 输出到 GUI 窗口guiLogWriter |
| GUI | 原生实现 | — | gui_windows.go 中的 GUI 窗口GTK/原生混合) |
---
## 三、架构设计
`
┌─────────────────────────────────────────────────────────────────┐
│ kfz-goods-pricing.exe │
│ │
│ ┌────────────┐ ┌──────────────┐ ┌─────────────────┐ │
│ │ GUI 窗口 │ │ HTTP 服务 │ │ 定时器 (Timer) │ │
│ │(gui_windows)│ │(net/http) │ │ 每 N 秒执行一次 │ │
│ └────────────┘ └──────┬───────┘ └────────┬────────┘ │
│ │ │ │
│ ┌──────────────────┬┴─────────────────────┘ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ ┌────────────────────────┐ │
│ │ TokenHandler │ │GoodsHandler │ │ GoodsService │ │
│ │ /api/token/* │ │/api/goods/...│ │ (核心业务逻辑) │ │
│ └──────┬───────┘ └──────┬───────┘ └─────────┬──────────────┘ │
│ │ │ │ │
│ ┌──────▼────────────────▼────────────────────▼──────────────┐ │
│ │ Repository 层 │ │
│ │ TokenRepo │ GoodsRepo │ ConfigRepo │ │
│ └────────────────────────┬───────────────────────────────────┘ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐
│ │ SQLite (goods_pricing.db) │
│ │ kfz_token (Token管理) │ kfz_goods (商品记录) │ kfz_config │
│ └─────────────────────────────────────────────────────────────────┘
│ │
│ 外部调用: │
│ ① 上游系统 → POST /api/goods/query → 写入 kfz_goods │
│ ② 定时器 → 查询 kfz_goods按失败次数/更新时间排序) │
│ → 爬孔网搜索API → 计算价格 → POST callbackURL │
│ ③ 回调 URL: http://192.168.101.213:9090/api/product/updatePrice│
└─────────────────────────────────────────────────────────────────┘
`
---
## 四、HTTP API 接口
### 4.1 商品查询(上游系统调用入口)
**POST** /api/goods/query
**请求参数**form-data 或 x-www-form-urlencoded
| 参数 | 类型 | 必填 | 说明 |
|--------------------|------|--|-------------------|
| isbn | string | | 商品 ISBN |
| book_name | string | | 商品 名称 |
| author | string | | 商品 作者 |
| publishing | string | | 商品 出版社 |
| out_id | string | | 输出 ID回调时透传 |
| quality | string | | 品相100=全新90=九品等) |
| query_index | int | | 想排第几位(默认取最后一条) |
| user_id | string | | 用户 ID回调时透传 |
| placeholder_down_price | float | | 占位降价 |
| min_shipping_fee | float | | 最低运费 |
| min_price | float | | 最低书价 |
**响应**
`json
{"code":200,"message":"success","id":123}
`
> 说明:只写入数据库,返回插入记录的 ID。实际核价由定时器异步完成。
### 4.2 Token 管理
| 方法 | 路径 | 说明 |
|------|------|------|
| POST | /api/token/add | 批量添加 TokenJSON数组 |
| GET | /api/token/list | 查询所有 Token |
| POST | /api/token/delete | 删除 Tokenid 参数) |
| POST | /api/token/update | 修改 Tokenid/username/token/is_enable |
| GET | /api/token/enabled | 获取所有启用的 Token |
**批量添加请求体**
`json
[{"username":"孔网账号1","token":"PHPSESSID_xxx"},{"username":"孔网账号2","token":"PHPSESSID_yyy"}]
`
### 4.3 孔网登录
**POST** /api/kfz/login
| 参数 | 说明 |
|------|------|
| username | 孔网用户名 |
| password | 孔网密码 |
**响应**
`json
{"code":200,"message":"success","data":{"userId":123456,"nickname":"xxx","mobile":"138xxxx","token":"PHPSESSID_xxx"}}
`
### 4.4 价格配置
| 方法 | 路径 | 说明 |
|------|------|------|
| GET | /api/config/price/get | 获取完整配置yaml + DB 覆盖层合并) |
| POST | /api/config/price/set | 修改价格配置(只更新入参字段) |
**配置合并逻辑**GetConfigPrice
`
yaml配置Port / TimerInterval / APIRateLimit / CallbackURL
+ DB配置NewPrice / PlaceholderDownPrice / MinShippingFee / MinPrice / QueryIndex
= 最终配置DB 优先覆盖 yaml 的价格字段)
`
---
## 五、定时器核心逻辑GoodsService.syncGoodsPricing
`
每 N 秒(默认 5 秒)触发一次:
1. 从 kfz_config 表读取价格参数
2. 从 kfz_goods 表取一条记录(按 fail_count ASC, updated_at DESC
└→ 优先处理失败次数少的记录
3. 调用 outGetAllGoods(isbn, quality, queryIndex) 爬孔网搜索 API
├→ 使用轮询方式从 kfz_token 表选一个启用 Token
└→ URL: https://search.kongfz.com/pc-gw/search-web/client/pc/product/keyword/list
4. 计算最终价格:
finalPrice = totalPrice - placeholderDownPrice - minShippingFee
if (finalPrice < minPrice) → finalPrice = minPrice
5. 更新 kfz_goods 表price / shipping_fee / final_price / updated_at
6. 调用 sendCallback(outID, userID, finalPrice, minShippingFee)
└→ POST
product_id=xxx&user_id=xxx&sale_price=xxx&cost=xxx
sale_price 和 cost 均为整数,单位:分 = 原价 × 100
7. 失败处理MarkFailed → fail_count++,下次优先重试
`
---
## 六、数据库设计
### 6.1 kfz_token 表
`sql
CREATE TABLE kfz_token (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT NOT NULL,
token TEXT NOT NULL,
is_enable INTEGER DEFAULT 1
);
`
**用途**:存储多个孔网账号 Token支持轮询切换防止单个 Token 请求过快被限
### 6.2 kfz_goods 表
`sql
CREATE TABLE kfz_goods (
id INTEGER PRIMARY KEY AUTOINCREMENT,
isbn TEXT NOT NULL,
out_id TEXT,
quality TEXT,
user_id TEXT,
query_index INTEGER DEFAULT 1,
placeholder_down_price REAL DEFAULT 0.01,
min_shipping_fee REAL DEFAULT 5.0,
min_price REAL DEFAULT 1.0,
price REAL DEFAULT 0,
shipping_fee REAL DEFAULT 0,
final_price REAL DEFAULT 0,
fail_count INTEGER DEFAULT 0,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
`
**用途**:上游调用 QueryGoods 时写入记录,定时器按 fail_count ASC 排序依次处理
### 6.3 kfz_config 表
`sql
CREATE TABLE kfz_config (
id INTEGER PRIMARY KEY CHECK (id = 1),
new_price REAL DEFAULT 0,
placeholder_down_price REAL DEFAULT 0.01,
min_shipping_fee REAL DEFAULT 5.0,
min_price REAL DEFAULT 1.0,
query_index INTEGER DEFAULT 1
);
`
**用途**始终只有一条记录ID=1与 config.yaml 共同构成配置体系。yaml 负责基础配置Port/Timer/APIRateLimit/CallbackURLDB 负责价格参数
---
## 七、关键设计
### Token 轮询机制
`
tokens = GetEnabledTokens() // 从 DB 取出所有启用 Token
currentTokenIndex = 0 // 原子操作的轮询索引
每次爬孔网 API 时:
token = tokens[currentTokenIndex % len(tokens)]
currentTokenIndex++
`
> 目的:多个孔网账号 Token 轮询使用,防止单个账号请求过快被限
### 价格计算公式
`
totalPrice = price + shippingFee
finalPrice = totalPrice - placeholderDownPrice - minShippingFee
if finalPrice < minPrice → finalPrice = minPrice
`
最终回调中的 sale_price 和 cost 均为整数(单位:分 = float × 100
### 配置双层机制
| 来源 | 字段 | 说明 |
|------|------|------|
| config/config.yaml | Port, TimerInterval, APIRateLimit, CallbackURL | 基础运行时配置 |
| kfz_config DB | NewPrice, PlaceholderDownPrice, MinShippingFee, MinPrice, QueryIndex | 价格参数 |
yaml 不可动态修改DB 配置可通过 API 动态修改(实时生效)。
---
## 八、第三方接口
### 孔网登录
| 步骤 | URL | 方法 |
|------|-----|------|
| 登录 | https://login.kongfz.com/Pc/Login/account | POST (form-data) |
| 获取用户信息 | https://user.kongfz.com/User/Index/getUserInfo/ | GET (Cookie: PHPSESSID=xxx) |
### 孔网商品搜索
| 用途 | URL |
|------|-----|
| 搜索商品 | https://search.kongfz.com/pc-gw/search-web/client/pc/product/keyword/list?dataType=0&page=1&sortType=7&quaSelect=2&keyword=ISBN&quality=品相~ |
### 回调
| 回调 URL | 参数 |
|---------|------|
| http://192.168.101.213:9090/api/product/updatePrice | product_id, user_id, sale_price, cost |
---
## 九、运行与构建
`ash
# 运行(开发)
go run cmd/server/main.go
# 编译
go build -o kfz-goods-pricing.exe ./cmd/server
`
**启动后**
1. 加载 config/config.yaml
2. 初始化 data/goods_pricing.dbSQLite自动建表
3. 启动 HTTP 服务(默认端口 8080
4. 启动 GUI 窗口(阻塞主线程)
5. 启动定时器5秒间隔开始首次同步
**默认配置**config/config.yaml
`yaml
port: "8080"
timer_interval: 5 # 每5秒执行一次定时任务
api_rate_limit: 2 # API 请求间隔2秒
callback_url: "http://192.168.101.213:9090/api/product/updatePrice"
`
---
## 十、交接注意事项
| 文件/目录 | 说明 | 注意点 |
|-----------|------|--------|
| config/config.yaml | 基础配置 | Port/TimerInterval/APIRateLimit/CallbackURL |
| data/goods_pricing.db | SQLite 数据库 | 运行时自动创建,无需手动初始化 |
| internal/handler/kfz_handler.go | 孔网登录逻辑 | HTTP 直接调用孔网官网,无需 kfz.dll与之前的 Wails 项目不同) |
| internal/service/goods_service.go | 定时器核心 | 异步处理,依赖 Token 池 |
| kfz-goods-pricing.exe | 可执行文件 | 需与 config/、data/ 目录同目录运行 |
### 快速排查
- **上游调用无响应**:检查 HTTP 服务是否正常(默认端口 8080检查防火墙
- **定时器无动作**:检查 kfz_goods 是否有数据;检查 Token 是否为空或已过期
- **核价结果全为默认值**:孔网搜索 API 无返回kfzConfig.NewPrice 作为兜底
- **回调失败**:检查 callbackURL 是否可达192.168.101.213:9090
- **Token 失效**:上游系统调用 /api/kfz/login 获取新 Token或手动 /api/token/add 添加
### 与 Wails 桌面项目的核心区别
| 对比项 | 本项目kfz-goods-pricing | Wails 项目们 |
|--------|----------------------------|-------------|
| 类型 | **纯 Go HTTP 服务 + GUI 窗口** | Wails 桌面应用 |
| 前端 | **无**(无 Vue/HTML | Vue 3 + Element Plus |
| 子进程 | **无** | 有verify-price-b |
| Token 来源 | HTTP 直接登录孔网 | kfz.dllWindows DLL |
| 数据库 | SQLite本地文件 | SQLite |
| 部署方式 | 后台 HTTP 服务(可远程调用) | 桌面 exe本地运行 |