|
@ -6,9 +6,8 @@ import ( |
|
|
"link_homework/api/v1/record" |
|
|
"link_homework/api/v1/record" |
|
|
"link_homework/internal/model/dto" |
|
|
"link_homework/internal/model/dto" |
|
|
"link_homework/internal/service" |
|
|
"link_homework/internal/service" |
|
|
"log" |
|
|
|
|
|
"net/http" |
|
|
|
|
|
"strconv" |
|
|
"strconv" |
|
|
|
|
|
"time" |
|
|
) |
|
|
) |
|
|
|
|
|
|
|
|
type ManageRecord struct{} |
|
|
type ManageRecord struct{} |
|
@ -102,168 +101,74 @@ func (m *ManageRecord) GetShopInfo(r *ghttp.Request) { |
|
|
}) |
|
|
}) |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
func (m *ManageRecord) ExeclExport(r *ghttp.Request) { |
|
|
|
|
|
//获取前端传来的参数
|
|
|
|
|
|
|
|
|
// ExportRecordByCondition 导出记录
|
|
|
|
|
|
func (m *ManageRecord) ExportRecordByCondition(r *ghttp.Request) { |
|
|
var req record.GetRecordByConditionReq |
|
|
var req record.GetRecordByConditionReq |
|
|
|
|
|
// 解析查询条件
|
|
|
if err := r.Parse(&req); err != nil { |
|
|
if err := r.Parse(&req); err != nil { |
|
|
r.Response.WriteJsonExit(dto.Result{ |
|
|
|
|
|
Code: 400, |
|
|
|
|
|
Message: err.Error(), |
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
|
|
|
//获取数据
|
|
|
|
|
|
data, err := service.Record().GetRecordByCondition(r.Context(), req.Id, req.Jwcode, req.DeptId, req.ShopId, req.PageNo, req.PageSize) |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
r.Response.WriteJsonExit(dto.Result{ |
|
|
|
|
|
Code: 400, |
|
|
|
|
|
Message: err.Error(), |
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
r.Response.WriteJsonExit(dto.Error("参数解析失败: " + err.Error())) |
|
|
|
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
//创建文件
|
|
|
|
|
|
f := excelize.NewFile() |
|
|
|
|
|
sheet := "Sheet1" |
|
|
|
|
|
f.NewSheet(sheet) |
|
|
|
|
|
|
|
|
|
|
|
////创建表头
|
|
|
|
|
|
//var header []string
|
|
|
|
|
|
////for _, field :=range reflect.Type(record.GetRecordListRes{}){
|
|
|
|
|
|
//// header = append(header, field.Name)
|
|
|
|
|
|
////}
|
|
|
|
|
|
//for i := 0; i < reflect.TypeOf(record.GetRecordListRes{}).NumField(); i++ {
|
|
|
|
|
|
// header = append(header, reflect.TypeOf(record.GetRecordListRes{}).Field(i).Name)
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
headers := []string{"精网号", "用户名字", "部门名", "门店名", "答案详情", "提交时间"} |
|
|
|
|
|
headerStyle, err := f.NewStyle(`{"font":{"bold":true},"fill":{"type":"solid","color":"#4F81BD"}}`) |
|
|
|
|
|
|
|
|
// 调用查询服务
|
|
|
|
|
|
records, err := service.Record().GetRecordByCondition( |
|
|
|
|
|
r.Context(), |
|
|
|
|
|
req.Id, req.Jwcode, req.DeptId, req.ShopId, req.PageNo, req.PageSize, |
|
|
|
|
|
) |
|
|
if err != nil { |
|
|
if err != nil { |
|
|
r.Response.WriteJsonExit(dto.Result{ |
|
|
|
|
|
Code: 400, |
|
|
|
|
|
Message: err.Error(), |
|
|
|
|
|
}) |
|
|
|
|
|
} |
|
|
|
|
|
for i, header := range headers { |
|
|
|
|
|
cellName := getExcelColumnName(i) + "1" |
|
|
|
|
|
f.SetCellValue(sheet, cellName, header) |
|
|
|
|
|
f.SetCellStyle(sheet, cellName, cellName, headerStyle) |
|
|
|
|
|
|
|
|
//g.Log().Error(r.Context(), "查询记录失败: ", err)
|
|
|
|
|
|
r.Response.WriteJsonExit(dto.Error("查询记录失败: " + err.Error())) |
|
|
|
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
//填充数据
|
|
|
|
|
|
for rowIndex, row := range data { |
|
|
|
|
|
// 假设 data 是一个 []GetRecordListRes 类型的切片
|
|
|
|
|
|
rowData := row //.(record.GetRecordListRes) // 类型断言(这里需要确保 data 的类型是正确的)
|
|
|
|
|
|
// 或者使用反射,但更推荐使用标签和直接映射
|
|
|
|
|
|
f.SetCellValue(sheet, getExcelColumnName(0)+strconv.Itoa(rowIndex+2), rowData.Jwcode) |
|
|
|
|
|
f.SetCellValue(sheet, getExcelColumnName(1)+strconv.Itoa(rowIndex+2), rowData.Name) |
|
|
|
|
|
f.SetCellValue(sheet, getExcelColumnName(2)+strconv.Itoa(rowIndex+2), rowData.DeptName) |
|
|
|
|
|
f.SetCellValue(sheet, getExcelColumnName(3)+strconv.Itoa(rowIndex+2), rowData.ShopName) |
|
|
|
|
|
f.SetCellValue(sheet, getExcelColumnName(4)+strconv.Itoa(rowIndex+2), rowData.Reply) |
|
|
|
|
|
f.SetCellValue(sheet, getExcelColumnName(5)+strconv.Itoa(rowIndex+2), rowData.Reply[0].UpdatedAt.Format("2006-01-02 15:04:05")) |
|
|
|
|
|
|
|
|
if len(records) == 0 { |
|
|
|
|
|
//g.Log().Warning(r.Context(), "查询结果为空: 参数 = ", req)
|
|
|
|
|
|
r.Response.WriteJsonExit(dto.Error("查询结果为空")) |
|
|
|
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
// 创建 Excel 文件
|
|
|
|
|
|
excelFile := excelize.NewFile() // 默认会创建一个 Sheet1
|
|
|
|
|
|
sheetName := "Sheet1" // 使用默认的 Sheet1
|
|
|
|
|
|
|
|
|
////定义表头样式,font:定义字体样式,如字体类型、字体大小、字体颜色等,Arial:字体家族为Arial.
|
|
|
|
|
|
////fill:定义填充样式,如背景色
|
|
|
|
|
|
//const (
|
|
|
|
|
|
// headerStyleJSON = `{
|
|
|
|
|
|
// "font": {
|
|
|
|
|
|
// "bold": true,
|
|
|
|
|
|
// "italic": false,
|
|
|
|
|
|
// "family": "Arial",
|
|
|
|
|
|
// "size": 14,
|
|
|
|
|
|
// "color": "#FFFFFF" //更换成你想要的字体font
|
|
|
|
|
|
// },
|
|
|
|
|
|
// "fill": {
|
|
|
|
|
|
// "type": "solid",
|
|
|
|
|
|
// "color": "#4F81BD" // 更换为你想要的背景色
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }`
|
|
|
|
|
|
//)
|
|
|
|
|
|
|
|
|
|
|
|
////填充表头
|
|
|
|
|
|
//headerStyle, err := f.NewStyle(headerStyleJSON)
|
|
|
|
|
|
//if err != nil {
|
|
|
|
|
|
// r.Response.WriteJsonExit(dto.Result{
|
|
|
|
|
|
// Code: 400,
|
|
|
|
|
|
// Message: err.Error(),
|
|
|
|
|
|
// })
|
|
|
|
|
|
//}
|
|
|
|
|
|
//
|
|
|
|
|
|
//for i, v := range header {
|
|
|
|
|
|
// cell := fmt.Sprintf("%s%d", getExcelColumnName(i), 1)
|
|
|
|
|
|
// f.SetCellValue(sheet, cell, v)
|
|
|
|
|
|
// f.SetCellStyle(sheet, cell, cell, headerStyle)
|
|
|
|
|
|
//}
|
|
|
|
|
|
//
|
|
|
|
|
|
////填充数据
|
|
|
|
|
|
//for i, row := range data {
|
|
|
|
|
|
// for j, col := range header {
|
|
|
|
|
|
// cell := fmt.Sprintf("%s%d", getExcelColumnName(j), i+2)
|
|
|
|
|
|
// //f.SetCellValue(sheet, cell, row[col])
|
|
|
|
|
|
// f.SetCellValue(sheet, cell, row.FieldByName(col).Interface())
|
|
|
|
|
|
// }
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
//设置默认打开的sheet
|
|
|
|
|
|
f.SetActiveSheet(f.GetSheetIndex(sheet)) |
|
|
|
|
|
|
|
|
|
|
|
// 将 Excel 文件作为 HTTP 响应发送给客户端
|
|
|
|
|
|
r.Response.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") |
|
|
|
|
|
r.Response.Header().Set("Content-Disposition", "attachment; filename=data.xlsx") |
|
|
|
|
|
r.Response.WriteHeader(http.StatusOK) |
|
|
|
|
|
if _, err := f.WriteTo(r.Response.Writer); err != nil { //这里有问题,说左右值数量不同,我加了个_
|
|
|
|
|
|
// 处理错误(注意:这里可能无法再发送 JSON 响应,因为响应头已经发送)
|
|
|
|
|
|
log.Printf("Error writing Excel file to response: %v", err) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
////保存文件
|
|
|
|
|
|
//if err := f.SaveAs("data.xlsx"); err != nil {
|
|
|
|
|
|
// r.Response.WriteJsonExit(dto.Result{
|
|
|
|
|
|
// Code: 400,
|
|
|
|
|
|
// Message: err.Error(),
|
|
|
|
|
|
// })
|
|
|
|
|
|
//}
|
|
|
|
|
|
//
|
|
|
|
|
|
//r.Response.WriteJsonExit(dto.Result{
|
|
|
|
|
|
// Code: 200,
|
|
|
|
|
|
// Message: "success",
|
|
|
|
|
|
// Data: "data.xlsx",
|
|
|
|
|
|
//})
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
//func getExcelColumnName(index int) string {
|
|
|
|
|
|
// var result string
|
|
|
|
|
|
// for index > 0 {
|
|
|
|
|
|
// index--
|
|
|
|
|
|
// result = string('A'+index%26) + result
|
|
|
|
|
|
// index /= 26
|
|
|
|
|
|
// }
|
|
|
|
|
|
// return result
|
|
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
// getExcelColumnName 将整数索引转换为 Excel 列名
|
|
|
|
|
|
func getExcelColumnName(index int) string { |
|
|
|
|
|
if index <= 0 { |
|
|
|
|
|
return "" |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
const letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
|
|
|
|
|
var columnName string |
|
|
|
|
|
|
|
|
|
|
|
for index > 0 { |
|
|
|
|
|
// 取余数得到当前位的字母
|
|
|
|
|
|
remainder := index % 26 |
|
|
|
|
|
// 将字母添加到结果字符串的前面
|
|
|
|
|
|
columnName = string(letters[remainder-1]) + columnName |
|
|
|
|
|
// 去掉已经处理过的最低位(因为 Excel 列名是从 1 开始计数的,所以这里要减 1)
|
|
|
|
|
|
// 但由于我们是从后往前构建字符串,所以实际上是在下一次循环中处理 index/26 的结果
|
|
|
|
|
|
index = (index - remainder) / 26 |
|
|
|
|
|
// 由于上面的计算中 index-remainder 可能是 26 的倍数(当 remainder 为 0 时),这样会导致 index 多减 1,
|
|
|
|
|
|
// 所以当 remainder 为 0 时,我们需要将 index 加回 1(或者等价地,不执行 index-- 操作)。
|
|
|
|
|
|
// 但由于我们在循环中每次都会执行 index = (index - remainder) / 26,并且当 remainder 为 0 时,
|
|
|
|
|
|
// (index - 0) / 26 实际上就是 index / 26,这是正确的。因此,我们不需要在这里对 remainder 为 0 的情况进行特殊处理。
|
|
|
|
|
|
// 这里的注释主要是为了解释为什么上面的计算是正确的,以及为什么不需要对 remainder 为 0 的情况进行额外处理。
|
|
|
|
|
|
|
|
|
// 设置表头
|
|
|
|
|
|
headers := []string{"序号", "精网号", "名字", "部门ID", "部门名称", "门店ID", "门店名称", "题目ID", "题目类型", "题目名称", "简答题标题", "作答内容", "提交时间"} |
|
|
|
|
|
for i, header := range headers { |
|
|
|
|
|
col := string('A' + i) // 将索引转换为 Excel 列名
|
|
|
|
|
|
excelFile.SetCellValue(sheetName, col+"1", header) |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 写入数据
|
|
|
|
|
|
rowIndex := 2 // 从第二行开始写入数据
|
|
|
|
|
|
for _, record := range records { |
|
|
|
|
|
for _, reply := range record.Reply { // 遍历每个 `Reply`
|
|
|
|
|
|
excelFile.SetCellValue(sheetName, "A"+strconv.Itoa(rowIndex), rowIndex-1) |
|
|
|
|
|
excelFile.SetCellValue(sheetName, "B"+strconv.Itoa(rowIndex), record.Jwcode) |
|
|
|
|
|
excelFile.SetCellValue(sheetName, "C"+strconv.Itoa(rowIndex), record.Name) |
|
|
|
|
|
excelFile.SetCellValue(sheetName, "D"+strconv.Itoa(rowIndex), record.DeptId) |
|
|
|
|
|
excelFile.SetCellValue(sheetName, "E"+strconv.Itoa(rowIndex), record.DeptName) |
|
|
|
|
|
excelFile.SetCellValue(sheetName, "F"+strconv.Itoa(rowIndex), record.ShopId) |
|
|
|
|
|
excelFile.SetCellValue(sheetName, "G"+strconv.Itoa(rowIndex), record.ShopName) |
|
|
|
|
|
excelFile.SetCellValue(sheetName, "H"+strconv.Itoa(rowIndex), reply.FormId) |
|
|
|
|
|
excelFile.SetCellValue(sheetName, "I"+strconv.Itoa(rowIndex), reply.Type) |
|
|
|
|
|
excelFile.SetCellValue(sheetName, "J"+strconv.Itoa(rowIndex), reply.Description) |
|
|
|
|
|
excelFile.SetCellValue(sheetName, "K"+strconv.Itoa(rowIndex), reply.ContentTitle) |
|
|
|
|
|
excelFile.SetCellValue(sheetName, "L"+strconv.Itoa(rowIndex), reply.Content) |
|
|
|
|
|
excelFile.SetCellValue(sheetName, "M"+strconv.Itoa(rowIndex), reply.UpdatedAt.String()) |
|
|
|
|
|
rowIndex++ |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 设置文件名
|
|
|
|
|
|
fileName := "Records_" + time.Now().Format("20060102150405") + ".xlsx" |
|
|
|
|
|
|
|
|
|
|
|
// 保存到缓冲区并返回
|
|
|
|
|
|
buffer, err := excelFile.WriteToBuffer() |
|
|
|
|
|
if err != nil { |
|
|
|
|
|
r.Response.WriteJsonExit(dto.Error("生成 Excel 文件失败: " + err.Error())) |
|
|
|
|
|
return |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
return columnName |
|
|
|
|
|
|
|
|
r.Response.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet") |
|
|
|
|
|
r.Response.Header().Set("Content-Disposition", "attachment; filename="+fileName) |
|
|
|
|
|
r.Response.Write(buffer.Bytes()) |
|
|
} |
|
|
} |