chore: 初始化 T-001 Go 骨架
This commit is contained in:
@@ -0,0 +1,25 @@
|
|||||||
|
# 占位示例,勿填写真实凭据后提交。
|
||||||
|
osi:
|
||||||
|
base_url: "http://<osi-host>"
|
||||||
|
org_code: "<org-code>"
|
||||||
|
user_name: "<user-name>"
|
||||||
|
ask: "<ask-secret>"
|
||||||
|
device_sn: "<device-sn>"
|
||||||
|
operate_user: "<operate-user>"
|
||||||
|
timeout_sec: 20
|
||||||
|
socks5_proxy: "<socks5-host:port>"
|
||||||
|
|
||||||
|
phis:
|
||||||
|
base_url: "http://<phis-host>"
|
||||||
|
token: "<phis-token>"
|
||||||
|
region_code: "<region-code>"
|
||||||
|
poll_interval_sec: 5
|
||||||
|
|
||||||
|
redis:
|
||||||
|
addr: ""
|
||||||
|
db: 0
|
||||||
|
|
||||||
|
report_log_file_dir: "logs"
|
||||||
|
retry_max: 3
|
||||||
|
circuit_fail_threshold: 5
|
||||||
|
circuit_sleep_sec: 60
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import "github.com/spf13/viper"
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
OSI OSIConfig `mapstructure:"osi"`
|
||||||
|
PHIS PHISConfig `mapstructure:"phis"`
|
||||||
|
Redis RedisConfig `mapstructure:"redis"`
|
||||||
|
ReportLogFileDir string `mapstructure:"report_log_file_dir"`
|
||||||
|
RetryMax int `mapstructure:"retry_max"`
|
||||||
|
CircuitFailThreshold int `mapstructure:"circuit_fail_threshold"`
|
||||||
|
CircuitSleepSec int `mapstructure:"circuit_sleep_sec"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type OSIConfig struct {
|
||||||
|
BaseURL string `mapstructure:"base_url"`
|
||||||
|
OrgCode string `mapstructure:"org_code"`
|
||||||
|
UserName string `mapstructure:"user_name"`
|
||||||
|
Ask string `mapstructure:"ask"`
|
||||||
|
DeviceSN string `mapstructure:"device_sn"`
|
||||||
|
OperateUser string `mapstructure:"operate_user"`
|
||||||
|
TimeoutSec int `mapstructure:"timeout_sec"`
|
||||||
|
Socks5Proxy string `mapstructure:"socks5_proxy"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PHISConfig struct {
|
||||||
|
BaseURL string `mapstructure:"base_url"`
|
||||||
|
Token string `mapstructure:"token"`
|
||||||
|
RegionCode string `mapstructure:"region_code"`
|
||||||
|
PollIntervalSec int `mapstructure:"poll_interval_sec"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RedisConfig struct {
|
||||||
|
Addr string `mapstructure:"addr"`
|
||||||
|
DB int `mapstructure:"db"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func Load(path string) (Config, error) {
|
||||||
|
v := viper.New()
|
||||||
|
setDefaults(v)
|
||||||
|
|
||||||
|
if path != "" {
|
||||||
|
v.SetConfigFile(path)
|
||||||
|
v.SetConfigType("yaml")
|
||||||
|
if err := v.ReadInConfig(); err != nil {
|
||||||
|
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
||||||
|
return Config{}, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var cfg Config
|
||||||
|
if err := v.Unmarshal(&cfg); err != nil {
|
||||||
|
return Config{}, err
|
||||||
|
}
|
||||||
|
return cfg, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setDefaults(v *viper.Viper) {
|
||||||
|
v.SetDefault("osi.base_url", "http://<osi-host>")
|
||||||
|
v.SetDefault("osi.timeout_sec", 20)
|
||||||
|
v.SetDefault("phis.poll_interval_sec", 5)
|
||||||
|
v.SetDefault("redis.db", 0)
|
||||||
|
v.SetDefault("report_log_file_dir", "logs")
|
||||||
|
v.SetDefault("retry_max", 3)
|
||||||
|
v.SetDefault("circuit_fail_threshold", 5)
|
||||||
|
v.SetDefault("circuit_sleep_sec", 60)
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package config
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestLoadExampleConfig(t *testing.T) {
|
||||||
|
cfg, err := Load("../config.yaml.example")
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Load example config: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.OSI.BaseURL == "" {
|
||||||
|
t.Fatal("OSI base_url should have a placeholder value")
|
||||||
|
}
|
||||||
|
if cfg.OSI.TimeoutSec != 20 {
|
||||||
|
t.Fatalf("OSI timeout_sec = %d, want 20", cfg.OSI.TimeoutSec)
|
||||||
|
}
|
||||||
|
if cfg.ReportLogFileDir != "logs" {
|
||||||
|
t.Fatalf("report_log_file_dir = %q, want logs", cfg.ReportLogFileDir)
|
||||||
|
}
|
||||||
|
}
|
||||||
+2
-1
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
## 阶段 0 · 脚手架与契约骨架
|
## 阶段 0 · 脚手架与契约骨架
|
||||||
|
|
||||||
- [ ] 初始化 Go module、单 `main.go`(`-mode server|deliver`)、`config/`(viper)。
|
- [x] 初始化 Go module、单 `main.go`(`-mode server|deliver`)、`config/`(viper)。
|
||||||
- [ ] 落地 `osi/sign.go`(MD5 签名)+ 单测:用文档约定的 `ts/ask` 校验 `password` 形态(32 位小写)。
|
- [ ] 落地 `osi/sign.go`(MD5 签名)+ 单测:用文档约定的 `ts/ask` 校验 `password` 形态(32 位小写)。
|
||||||
- [ ] 移植旧项目 `transport.go`/`http_client.go` → `osi/transport.go`(保留 SOCKS5/超时,去 cookiejar 与拟态头)。
|
- [ ] 移植旧项目 `transport.go`/`http_client.go` → `osi/transport.go`(保留 SOCKS5/超时,去 cookiejar 与拟态头)。
|
||||||
- [ ] `osi/client.go` 的 `Call(serviceId, body, out)`:注入头+信封+发送+判码(成功码实测 `"01"`,按去前导零判定;`405` 可重试,见 docs/01 §1)。
|
- [ ] `osi/client.go` 的 `Call(serviceId, body, out)`:注入头+信封+发送+判码(成功码实测 `"01"`,按去前导零判定;`405` 可重试,见 docs/01 §1)。
|
||||||
@@ -87,3 +87,4 @@
|
|||||||
- 映射层:**与旧项目相当或略增**(但从"逆向猜"变为"照文档写",更确定、更可测)。
|
- 映射层:**与旧项目相当或略增**(但从"逆向猜"变为"照文档写",更确定、更可测)。
|
||||||
- 投递流水线:**基本复用**旧项目经验,少量适配。
|
- 投递流水线:**基本复用**旧项目经验,少量适配。
|
||||||
- 净效果:总复杂度显著下降,且代码意图清晰可交接。
|
- 净效果:总复杂度显著下降,且代码意图清晰可交接。
|
||||||
|
|
||||||
|
|||||||
+26
-18
@@ -6,25 +6,28 @@
|
|||||||
## 当前快照
|
## 当前快照
|
||||||
|
|
||||||
- 日期:2026-07-06
|
- 日期:2026-07-06
|
||||||
- 阶段:**pre-code**(设计完成、Go 骨架未建立;路线图阶段 0 未开始)
|
- 阶段:**Phase 0 已起步**;T-001 已完成,Go module 与 CLI/config 骨架已建立
|
||||||
- 技术栈:目标 Go 1.24 单二进制(未落地);当前仅有 Python 联调脚本(requests,本地不入库)
|
- 技术栈:Go 1.24 单二进制;`main.go -mode server|deliver`;配置读取使用 viper
|
||||||
- 生产代码:**无**。`go.mod` 不存在,`osi/ contract/ mapping/` 等目录均待建
|
- 生产代码:已有 `main.go`、`config/`、`go.mod`/`go.sum`;`osi/ contract/ mapping/` 等业务目录仍待后续任务建立
|
||||||
- 联调现实:**JKDA00002 个人档案查询已用 Python 脚本打通真实沙箱**(`code="01"`),实测契约沉淀在 `docs/04 §8`
|
- 联调现实:**JKDA00002 个人档案查询已用 Python 脚本打通真实沙箱**(`code="01"`),实测契约沉淀在 `docs/04 §8`
|
||||||
- 测试:`tests/test_query_health_record.py` **已失效**(测的是被重写前的脚本 API,import 报错)
|
- 测试:`go test ./...` 通过;当前测试覆盖 mode 解析与 `config.yaml.example` 加载
|
||||||
- 标准启动路径:`./init.sh`(阶段 0 未完成前会提示先做 T-001,属预期行为)
|
- 标准启动路径:`./init.sh`(需要 bash/WSL 环境;当前 Windows 环境未安装 WSL,直接运行会失败)
|
||||||
- 标准验证路径:`go test ./...`(骨架建立后生效)
|
- 标准验证路径:`go test ./...`、`go build ./...`
|
||||||
- 当前 blocker:无硬 blocker。软阻塞:厂家侧 B1/B2/B4/B5 契约缺口(见 `docs/06`),只影响阶段 4,不阻塞阶段 0~3
|
- 当前 blocker:无硬 blocker。软阻塞:厂家侧 B1/B2/B4/B5 契约缺口(见 `docs/06`),只影响阶段 4,不阻塞阶段 0~3
|
||||||
|
|
||||||
## 当前目录要点
|
## 当前目录要点
|
||||||
|
|
||||||
| 路径 | 状态 | 说明 |
|
| 路径 | 状态 | 说明 |
|
||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
|
| `main.go` | 已有 | 单入口,解析 `-mode server|deliver` 与 `-config` |
|
||||||
|
| `config/` | 已有 | viper 配置加载,含最小单测 |
|
||||||
|
| `config.yaml.example` | 已有 | 占位配置,不含真实凭据 |
|
||||||
|
| `go.mod` `go.sum` | 已有 | module `chis_osi`,依赖 viper |
|
||||||
| `docs/` | 已有 | 设计文档集 01~06 + 本快照;`账号.txt` 本地留存不入库 |
|
| `docs/` | 已有 | 设计文档集 01~06 + 本快照;`账号.txt` 本地留存不入库 |
|
||||||
| `tasks.md` `progress.md` | 已有 | 任务看板 / 执行流水(根目录) |
|
| `tasks.md` `progress.md` | 已有 | 任务看板 / 执行流水(根目录) |
|
||||||
| `scripts/` | 已有·不入库 | Python 联调脚本(硬编码真实凭据与身份证,已 gitignore) |
|
| `scripts/` | 已有·不入库 | Python 联调脚本(硬编码真实凭据与身份证,勿提交) |
|
||||||
| `tests/` | 已有·失效 | 旧脚本测试,待 T-000 处理 |
|
| `config.yaml` | 已有·不入库 | 真实凭据;不要提交 |
|
||||||
| `config.yaml` | 已有·不入库 | 真实凭据;`config.yaml.example` 待 T-001 建立 |
|
| `osi/` `contract/` `mapping/` 等 | 待建 | 路线图阶段 0~2 后续任务 |
|
||||||
| `main.go` `osi/` `contract/` `mapping/` 等 | 待建 | 路线图阶段 0~2 |
|
|
||||||
|
|
||||||
## 已验证事实(写代码时直接依赖)
|
## 已验证事实(写代码时直接依赖)
|
||||||
|
|
||||||
@@ -37,19 +40,24 @@
|
|||||||
## 当前可运行内容
|
## 当前可运行内容
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 本地联调查询(唯一可运行路径;脚本硬编码凭据,勿提交)
|
# Go 骨架验证
|
||||||
python3 scripts/query_health_record.py
|
|
||||||
|
|
||||||
# Go 侧(阶段 0 后)
|
|
||||||
./init.sh
|
|
||||||
go test ./...
|
go test ./...
|
||||||
|
go build ./...
|
||||||
|
go run . -mode server -config config.yaml.example
|
||||||
|
go run . -mode deliver -config config.yaml.example
|
||||||
|
|
||||||
|
# 统一入口(需要 bash/WSL)
|
||||||
|
./init.sh
|
||||||
|
|
||||||
|
# 本地联调查询(脚本硬编码凭据,勿提交)
|
||||||
|
python3 scripts/query_health_record.py
|
||||||
```
|
```
|
||||||
|
|
||||||
## 下一步
|
## 下一步
|
||||||
|
|
||||||
1. T-000:处理失效测试(`tasks.md` Phase B)。
|
1. T-002:`osi/sign.go` MD5 头签名 + 单测。
|
||||||
2. T-001:go module + 双子命令骨架,进入路线图阶段 0。
|
2. T-003:`osi/transport.go` 传输层骨架。
|
||||||
|
|
||||||
## 维护规则
|
## 维护规则
|
||||||
|
|
||||||
发生以下变化时覆盖更新本文:入口/目录变动、任务状态变化、新增可运行命令、发现文档与代码现实不一致。本文只留当前快照,不留历史。
|
发生以下变化时覆盖更新本文:入口/目录变动、任务状态变化、新增可运行命令、发现文档与代码现实不一致。本文只留当前快照,不留历史。
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
module chis_osi
|
||||||
|
|
||||||
|
go 1.24
|
||||||
|
|
||||||
|
require github.com/spf13/viper v1.20.1
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||||
|
github.com/sagikazarmark/locafero v0.7.0 // indirect
|
||||||
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
|
github.com/spf13/afero v1.12.0 // indirect
|
||||||
|
github.com/spf13/cast v1.7.1 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.6 // indirect
|
||||||
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
|
go.uber.org/atomic v1.9.0 // indirect
|
||||||
|
go.uber.org/multierr v1.9.0 // indirect
|
||||||
|
golang.org/x/sys v0.29.0 // indirect
|
||||||
|
golang.org/x/text v0.21.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
|
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
|
||||||
|
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||||
|
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
|
github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo=
|
||||||
|
github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k=
|
||||||
|
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||||
|
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||||
|
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
|
||||||
|
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
|
||||||
|
github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
||||||
|
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||||
|
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||||
|
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||||
|
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
|
||||||
|
github.com/spf13/viper v1.20.1/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||||
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
|
go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
|
||||||
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
|
||||||
|
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
|
||||||
|
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||||
|
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||||
|
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"chis_osi/config"
|
||||||
|
)
|
||||||
|
|
||||||
|
type appMode string
|
||||||
|
|
||||||
|
const (
|
||||||
|
modeServer appMode = "server"
|
||||||
|
modeDeliver appMode = "deliver"
|
||||||
|
)
|
||||||
|
|
||||||
|
func parseMode(raw string) (appMode, error) {
|
||||||
|
switch appMode(raw) {
|
||||||
|
case modeServer:
|
||||||
|
return modeServer, nil
|
||||||
|
case modeDeliver:
|
||||||
|
return modeDeliver, nil
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("invalid mode %q, want server or deliver", raw)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
modeFlag := flag.String("mode", string(modeServer), "运行模式:server 或 deliver")
|
||||||
|
configPath := flag.String("config", "config.yaml", "配置文件路径")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
mode, err := parseMode(*modeFlag)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = config.Load(*configPath)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "load config: %v\n", err)
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("chis_osi mode=%s\n", mode)
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestParseModeAcceptsServerAndDeliver(t *testing.T) {
|
||||||
|
for _, raw := range []string{"server", "deliver"} {
|
||||||
|
mode, err := parseMode(raw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("parseMode(%q) returned error: %v", raw, err)
|
||||||
|
}
|
||||||
|
if string(mode) != raw {
|
||||||
|
t.Fatalf("parseMode(%q) = %q", raw, mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseModeRejectsUnknownMode(t *testing.T) {
|
||||||
|
if _, err := parseMode("worker"); err == nil {
|
||||||
|
t.Fatal("parseMode accepted unknown mode")
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -48,3 +48,12 @@
|
|||||||
- 验证:`git status --short` 确认删除前后均无该目录的跟踪历史;删除后工作区干净。
|
- 验证:`git status --short` 确认删除前后均无该目录的跟踪历史;删除后工作区干净。
|
||||||
- 决策:不改写为新契约的最小测试,直接删除——脚本是硬编码的本地联调工具(不入库),暂不需要自动化测试覆盖。
|
- 决策:不改写为新契约的最小测试,直接删除——脚本是硬编码的本地联调工具(不入库),暂不需要自动化测试覆盖。
|
||||||
- 下一步:T-001,进入路线图阶段 0(go module + 双子命令骨架)。
|
- 下一步:T-001,进入路线图阶段 0(go module + 双子命令骨架)。
|
||||||
|
|
||||||
|
## 2026-07-06 T-001 初始化 Go module 与双子命令骨架
|
||||||
|
|
||||||
|
- 状态:DONE
|
||||||
|
- 变更:新增 `go.mod`/`go.sum`、`main.go`、`main_test.go`、`config/config.go`、`config/config_test.go`、`config.yaml.example`;`tasks.md` 标记 T-001 完成;`docs/05` 勾选阶段 0 第一项。
|
||||||
|
- 验证:`go test ./...` 通过;`go build ./...` 通过;`go run . -mode server -config config.yaml.example` 输出 `chis_osi mode=server`;`go run . -mode deliver -config config.yaml.example` 输出 `chis_osi mode=deliver`。
|
||||||
|
- 验证补充:`bash init.sh` 在当前 Windows 环境失败,系统提示未安装 WSL;已用等价 Go 命令完成验收。
|
||||||
|
- 决策:Go module 使用本地模块名 `chis_osi`;`main.go` 只打印 mode,不打印配置值,避免泄露真实环境信息;`config.yaml.example` 只保留占位值。
|
||||||
|
- 下一步:T-002(`osi/sign.go` MD5 头签名 + 单测)。
|
||||||
|
|||||||
@@ -25,7 +25,7 @@
|
|||||||
|
|
||||||
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
| ID | 任务 | 依赖 | 验收要点 | 状态 |
|
||||||
| --- | --- | --- | --- | --- |
|
| --- | --- | --- | --- | --- |
|
||||||
| T-001 | 初始化 go module + `main.go` 双子命令骨架 + `config/`(viper) | - | `go build ./...` 通过;`-mode server\|deliver` 可解析;`config.yaml.example` 占位值就绪 | TODO |
|
| T-001 | 初始化 go module + `main.go` 双子命令骨架 + `config/`(viper) | - | `go build ./...` 通过;`-mode server\|deliver` 可解析;`config.yaml.example` 占位值就绪 | DONE |
|
||||||
| T-002 | `osi/sign.go` MD5 头签名 + 单测 | T-001 | 单测校验 `password=md5("ts=<ts>&ask=<ask>")` 32 位小写;与 Python 脚本(scripts/,本地)产出比对一致 | TODO |
|
| T-002 | `osi/sign.go` MD5 头签名 + 单测 | T-001 | 单测校验 `password=md5("ts=<ts>&ask=<ask>")` 32 位小写;与 Python 脚本(scripts/,本地)产出比对一致 | TODO |
|
||||||
| T-003 | `osi/transport.go`:从 chis_upload 移植传输层 | T-001 | 保留 SOCKS5/超时;去掉 cookiejar 与网页拟态头;单测或最小连通验证 | TODO |
|
| T-003 | `osi/transport.go`:从 chis_upload 移植传输层 | T-001 | 保留 SOCKS5/超时;去掉 cookiejar 与网页拟态头;单测或最小连通验证 | TODO |
|
||||||
| T-004 | `osi/client.go` `Call` + `osi/codes.go` + `contract/envelope.go` | T-002, T-003 | 信封为 `serviceId`+`uploadinfo{baseInfo,manageInfo,...}`(docs/04 §8);成功码按去前导零 == `"1"` 判定(实测 `"01"`,docs/01 §1);`405` 归类可重试 | TODO |
|
| T-004 | `osi/client.go` `Call` + `osi/codes.go` + `contract/envelope.go` | T-002, T-003 | 信封为 `serviceId`+`uploadinfo{baseInfo,manageInfo,...}`(docs/04 §8);成功码按去前导零 == `"1"` 判定(实测 `"01"`,docs/01 §1);`405` 归类可重试 | TODO |
|
||||||
@@ -62,3 +62,5 @@
|
|||||||
- 阶段 5:PHIS 真实接入与状态回写。
|
- 阶段 5:PHIS 真实接入与状态回写。
|
||||||
- 阶段 6:加固与交接(完整度定论、密钥环境变量化、运维文档、全绿)。
|
- 阶段 6:加固与交接(完整度定论、密钥环境变量化、运维文档、全绿)。
|
||||||
- 联调依赖跟踪见 `docs/06-厂家联调清单.md`(B 组契约缺口会阻塞阶段 4)。
|
- 联调依赖跟踪见 `docs/06-厂家联调清单.md`(B 组契约缺口会阻塞阶段 4)。
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user