Files
Qianandchengqian be9e707bfd 修复异步图片导致的多个问题 (#12)
* fix:修复响应污染问题

* fix:修改存储相关文件为中文注释

* fix:修复二次退款问题

* fix:修复多个考虑不全面问题
1.误删用户数据
2.R2存储默认区域
3.删除失败导致孤儿记录
4.网络抖动事变时重试
5.前端无限次请求获取图片
6.文件管理权限

* fix:修复字符串截断异常问题和文件下载超时问题

* fix:修复多图问题

* fix:异步生成图片时未配置oss提示错误信息

* fix:修复关闭异步后同步生成出错问题

* feat:新增文件存储测试功能,防止设置后不生效

* fix:修复同步图片转异步后计费错误

* fix:修复无下限扣款问题

* fix:修复结算重复扣款问题

* fix:修复计费出错导致的0费用结算

* fix:修复回调双重履约

* fix:修复预扣事务

* fix:修复费用字段类型问题

* fix:修复缓存问题

* fix:修复安全问题

* fix:修复数据库操作事务问题

* refactor:重构事务使用方式,改为官方推荐的dao形式

* fix:修复余额更新隐患

* fix:修复N+1查询问题

* fix:修复密钥安全问题

* fix:结算失败时打印错误日志

* improve:优化扣款

* fix:修复用户接口越权

---------

Co-authored-by: chengqian <wuchengqian@kkidc.com>
2026-07-22 17:53:18 +08:00

81 lines
2.4 KiB
Go

package export
import (
"fmt"
"net/url"
"strconv"
"strings"
"time"
"github.com/gogf/gf/v2/net/ghttp"
)
var (
csvContentType = "text/csv; charset=utf-8"
xlsxContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
)
// setDownloadHeaders sets HTTP headers for file download.
func setDownloadHeaders(r *ghttp.Request, filename, format string) {
contentType := csvContentType
ext := ".csv"
if format == "xlsx" {
contentType = xlsxContentType
ext = ".xlsx"
}
encodedName := url.PathEscape(filename + ext)
r.Response.Header().Set("Content-Type", contentType)
r.Response.Header().Set("Content-Disposition", fmt.Sprintf(`attachment; filename="%s"; filename*=UTF-8''%s`, filename+ext, encodedName))
r.Response.Header().Set("X-Content-Type-Options", "nosniff")
}
// defaultFilename generates a default filename with timestamp.
func defaultFilename(prefix string) string {
return fmt.Sprintf("%s_%s", prefix, time.Now().Format("20060102_150405"))
}
// detectFormat validates and normalizes the format string.
func detectFormat(format string) string {
f := strings.ToLower(strings.TrimSpace(format))
if f == "xlsx" || f == "excel" {
return "xlsx"
}
return "csv"
}
// GetCellValue extracts a value from a row map, applying the column's Transform if set.
func GetCellValue(row map[string]any, col Column) string {
val, ok := row[col.Field]
if !ok {
return ""
}
if col.Transform != nil {
return col.Transform(val)
}
if val == nil {
return ""
}
return fmt.Sprintf("%v", val)
}
// sanitizeCSVField 防御 CSV 公式注入(CWE-1236)。
// Excel/LibreOffice/Google Sheets 打开 CSV 时,会把以 = + - @ 及 TAB(0x09)/CR(0x0D) 开头的
// 单元格当作公式求值,用户可控字段(租户名/成员名/工单标题等)可借此注入 DDE/命令执行。
// 处理策略:对以危险字符开头且【整体不是合法数字】的值,前置单引号中和为纯文本;
// 合法数字(含负号/正号/科学计数法)保持原样,避免破坏数值列的可计算性。
// 仅用于 CSV 写入;XLSX 经 excelize 以字符串类型单元格存储,Excel 不会将其当公式求值,无需处理。
func sanitizeCSVField(s string) string {
if s == "" {
return s
}
switch s[0] {
case '=', '+', '-', '@', '\t', '\r':
if _, err := strconv.ParseFloat(strings.TrimSpace(s), 64); err == nil {
return s // 纯数字,Excel 不会当公式,安全
}
return "'" + s
}
return s
}