Compare commits

..
Author SHA1 Message Date
QiuSW c72371ce90 feat(admin): initialize procurement service skeleton 2026-08-03 18:13:30 +08:00
QiuSW b7559a9451 docs(tasks): define parallel T-001 and T-002 2026-08-03 17:57:53 +08:00
12 changed files with 402 additions and 34 deletions
+26
View File
@@ -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
}
+42
View File
@@ -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
)
+90
View File
@@ -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=
+19
View File
@@ -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
}
+28
View File
@@ -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)
}
}
+13
View File
@@ -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)
}
@@ -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)
}
}
+8 -7
View File
@@ -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
+3 -4
View File
@@ -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` |
+30 -23
View File
@@ -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 完成后补齐。
## 关键背景
+61
View File
@@ -0,0 +1,61 @@
---
id: T-001
title: 初始化采购服务 admin Go 骨架
phase: 0
deps: []
status: DONE
created: 2026-08-03
vikunja_task_id: 18
context_ref: a63bdb8
work_branch: task/t-001-admin-skeleton
needs_device: false
needs_human_review: false
write_paths:
- docs/tasks/T-001.md
- admin/**
- docs/03-tech-stack.md
- docs/00-ai-start-here.md
- docs/current-state.md
---
<!-- BEGIN VIKUNJA EXPORT id=18 synced=2026-08-03T10:13:08Z sha256=81e7657246ae88d8b8c8c9eeb370989c11a524a7ed2e7bd25d647dc77efd747e -->
## 问题 / 背景
仓库尚无 admin/go.mod 和可运行的采购服务代码,init.ps1 因此按预期以退出码 3 停止。没有稳定的 Go 骨架、健康检查和真实验证命令,后续数据模型与服务端任务无法开始。
## 关联需求与交互
- 功能:Phase 0 工程地基,不新增业务功能。
- 用户故事 / 交互:不适用,不实现生产页面。
- 架构 / API:遵循 docs/03-tech-stack.md;只提供健康检查,不新增采购业务接口。
## 方案
1. 在 admin/ 初始化 Go 1.23+ 模块,采用既定 gin / SQLite 技术栈和可测试的应用入口。
2. 提供健康检查端点及对应单元测试;启动时错误必须显式返回,不吞错。
3. 建立后续可扩展但保持最小的目录结构,不提前实现登录、任务、授权、提交订单或支付逻辑。
4. 运行 go test ./... 与 go vet ./...,并记录真实命令。
5. T-001 作为本轮共享文档所有者,只更新 admin 相关的 docs/03-tech-stack.md、docs/00-ai-start-here.md、docs/current-state.md 段落;不得代写尚未验证的 client 命令。
## 验收要点
- admin/go.mod 存在,Go 模块与依赖可解析。
- 健康检查端点可通过 httptest 验证状态码和稳定响应。
- go test ./...、go vet ./... 在 admin/ 通过。
- 共享文档中的 admin 占位命令替换为经实际运行的命令,client 未完成部分仍明确标为未初始化。
- 不包含采购业务、真机页面判据、下单或付款代码。
## 执行记录
### 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 修正。
<!-- END VIKUNJA EXPORT -->
## 边界
- 不实现管理员登录、任务 CRUD、授权、围栏、真机自动化或任何 Phase 1+ 功能。
- 不新增或修改采购业务 API;健康检查不承载业务数据。
- 不编写任何下单、提交订单、付款或支付相关代码。
- 不修改 `client/`、`docs/api.md`、`docs/04-architecture.md`、需求或交互原型。
- 共享文档只写已经实际运行验证的 admin 命令,不代写尚未完成的 client 命令。
+58
View File
@@ -0,0 +1,58 @@
---
id: T-002
title: 初始化采购工具 client Python 骨架
phase: 0
deps: []
status: DOING
created: 2026-08-03
vikunja_task_id: 17
context_ref: a63bdb8
work_branch: task/t-002-client-skeleton
needs_device: false
needs_human_review: false
write_paths:
- docs/tasks/T-002.md
- client/**
---
<!-- BEGIN VIKUNJA EXPORT id=17 synced=2026-08-03T09:57:46Z sha256=14061aa9aca2b854afd2b657c051f593e29272ed67327d0c840d499708f8c821 -->
## 问题 / 背景
仓库尚无 client/requirements.txt 和可运行的采购工具代码,init.ps1 因此按预期以退出码 3 停止。没有稳定的 Python 包、测试入口、日志与产物策略,后续 ADB/uiautomator2 真机取证无法开始。
## 关联需求与交互
- 功能:Phase 0 工程地基,不实现采购执行流程。
- 用户故事 / 交互:不适用,本任务只建立应用骨架,不实现已确认原型的生产界面。
- 架构 / API:遵循 docs/03-tech-stack.md 与 TaskSource / ResultSink 边界;不新增跨端接口。
## 方案
1. 在 client/ 建立 Python 3.11+ 包、requirements.txt、应用入口和最小测试结构。
2. 声明既定 PySide6 / uiautomator2 依赖,但基础单元测试不得要求连接真机。
3. 建立日志和运行产物目录策略;本地虚拟环境与运行产物必须被 client 范围内的忽略规则排除,日志不得记录 token、地址、手机号或支付信息。
4. 保证 python -m unittest discover -s tests -t . 与 python -m compileall -q src tests 可运行。
5. T-001 关闭前不修改共享文档。先在 Vikunja 评论记录已验证的 client 命令;T-001 完成后由任务所有者先提交 write_paths 扩展,再更新共享文档并关闭 T-002。
## 验收要点
- client/requirements.txt、src 包、应用入口和 tests 存在。
- unittest 与 compileall 在 client/ 通过,且不需要真机。
- 日志 / 产物路径和脱敏边界有测试或可验证配置。
- 不包含拼多多页面判据、真机自动化实现、下单或付款代码。
- 关闭 T-002 前,共享文档中的 client 占位命令已由扩展后的独占 write_paths 更新为实际命令。
## 执行记录
(暂无)
<!-- END VIKUNJA EXPORT -->
## 边界
- 不实现拼多多页面判据、ADB/uiautomator2 真机流程、采购执行 UI 或任何 Phase 1+ 功能。
- 不编写任何进入订单确认、提交订单、付款或支付相关代码。
- 不修改 `admin/`、`docs/api.md`、`docs/04-architecture.md`、需求或交互原型。
- T-001 为 `DOING` 时不修改共享文档;先在 Vikunja 记录验证命令。T-001 关闭后,必须先提交
`write_paths` 扩展,再更新 `docs/03-tech-stack.md`、`docs/00-ai-start-here.md`、
`docs/current-state.md`,不得绕过路径门禁。
- 本地虚拟环境、缓存、日志和运行产物不得提交 Git;凭据和敏感信息不得进入日志。