chore(tools): add bypos-collector (条码批量采集器源码备份)
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
//go:embed web/*
|
||||
var webFS embed.FS
|
||||
|
||||
var collector = NewCollector("")
|
||||
|
||||
func main() {
|
||||
addr := flag.String("addr", "127.0.0.1:8765", "本地监听地址")
|
||||
noOpen := flag.Bool("no-open", false, "不自动打开浏览器")
|
||||
sdog := flag.String("sdogid", "", "中心库账号 id(默认使用内置值)")
|
||||
flag.Parse()
|
||||
if *sdog != "" {
|
||||
collector.sdogID = *sdog
|
||||
}
|
||||
|
||||
sub, _ := fs.Sub(webFS, "web")
|
||||
mux := http.NewServeMux()
|
||||
mux.Handle("/", http.FileServer(http.FS(sub)))
|
||||
mux.HandleFunc("/api/start", handleStart)
|
||||
mux.HandleFunc("/api/stop", handleStop)
|
||||
mux.HandleFunc("/api/stats", handleStats)
|
||||
mux.HandleFunc("/api/download", handleDownload)
|
||||
mux.HandleFunc("/api/export.csv", handleExportCSV)
|
||||
|
||||
ln, err := net.Listen("tcp", *addr)
|
||||
if err != nil {
|
||||
log.Fatalf("无法监听 %s: %v", *addr, err)
|
||||
}
|
||||
realAddr := ln.Addr().String()
|
||||
urlStr := "http://" + realAddr + "/"
|
||||
fmt.Println("==============================================")
|
||||
fmt.Println(" 中心库商品采集器 bypos-collector")
|
||||
fmt.Println(" 控制台: " + urlStr)
|
||||
fmt.Println(" 关闭本窗口即停止程序")
|
||||
fmt.Println("==============================================")
|
||||
if !*noOpen {
|
||||
go openBrowser(urlStr)
|
||||
}
|
||||
log.Fatal(http.Serve(ln, mux))
|
||||
}
|
||||
|
||||
func openBrowser(url string) {
|
||||
time.Sleep(600 * time.Millisecond)
|
||||
var cmd string
|
||||
var args []string
|
||||
switch runtime.GOOS {
|
||||
case "windows":
|
||||
cmd = "rundll32"
|
||||
args = []string{"url.dll,FileProtocolHandler", url}
|
||||
case "darwin":
|
||||
cmd = "open"
|
||||
args = []string{url}
|
||||
default:
|
||||
cmd = "xdg-open"
|
||||
args = []string{url}
|
||||
}
|
||||
_ = exec.Command(cmd, args...).Start()
|
||||
}
|
||||
|
||||
func writeJSON(w http.ResponseWriter, code int, v interface{}) {
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(code)
|
||||
json.NewEncoder(w).Encode(v)
|
||||
}
|
||||
|
||||
func handleStart(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
http.Error(w, "method", 405)
|
||||
return
|
||||
}
|
||||
var req JobReq
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
writeJSON(w, 400, map[string]string{"error": "请求格式错误"})
|
||||
return
|
||||
}
|
||||
if err := collector.Start(req); err != nil {
|
||||
writeJSON(w, 400, map[string]string{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
writeJSON(w, 200, map[string]string{"ok": "started"})
|
||||
}
|
||||
|
||||
func handleStop(w http.ResponseWriter, r *http.Request) {
|
||||
collector.Stop()
|
||||
writeJSON(w, 200, map[string]string{"ok": "stopping"})
|
||||
}
|
||||
|
||||
func handleStats(w http.ResponseWriter, r *http.Request) {
|
||||
writeJSON(w, 200, map[string]interface{}{
|
||||
"stats": collector.snapshot(),
|
||||
"recent": collector.recentResults(),
|
||||
})
|
||||
}
|
||||
|
||||
func handleDownload(w http.ResponseWriter, r *http.Request) {
|
||||
s := collector.snapshot()
|
||||
if s.OutFile == "" {
|
||||
http.Error(w, "no output yet", 404)
|
||||
return
|
||||
}
|
||||
f, err := os.Open(s.OutFile)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 404)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
w.Header().Set("Content-Type", "application/x-ndjson; charset=utf-8")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=products.jsonl")
|
||||
io.Copy(w, f)
|
||||
}
|
||||
|
||||
func handleExportCSV(w http.ResponseWriter, r *http.Request) {
|
||||
s := collector.snapshot()
|
||||
if s.OutFile == "" {
|
||||
http.Error(w, "no output yet", 404)
|
||||
return
|
||||
}
|
||||
f, err := os.Open(s.OutFile)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), 404)
|
||||
return
|
||||
}
|
||||
defer f.Close()
|
||||
w.Header().Set("Content-Type", "text/csv; charset=utf-8")
|
||||
w.Header().Set("Content-Disposition", "attachment; filename=products.csv")
|
||||
w.Write([]byte{0xEF, 0xBB, 0xBF}) // UTF-8 BOM so Excel reads Chinese correctly
|
||||
cw := csv.NewWriter(w)
|
||||
cw.Write([]string{"barcode", "name", "spec", "unit", "area", "manufacturer", "license", "in_price", "sell_price", "status", "fetched_at"})
|
||||
dec := json.NewDecoder(f)
|
||||
for {
|
||||
var p Product
|
||||
if err := dec.Decode(&p); err != nil {
|
||||
break
|
||||
}
|
||||
cw.Write([]string{p.Barcode, p.Name, p.Spec, p.Unit, p.Area, p.Manufacturer, p.License, p.InPrice, p.SellPrice, p.Status, p.FetchedAt})
|
||||
}
|
||||
cw.Flush()
|
||||
}
|
||||
Reference in New Issue
Block a user