chore: optimize SEO and fix golangci-lint issues

- Implement dynamic SEO meta tags and sitemap.xml for toolbox.pengzhan.dev
- Fix SPA title/meta refresh issue in layout.html
- Address golangci-lint findings:
  - Refactor switch-case for tool path and SVG token parsing
  - Replace math.Pow with direct multiplication for performance
  - Fix unhandled error returns and unused imports
  - Omit redundant type declarations
This commit is contained in:
2026-03-01 09:54:04 -08:00
parent c3d0dee806
commit 5c185da0a9
9 changed files with 657 additions and 229 deletions
+75 -32
View File
@@ -17,7 +17,7 @@ type WritingRequest struct {
}
type WritingNode struct {
X, Y, R float64
X, Y, R float64
AngleA, AngleB float64
BulgeR, Dist float64
Number int
@@ -26,11 +26,15 @@ type WritingNode struct {
func GenerateWritingPDF(req WritingRequest) ([]byte, error) {
pdf := &gopdf.GoPdf{}
rect := gopdf.Rect{W: 595.28, H: 841.89}
if req.PaperSize == "Letter" { rect = gopdf.Rect{W: 612, H: 792} }
if req.PaperSize == "Letter" {
rect = gopdf.Rect{W: 612, H: 792}
}
pdf.Start(gopdf.Config{PageSize: rect})
err := pdf.AddTTFFontData("basic", data.FontContent)
if err != nil { return nil, err }
if err != nil {
return nil, err
}
currentNum := req.StartNum
// 只要还没达到 EndNum,就继续生成“整页”内容
@@ -54,7 +58,7 @@ func drawOneFullPage(pdf *gopdf.GoPdf, start int, pW, pH float64) int {
nodeR := 28.0
bulgeR := nodeR * 0.32
dist := nodeR + bulgeR + 18.0
checkR := nodeR * 1.3
checkR := nodeR * 1.3
var nodes []WritingNode
num := start
@@ -63,11 +67,13 @@ func drawOneFullPage(pdf *gopdf.GoPdf, start int, pW, pH float64) int {
for {
placed := false
for retry := 0; retry < 1000; retry++ {
rx := xBase + dist + 20 + rand.Float64()*(boxW - 2*dist - 40)
ry := yBase + dist + 20 + rand.Float64()*(boxH - 2*dist - 40)
if isColliding(rx, ry, checkR, nodes) { continue }
rx := xBase + dist + 20 + rand.Float64()*(boxW-2*dist-40)
ry := yBase + dist + 20 + rand.Float64()*(boxH-2*dist-40)
if isColliding(rx, ry, checkR, nodes) {
continue
}
angleA := rand.Float64() * 2 * math.Pi
angleB := angleA + math.Pi + (rand.Float64()-0.5)*1.5
nodes = append(nodes, WritingNode{X: rx, Y: ry, R: nodeR, AngleA: angleA, AngleB: angleB, BulgeR: bulgeR, Dist: dist, Number: num})
@@ -98,7 +104,9 @@ func drawOneFullPage(pdf *gopdf.GoPdf, start int, pW, pH float64) int {
}
func optimizeSpace(nodes []WritingNode, xBase, yBase, boxW, boxH, dist, checkR float64) {
if len(nodes) < 2 { return }
if len(nodes) < 2 {
return
}
for iter := 0; iter < 120; iter++ {
for i := range nodes {
bestX, bestY := nodes[i].X, nodes[i].Y
@@ -107,11 +115,16 @@ func optimizeSpace(nodes []WritingNode, xBase, yBase, boxW, boxH, dist, checkR f
moveAngle := rand.Float64() * 2 * math.Pi
nx := nodes[i].X + math.Cos(moveAngle)*5.0
ny := nodes[i].Y + math.Sin(moveAngle)*5.0
if nx-dist < xBase || nx+dist > xBase+boxW || ny-dist < yBase || ny+dist > yBase+boxH { continue }
if isColliding(nx, ny, checkR, nodes[:i]) || isColliding(nx, ny, checkR, nodes[i+1:]) { continue }
if nx-dist < xBase || nx+dist > xBase+boxW || ny-dist < yBase || ny+dist > yBase+boxH {
continue
}
if isColliding(nx, ny, checkR, nodes[:i]) || isColliding(nx, ny, checkR, nodes[i+1:]) {
continue
}
newMinDist := calcMinDist(nx, ny, i, nodes)
if newMinDist > maxMinDist {
maxMinDist = newMinDist; bestX, bestY = nx, ny
maxMinDist = newMinDist
bestX, bestY = nx, ny
}
}
nodes[i].X, nodes[i].Y = bestX, bestY
@@ -121,8 +134,12 @@ func optimizeSpace(nodes []WritingNode, xBase, yBase, boxW, boxH, dist, checkR f
func isColliding(x, y, r float64, existing []WritingNode) bool {
for _, e := range existing {
d := math.Sqrt(math.Pow(x-e.X, 2) + math.Pow(y-e.Y, 2))
if d < (r + e.R*1.3 + 30.0) { return true }
dx := x - e.X
dy := y - e.Y
d := math.Sqrt(dx*dx + dy*dy)
if d < (r + e.R*1.3 + 30.0) {
return true
}
}
return false
}
@@ -130,50 +147,76 @@ func isColliding(x, y, r float64, existing []WritingNode) bool {
func calcMinDist(x, y float64, idx int, nodes []WritingNode) float64 {
minD := 10000.0
for i, n := range nodes {
if i == idx { continue }
d := math.Sqrt(math.Pow(x-n.X, 2) + math.Pow(y-n.Y, 2))
if d < minD { minD = d }
if i == idx {
continue
}
dx := x - n.X
dy := y - n.Y
d := math.Sqrt(dx*dx + dy*dy)
if d < minD {
minD = d
}
}
return minD
}
func getEdgePoint(x, y, r, angle float64, isCircle bool) (float64, float64) {
gap := 2.0
gap := 2.0
er := r + gap
if isCircle { return x + math.Cos(angle)*er, y + math.Sin(angle)*er }
if isCircle {
return x + math.Cos(angle)*er, y + math.Sin(angle)*er
}
absCos, absSin := math.Abs(math.Cos(angle)), math.Abs(math.Sin(angle))
var d float64
if absCos > absSin { d = er / absCos } else { d = er / absSin }
if absCos > absSin {
d = er / absCos
} else {
d = er / absSin
}
return x + math.Cos(angle)*d, y + math.Sin(angle)*d
}
func drawCenteredText(pdf *gopdf.GoPdf, cx, cy float64, text string, fontSize float64) {
pdf.SetFont("basic", "", fontSize)
_ = pdf.SetFont("basic", "", fontSize)
tw, _ := pdf.MeasureTextWidth(text)
pdf.SetXY(cx - tw/2, cy + fontSize*0.38)
pdf.Text(text)
pdf.SetXY(cx-tw/2, cy+fontSize*0.38)
_ = pdf.Text(text)
}
func drawFullNode(pdf *gopdf.GoPdf, n WritingNode) {
isOdd := n.Number%2 != 0
pdf.SetStrokeColor(0, 0, 0); pdf.SetLineWidth(1.8)
if isOdd { pdf.Oval(n.X-n.R, n.Y-n.R, n.X+n.R, n.Y+n.R) } else { pdf.RectFromUpperLeft(n.X-n.R, n.Y-n.R, n.R*2, n.R*2) }
pdf.SetStrokeColor(0, 0, 0)
pdf.SetLineWidth(1.8)
if isOdd {
pdf.Oval(n.X-n.R, n.Y-n.R, n.X+n.R, n.Y+n.R)
} else {
pdf.RectFromUpperLeft(n.X-n.R, n.Y-n.R, n.R*2, n.R*2)
}
pdf.SetTextColor(220, 220, 220)
drawCenteredText(pdf, n.X, n.Y, strconv.Itoa(n.Number), n.R*1.35)
pdf.SetStrokeColor(180, 180, 180); pdf.SetLineWidth(1.0)
ax, ay := n.X + math.Cos(n.AngleA)*n.Dist, n.Y + math.Sin(n.AngleA)*n.Dist
pdf.SetStrokeColor(180, 180, 180)
pdf.SetLineWidth(1.0)
ax, ay := n.X+math.Cos(n.AngleA)*n.Dist, n.Y+math.Sin(n.AngleA)*n.Dist
lx1, ly1 := getEdgePoint(n.X, n.Y, n.R, n.AngleA, isOdd)
lx2, ly2 := getEdgePoint(ax, ay, n.BulgeR, n.AngleA+math.Pi, !isOdd)
pdf.Line(lx1, ly1, lx2, ly2)
if isOdd { pdf.RectFromUpperLeft(ax-n.BulgeR, ay-n.BulgeR, n.BulgeR*2, n.BulgeR*2) } else { pdf.Oval(ax-n.BulgeR, ay-n.BulgeR, ax+n.BulgeR, ay+n.BulgeR) }
if isOdd {
pdf.RectFromUpperLeft(ax-n.BulgeR, ay-n.BulgeR, n.BulgeR*2, n.BulgeR*2)
} else {
pdf.Oval(ax-n.BulgeR, ay-n.BulgeR, ax+n.BulgeR, ay+n.BulgeR)
}
bx, by := n.X + math.Cos(n.AngleB)*n.Dist, n.Y + math.Sin(n.AngleB)*n.Dist
bx, by := n.X+math.Cos(n.AngleB)*n.Dist, n.Y+math.Sin(n.AngleB)*n.Dist
lx3, ly3 := getEdgePoint(n.X, n.Y, n.R, n.AngleB, isOdd)
lx4, ly4 := getEdgePoint(bx, by, n.BulgeR, n.AngleB+math.Pi, !isOdd)
pdf.Line(lx3, ly3, lx4, ly4)
if isOdd { pdf.RectFromUpperLeft(bx-n.BulgeR, by-n.BulgeR, n.BulgeR*2, n.BulgeR*2) } else { pdf.Oval(bx-n.BulgeR, by-n.BulgeR, bx+n.BulgeR, by+n.BulgeR) }
if isOdd {
pdf.RectFromUpperLeft(bx-n.BulgeR, by-n.BulgeR, n.BulgeR*2, n.BulgeR*2)
} else {
pdf.Oval(bx-n.BulgeR, by-n.BulgeR, bx+n.BulgeR, by+n.BulgeR)
}
pdf.SetTextColor(120, 120, 120)
drawCenteredText(pdf, bx, by, strconv.Itoa(n.Number+1), math.Max(7, n.BulgeR*1.2))
}