diff --git a/admin/cmd/server/main.go b/admin/cmd/server/main.go new file mode 100644 index 0000000..b68b8db --- /dev/null +++ b/admin/cmd/server/main.go @@ -0,0 +1,26 @@ +package main + +import ( + "errors" + "log" + "net/http" + + "cmbuyer/admin/internal/server" +) + +const listenAddress = ":8080" + +func main() { + if err := run(); err != nil { + log.Fatal(err) + } +} + +func run() error { + err := http.ListenAndServe(listenAddress, server.NewRouter()) + if errors.Is(err, http.ErrServerClosed) { + return nil + } + + return err +} diff --git a/admin/go.mod b/admin/go.mod new file mode 100644 index 0000000..52f14d9 --- /dev/null +++ b/admin/go.mod @@ -0,0 +1,42 @@ +module cmbuyer/admin + +go 1.23.0 + +require ( + github.com/gin-gonic/gin v1.11.0 + github.com/mattn/go-sqlite3 v1.14.49 +) + +require ( + github.com/bytedance/sonic v1.14.0 // indirect + github.com/bytedance/sonic/loader v0.3.0 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.27.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/goccy/go-yaml v1.18.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/quic-go/qpack v0.5.1 // indirect + github.com/quic-go/quic-go v0.54.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.0 // indirect + go.uber.org/mock v0.5.0 // indirect + golang.org/x/arch v0.20.0 // indirect + golang.org/x/crypto v0.40.0 // indirect + golang.org/x/mod v0.25.0 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.27.0 // indirect + golang.org/x/tools v0.34.0 // indirect + google.golang.org/protobuf v1.36.9 // indirect +) diff --git a/admin/go.sum b/admin/go.sum new file mode 100644 index 0000000..8e399c0 --- /dev/null +++ b/admin/go.sum @@ -0,0 +1,90 @@ +github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= +github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= +github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= +github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +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/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= +github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= +github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= +github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.49 h1:B8jBHC3xhxZgxztrgruTuLucebnULQnx4W7cF7SAE9w= +github.com/mattn/go-sqlite3 v1.14.49/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +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/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= +github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= +github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= +golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= +golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w= +golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= +golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= +golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= +golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= +google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= +google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/admin/internal/server/router.go b/admin/internal/server/router.go new file mode 100644 index 0000000..0844e1b --- /dev/null +++ b/admin/internal/server/router.go @@ -0,0 +1,19 @@ +// Package server 定义采购服务当前拥有的 HTTP 端点。 +package server + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +// NewRouter 返回当前服务范围内的完整 HTTP 路由。 +func NewRouter() *gin.Engine { + router := gin.New() + + router.GET("/healthz", func(context *gin.Context) { + context.JSON(http.StatusOK, gin.H{"status": "ok"}) + }) + + return router +} diff --git a/admin/internal/server/router_test.go b/admin/internal/server/router_test.go new file mode 100644 index 0000000..056f660 --- /dev/null +++ b/admin/internal/server/router_test.go @@ -0,0 +1,28 @@ +package server_test + +import ( + "net/http" + "net/http/httptest" + "testing" + + "cmbuyer/admin/internal/server" +) + +func TestHealthz(t *testing.T) { + request := httptest.NewRequest(http.MethodGet, "/healthz", nil) + response := httptest.NewRecorder() + + server.NewRouter().ServeHTTP(response, request) + + if response.Code != http.StatusOK { + t.Fatalf("healthz status = %d, want %d", response.Code, http.StatusOK) + } + + if contentType := response.Header().Get("Content-Type"); contentType != "application/json; charset=utf-8" { + t.Fatalf("healthz content type = %q, want application/json; charset=utf-8", contentType) + } + + if body := response.Body.String(); body != "{\"status\":\"ok\"}" { + t.Fatalf("healthz body = %q, want {\"status\":\"ok\"}", body) + } +} diff --git a/admin/internal/storage/sqlite/sqlite.go b/admin/internal/storage/sqlite/sqlite.go new file mode 100644 index 0000000..7bd0739 --- /dev/null +++ b/admin/internal/storage/sqlite/sqlite.go @@ -0,0 +1,13 @@ +// Package sqlite 提供采购服务的 SQLite 驱动注册。 +package sqlite + +import ( + "database/sql" + + _ "github.com/mattn/go-sqlite3" +) + +// Open 打开 SQLite 数据源;调用方负责其 schema 与生命周期。 +func Open(dataSourceName string) (*sql.DB, error) { + return sql.Open("sqlite3", dataSourceName) +} diff --git a/admin/internal/storage/sqlite/sqlite_test.go b/admin/internal/storage/sqlite/sqlite_test.go new file mode 100644 index 0000000..791a1f1 --- /dev/null +++ b/admin/internal/storage/sqlite/sqlite_test.go @@ -0,0 +1,24 @@ +package sqlite_test + +import ( + "context" + "testing" + + "cmbuyer/admin/internal/storage/sqlite" +) + +func TestOpen(t *testing.T) { + database, err := sqlite.Open(":memory:") + if err != nil { + t.Fatalf("open SQLite database: %v", err) + } + t.Cleanup(func() { + if err := database.Close(); err != nil { + t.Errorf("close SQLite database: %v", err) + } + }) + + if err := database.PingContext(context.Background()); err != nil { + t.Fatalf("ping SQLite database: %v", err) + } +} diff --git a/docs/00-ai-start-here.md b/docs/00-ai-start-here.md index 3ebbf4b..3e02b29 100644 --- a/docs/00-ai-start-here.md +++ b/docs/00-ai-start-here.md @@ -85,13 +85,12 @@ cmbuyer 是一个自动化采购系统:**采购服务**(网页端,`admin/` ## 当前阶段 -**Phase 0 · 地基(尚未开始编码)。** 仓库目前只有文档,没有生产代码。 +**Phase 0 · 地基。** 采购服务骨架已初始化,采购工具骨架仍在进行;尚无采购业务代码。 执行按任务依赖驱动,**不按 Phase 整段串行等待**。当前优先路径: -1. 并行完成 T-001 采购服务骨架与 T-002 采购工具骨架。 -2. T-001 完成后推进 T-004 数据模型;T-002 完成后立即推进 - **T-101 → T-102 → T-103 真机取证**;两端骨架都完成后可并行补 T-003 统一入口。 +1. 继续完成 T-002 采购工具骨架;采购服务方向可并行推进 T-004 数据模型。 +2. T-002 完成后立即推进 **T-101 → T-102 → T-103 真机取证**,并可并行补 T-003 统一入口。 3. T-103 结论确认后,才开始依赖真机可读字段的 Phase 2 生产页面;采购服务核心与 T-104 → T-107 后续真机安全判据按依赖并行推进。 4. Phase 3:双端打通与**第一趟试选**端到端。 @@ -173,15 +172,17 @@ cmbuyer 是一个自动化采购系统:**采购服务**(网页端,`admin/` ## 验证命令 -> **占位符。** T-001 / T-002 落地后由该任务替换为真实命令,并同步 -> [`03-tech-stack.md`](03-tech-stack.md) 和 [`current-state.md`](current-state.md)。 +> 采购服务命令已由 T-001 实际验证;采购工具命令在 T-002 完成前仍是占位路径。 ```bash # 采购服务 admin/(改了 Go 代码后) +cd admin go test ./... go vet ./... +go build ./... -# 采购工具 client/(改了 Python 代码后) +# 采购工具 client/(T-002 完成后启用) +cd client python -m unittest discover -s tests -t . python -m compileall -q src tests diff --git a/docs/03-tech-stack.md b/docs/03-tech-stack.md index 8a0c5f4..91d4a1e 100644 --- a/docs/03-tech-stack.md +++ b/docs/03-tech-stack.md @@ -59,14 +59,13 @@ ## 五、构建与运行命令 -> **占位符。** 代码尚未初始化,以下命令分别在 T-001 / T-002 落地后由对应任务替换为真实可运行命令, -> 并同步到 [`00-ai-start-here.md`](00-ai-start-here.md) 和 [`current-state.md`](current-state.md)。 +> 采购服务命令已由 T-001 实际验证;采购工具命令由 T-002 完成后替换剩余占位项。 | 用途 | 采购服务(`admin/`) | 采购工具(`client/`) | | --- | --- | --- | | 安装依赖 | `go mod download` | `python -m venv .venv` + `pip install -r requirements.txt` | -| 本地开发 | 【T-001 填写】 | 【T-002 填写】 | -| 构建 | 【T-001 填写】 | 【T-002 填写】 | +| 本地开发 | `go run ./cmd/server` | 【T-002 填写】 | +| 构建 | `go build ./...` | 【T-002 填写】 | | 测试 | `go test ./...` | `python -m unittest discover -s tests -t .` | | 静态检查 | `go vet ./...` | `python -m compileall -q src tests` | diff --git a/docs/current-state.md b/docs/current-state.md index 01ba46a..9956ca2 100644 --- a/docs/current-state.md +++ b/docs/current-state.md @@ -12,30 +12,29 @@ ## 当前快照 - 日期:2026-08-03 -- 阶段:**Phase 0 · 地基(先确认原型,尚未开始生产编码)** +- 阶段:**Phase 0 · 地基(采购服务骨架完成,采购工具骨架进行中)** - MVP 形态:手工填链接建单 → 批量开始试选 → 定时轮询 → **第一趟试选** → 人工确认 → **第二趟下单** → 待付款 - 技术栈:已定。采购服务(`admin/`)使用 Go 1.23+ / gin / SQLite;采购工具(`client/`) 使用 Python 3.11+ / uiautomator2 / PySide6。 详见 [`03-tech-stack.md`](03-tech-stack.md) -- 生产代码:**无**。仓库目前只有文档 -- 测试:**无** +- 生产代码:`admin/` 已有最小 Go 服务、`GET /healthz` 与 SQLite 驱动封装;尚无采购业务代码 +- 测试:采购服务 2 项离线单元测试 - 数据:**无** -- 标准启动路径:`./init.ps1`(Windows)/ `./init.sh`。已存在,以 `admin/go.mod` 和 - `client/requirements.txt` 判断两端是否初始化;当前会以退出码 3 结束并列出 T-001 / T-002 - ——这是预期行为 -- 标准验证路径:目前只有 `python scripts/validate_agent_context.py` 可跑通 -- 当前 blocker:无外部 blocker。T-001 / T-002 尚未落成任务文件和初始化代码;两者是 - 下一波可并行任务。T-002 完成后立即进入 T-101,不等待整个 Phase 0 收尾。 +- 标准启动路径:`./init.ps1`(Windows)/ `./init.sh`。当前会验证采购服务,再因 + `client/requirements.txt` 尚不存在以退出码 3 提示 T-002——这是预期行为 +- 标准验证路径:`admin/` 下运行 `go test ./...`、`go vet ./...`、`go build ./...`;仓库根运行 + `python scripts/validate_agent_context.py` +- 当前 blocker:无外部 blocker。T-002 正在实现;T-004 已因 T-001 完成而可领取。 ## 当前目录要点 | 路径 | 状态 | 说明 | | --- | --- | --- | | `docs/` | 已有 | 项目规范化文档,本次已完整生成 | -| `docs/tasks/` | 已有(T-005~T-009) | T-005~T-008 已完成;T-009 记录关键路径与并行波次 | +| `docs/tasks/` | 已有(T-001、T-002、T-005~T-009) | T-001 已完成;T-002 正在进行;其余既有任务已完成 | | `docs/design/` | 已有(6 个原型) | web 登录 / 建单 / 工作台 / 详情,desk 采购执行 / 配置;均已人工确认 | | `scripts/` | 已有 | 上下文门禁、Vikunja 单向导出与 MCP 启动包装 | -| `admin/` | **待建** | “采购服务”Go 后端与管理页面(T-001) | +| `admin/` | 已初始化 | Go 1.23+ / gin / SQLite 骨架与健康检查;无采购业务路由 | | `client/` | **待建** | “采购工具”Python 桌面端(T-002) | | `init.ps1` / `init.sh` | 已有(骨架) | 统一入口。两端目录建好后由 T-003 补全并验证 | @@ -44,29 +43,37 @@ 任务状态以 `docs/tasks/` 各任务文件 frontmatter 的 `status` 为准。本节只写项目级摘要: - 已完成:T-005(采购服务交互原型)、T-006(采购工具交互原型)、T-007(产品名称与 - 源码目录契约)、T-008(Vikunja 任务权威与单向导出)、T-009(MVP 关键路径与并行波次)。 -- **下一步:并行落成并启动 T-001 与 T-002。** T-001 完成后可推进 T-004;T-002 完成后 - 立即推进 T-101 → T-102 → T-103;两端骨架都完成后可并行补 T-003。 + 源码目录契约)、T-008(Vikunja 任务权威与单向导出)、T-009(MVP 关键路径与并行波次), + 以及 T-001(采购服务 Go 骨架)。 +- 正在进行:T-002(采购工具 Python 骨架)。采购服务方向可并行落成 T-004;T-002 完成后 + 立即推进 T-101 → T-102 → T-103,并可补 T-003 统一入口。 - T-103 是当前最高优先级和 MVP 生死线。通过前不开发依赖真机可读字段的 Phase 2 生产页面。 - 已确认原型继续只作信息架构依据;原型假数据不调用真实接口、不驱动真机。真机结论改变 可读字段时必须先回修原型与交互清单。 ## 当前可运行内容 -**目前没有可运行的代码。** 唯一可运行的检查: +采购服务当前可运行: ```bash -# 校验 agent 上下文清单 +cd admin +go mod download +go run ./cmd/server +``` + +采购服务验证与仓库级上下文门禁: + +```bash +cd admin +go test ./... +go vet ./... +go build ./... + +cd .. python scripts/validate_agent_context.py ``` -T-001 / T-002 完成后,本节替换为真实命令: - -```bash -# 采购服务 admin/(T-001 后填写) -# 采购工具 client/(T-002 后填写) -# 统一入口(T-003 后填写) -``` +采购工具与统一入口的真实命令分别等待 T-002、T-003 完成后补齐。 ## 关键背景 diff --git a/docs/tasks/T-001.md b/docs/tasks/T-001.md index 4af2da0..4edb064 100644 --- a/docs/tasks/T-001.md +++ b/docs/tasks/T-001.md @@ -3,7 +3,7 @@ id: T-001 title: 初始化采购服务 admin Go 骨架 phase: 0 deps: [] -status: DOING +status: DONE created: 2026-08-03 vikunja_task_id: 18 context_ref: a63bdb8 @@ -18,7 +18,7 @@ write_paths: - docs/current-state.md --- - + ## 问题 / 背景 仓库尚无 admin/go.mod 和可运行的采购服务代码,init.ps1 因此按预期以退出码 3 停止。没有稳定的 Go 骨架、健康检查和真实验证命令,后续数据模型与服务端任务无法开始。 @@ -47,7 +47,9 @@ write_paths: ## 执行记录 -(暂无) +### 2026-08-03T10:11:42Z · ila + +T-001 实施与主审完成(2026-08-03):新增 admin Go 1.23 模块、gin 路由、GET /healthz、mattn/go-sqlite3 封装及 2 项离线测试;未实现业务路由、真机判据、下单或付款。主 agent 逐文件审查并独立运行:go mod tidy;gofmt -d .(无差异);go test -count=1 -race ./...(2 项通过);go vet ./...;go build ./...;python scripts/validate_agent_context.py;init.ps1(按 T-002 未完成预期退出 3)。已更新 admin 的真实运行/验证文档。风险:go-sqlite3 依赖 CGO;init.ps1 的“仓库只有文档”旧提示留给 T-003 修正。 ## 边界