Files
yml2213 c43f420875 支持客户、对话记录与统计报告 CSV 导出
- 新增 /customers/export、/sessions/export、/statistics/export
- 沿用列表筛选与角色可见范围,UTF-8 BOM 便于 Excel 打开
- 前端三处导出按钮接入真实下载
2026-07-15 15:10:46 +08:00

71 lines
1.3 KiB
Go

package handler
import (
"encoding/csv"
"fmt"
"net/http"
"net/url"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
)
const exportMaxRows = 5000
func csvEscape(s string) string {
return strings.ReplaceAll(s, "\r\n", " ")
}
func writeCSVResponse(c *gin.Context, filename string, header []string, rows [][]string) {
if !strings.HasSuffix(strings.ToLower(filename), ".csv") {
filename += ".csv"
}
// 兼容 Excel 中文:UTF-8 BOM
c.Header("Content-Type", "text/csv; charset=utf-8")
c.Header("Content-Disposition", fmt.Sprintf("attachment; filename*=UTF-8''%s", url.PathEscape(filename)))
c.Status(http.StatusOK)
if _, err := c.Writer.Write([]byte{0xEF, 0xBB, 0xBF}); err != nil {
return
}
w := csv.NewWriter(c.Writer)
_ = w.Write(header)
for _, row := range rows {
for i := range row {
row[i] = csvEscape(row[i])
}
_ = w.Write(row)
}
w.Flush()
}
func formatCSVTime(t time.Time) string {
if t.IsZero() {
return ""
}
return t.In(time.Local).Format("2006-01-02 15:04:05")
}
func formatCSVTimePtr(t *time.Time) string {
if t == nil {
return ""
}
return formatCSVTime(*t)
}
func formatUintPtr(v *uint) string {
if v == nil {
return ""
}
return strconv.FormatUint(uint64(*v), 10)
}
func formatIntPtr(v *int) string {
if v == nil {
return ""
}
return strconv.Itoa(*v)
}