fix(service): populate Excel pricing cells and verify attachment readback parity

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
WeiCHEN
2026-09-13 02:50:18 +00:00
committed by weichen
co-authored by Cursor
parent 59cca0594e
commit c65f5e8a3d
2 changed files with 102 additions and 0 deletions
+71
View File
@@ -1,12 +1,17 @@
package bizdev_test
import (
"bytes"
"context"
"os"
"path/filepath"
"strconv"
"strings"
"testing"
"time"
"github.com/xuri/excelize/v2"
"chat-agent-system/internal/bizdev"
"chat-agent-system/internal/config"
"chat-agent-system/internal/quote"
@@ -78,4 +83,70 @@ func TestExecuteTaskIndependent(t *testing.T) {
if !strings.Contains(quoteRes.MarkdownBody, "微创吻合器研发") {
t.Errorf("markdown missing product_name")
}
xlsxPrice, err := readQuoteSheetTotal(quoteRes.Attachments[0].Data)
if err != nil {
t.Fatalf("read Excel J18 from attachment: %v", err)
}
if diff := xlsxPrice - summary.TotalPrice; diff < -0.01 || diff > 0.01 {
t.Errorf("Excel J18 total %v != summary total %v", xlsxPrice, summary.TotalPrice)
}
}
func TestExecuteTaskWritesMaterials(t *testing.T) {
tmpDir := t.TempDir()
workDir := filepath.Join(tmpDir, "workspace")
cfg := &config.Config{
DataDir: tmpDir,
AgentBackend: "stub",
AgentTimeout: 5 * time.Second,
}
svc := bizdev.NewService(cfg)
materialContent := []byte("客户提供的补充需求说明")
res, err := svc.ExecuteTask(context.Background(), bizdev.TaskRunRequest{
TaskKey: "intake",
ProjectName: "测试项目",
CustomerName: "测试客户",
WorkDir: workDir,
Materials: []bizdev.MaterialInput{
{Filename: "requirements.txt", Content: materialContent},
},
})
if err != nil {
t.Fatalf("execute intake with materials failed: %v", err)
}
materialPath := filepath.Join(res.WorkDir, "materials_text", "requirements.txt")
data, err := os.ReadFile(materialPath)
if err != nil {
t.Fatalf("expected material file at %s: %v", materialPath, err)
}
if !bytes.Equal(data, materialContent) {
t.Errorf("material content mismatch: got %q, want %q", data, materialContent)
}
}
func readQuoteSheetTotal(xlsxData []byte) (float64, error) {
f, err := excelize.OpenReader(bytes.NewReader(xlsxData))
if err != nil {
return 0, err
}
defer f.Close()
const sheet = "报价模型"
raw, err := f.GetCellValue(sheet, "J18")
if err != nil {
return 0, err
}
return parseExcelNumber(raw)
}
func parseExcelNumber(raw string) (float64, error) {
raw = strings.TrimSpace(raw)
if raw == "" {
return 0, nil
}
raw = strings.ReplaceAll(raw, ",", "")
return strconv.ParseFloat(raw, 64)
}
+31
View File
@@ -78,6 +78,28 @@ func CalculateAndFill(in Input, templateXlsx string, outPath string) (*Summary,
if idx, _ := f.GetSheetIndex(sheet); idx != -1 {
_ = f.SetCellValue(sheet, "B3", in.CustomerName)
_ = f.SetCellValue(sheet, "B4", in.ProductName)
_ = f.SetCellValue(sheet, "B5", totalPrice)
writeSummaryLine(f, sheet, 8, summary.Lines[0])
writeSummaryLine(f, sheet, 9, summary.Lines[1])
writeSummaryLine(f, sheet, 10, summary.Lines[2])
var totalPersonYear, totalCost, totalBuffer, totalMargin, totalTax float64
for _, line := range summary.Lines {
totalPersonYear += line.PersonYear
totalCost += line.Cost
totalBuffer += line.Buffer
totalMargin += line.Margin
totalTax += line.Tax
}
writeSummaryLine(f, sheet, 18, SummaryLine{
PersonYear: totalPersonYear,
Cost: totalCost,
Buffer: totalBuffer,
Margin: totalMargin,
Tax: totalTax,
Price: totalPrice,
})
}
if err := os.MkdirAll(filepath.Dir(outPath), 0o755); err != nil {
@@ -89,3 +111,12 @@ func CalculateAndFill(in Input, templateXlsx string, outPath string) (*Summary,
return summary, nil
}
func writeSummaryLine(f *excelize.File, sheet string, row int, line SummaryLine) {
_ = f.SetCellValue(sheet, fmt.Sprintf("D%d", row), line.PersonYear)
_ = f.SetCellValue(sheet, fmt.Sprintf("E%d", row), line.Cost)
_ = f.SetCellValue(sheet, fmt.Sprintf("F%d", row), line.Buffer)
_ = f.SetCellValue(sheet, fmt.Sprintf("G%d", row), line.Margin)
_ = f.SetCellValue(sheet, fmt.Sprintf("H%d", row), line.Tax)
_ = f.SetCellValue(sheet, fmt.Sprintf("J%d", row), line.Price)
}