Browse Source

12月18日 导出excel尝试

dev_ljk
lijikun 5 months ago
parent
commit
ba62fa5255
  1. 24
      link_homework/api/v1/record/Record.go
  2. 2
      link_homework/go.mod
  3. 5
      link_homework/go.sum
  4. 18
      link_homework/internal/cmd/cmd.go
  5. 169
      link_homework/internal/controller/record/manageRecord.go
  6. 8
      link_homework/internal/logic/record/record.go
  7. 2
      link_homework/internal/service/record.go

24
link_homework/api/v1/record/Record.go

@ -1,6 +1,9 @@
package record package record
import "link_homework/internal/model/dto"
import (
"github.com/gogf/gf/v2/os/gtime"
"link_homework/internal/model/dto"
)
type GetRecordListReq struct { type GetRecordListReq struct {
Id int `json:"id" orm:"" dc:"作业id"` Id int `json:"id" orm:"" dc:"作业id"`
@ -44,3 +47,22 @@ type GetShopInfoByDeptIdRes struct {
ShopId string `json:"shopId" orm:"db:cms;member_info;column:shopId" dc:"门店id"` ShopId string `json:"shopId" orm:"db:cms;member_info;column:shopId" dc:"门店id"`
ShopName string `json:"shopName" orm:"db:cms;member_info;column:shopName" dc:"门店名"` ShopName string `json:"shopName" orm:"db:cms;member_info;column:shopName" dc:"门店名"`
} }
type ExcelExportReq struct {
Jwcode int `json:"jwcode" dc:"精网号"`
Name string `json:"name" dc:"用户名字"`
DeptId string `json:"deptId" dc:"部门id"`
DeptName string `json:"deptName" dc:"部门名"`
ShopId string `json:"shopId" dc:"门店id"`
ShopName string `json:"shopName" dc:"门店名"`
Reply []Reply `json:"Reply" dc:"答案详情"`
}
type Reply struct {
FormId int `json:"formId"`
Type int `json:"type"`
FormTitle string `json:"formTitle"`
ContentTitle string `json:"contentTitle"`
Content string `json:"content"`
SubmitTime gtime.Time `json:"submitTime"`
}

2
link_homework/go.mod

@ -3,6 +3,7 @@ module link_homework
go 1.21.13 go 1.21.13
require ( require (
github.com/360EntSecGroup-Skylar/excelize v1.4.1
github.com/gogf/gf/contrib/drivers/mysql/v2 v2.8.1 github.com/gogf/gf/contrib/drivers/mysql/v2 v2.8.1
github.com/gogf/gf/contrib/nosql/redis/v2 v2.8.2 github.com/gogf/gf/contrib/nosql/redis/v2 v2.8.2
github.com/gogf/gf/v2 v2.8.2 github.com/gogf/gf/v2 v2.8.2
@ -25,6 +26,7 @@ require (
github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect
github.com/redis/go-redis/v9 v9.7.0 // indirect github.com/redis/go-redis/v9 v9.7.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect github.com/rivo/uniseg v0.4.7 // indirect

5
link_homework/go.sum

@ -1,3 +1,5 @@
github.com/360EntSecGroup-Skylar/excelize v1.4.1 h1:l55mJb6rkkaUzOpSsgEeKYtS6/0gHwBYyfo5Jcjv/Ks=
github.com/360EntSecGroup-Skylar/excelize v1.4.1/go.mod h1:vnax29X2usfl7HHkBrX5EvSCJcmH3dT9luvxzu8iGAE=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
@ -47,6 +49,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -56,6 +60,7 @@ github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93Ge
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/stretchr/testify v1.2.3-0.20181224173747-660f15d67dbb/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo= go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=

18
link_homework/internal/cmd/cmd.go

@ -21,23 +21,6 @@ var (
Func: func(ctx context.Context, parser *gcmd.Parser) (err error) { Func: func(ctx context.Context, parser *gcmd.Parser) (err error) {
s := g.Server() s := g.Server()
//后台 //后台
////启动gtoken
//// 创建一个GfToken对象,用于处理用户登录、登出、权限验证等操作
//gfToken := &gtoken.GfToken{
// // 设置登录路径,即用户登录接口登入成功后会获得一个Token
// LoginPath: "/login",
// //// 设置登录前执行的函数,在用户登录之前会调用这个函数进行一些预处理,比如验证用户名和密码等。
// //LoginBeforeFunc: loginFunc, //手动编写 没有同时配置登入路径,登入方法,登出路径启动时会报错
// // 设置登出路径,即用户登出接口登入成功后会删除Token
// LogoutPath: "/user/logout",
// //// 设置需要拦截的路径,按照前缀拦截,所有以/user或/system开头的路径都需要进行Token认证。
// //AuthPaths: g.SliceStr{"/user", "/system"},
// //// 设置不需要拦截的路径,所有以/user/info或/system/user/开头的路径都不需要进行Token认证。
// //AuthExcludePaths: g.SliceStr{"/user/info", "/system/user/*"},
// //// 开启全局拦截,默认关闭,如果设置为true,则所有请求都会经过Token认证中间件,如果设置为false,则只有指定路径的请求会经过Token认证中间件。
// //GlobalMiddleware: true,
//}
s.Group("/api/homework_manage", func(group *ghttp.RouterGroup) { s.Group("/api/homework_manage", func(group *ghttp.RouterGroup) {
group.Middleware(middleware.MiddlewareCORS) group.Middleware(middleware.MiddlewareCORS)
//group.Middleware(middleware.MiddlewareIsLogin) //group.Middleware(middleware.MiddlewareIsLogin)
@ -47,6 +30,7 @@ var (
group.POST("/getrecordbycondition", record.NewManageRecord().GetRecordByCondition) group.POST("/getrecordbycondition", record.NewManageRecord().GetRecordByCondition)
group.POST("/getdeptinfo", record.NewManageRecord().GetDeptInfo) group.POST("/getdeptinfo", record.NewManageRecord().GetDeptInfo)
group.POST("/getshopinfo", record.NewManageRecord().GetShopInfo) group.POST("/getshopinfo", record.NewManageRecord().GetShopInfo)
group.POST("/exceleexport", record.NewManageRecord().ExeclExport)
group.POST("/get-homework-list", homework.Homework().GetHomeworkList) group.POST("/get-homework-list", homework.Homework().GetHomeworkList)
group.POST("/get-homework", homework.Homework().GetHomework) group.POST("/get-homework", homework.Homework().GetHomework)
group.POST("/add-homework", homework.Homework().AddHomework) group.POST("/add-homework", homework.Homework().AddHomework)

169
link_homework/internal/controller/record/manageRecord.go

@ -1,10 +1,14 @@
package record package record
import ( import (
"github.com/360EntSecGroup-Skylar/excelize"
"github.com/gogf/gf/v2/net/ghttp" "github.com/gogf/gf/v2/net/ghttp"
"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"
) )
type ManageRecord struct{} type ManageRecord struct{}
@ -96,5 +100,170 @@ func (m *ManageRecord) GetShopInfo(r *ghttp.Request) {
Message: "success", Message: "success",
Data: result, Data: result,
}) })
}
func (m *ManageRecord) ExeclExport(r *ghttp.Request) {
//获取前端传来的参数
var req record.GetRecordByConditionReq
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(),
})
}
//创建文件
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"}}`)
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)
}
//填充数据
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"))
}
////定义表头样式,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 的情况进行额外处理。
}
return columnName
} }

8
link_homework/internal/logic/record/record.go

@ -24,8 +24,12 @@ func NewRecord() *sRecord {
// 无条件全查 // 无条件全查
func (s *sRecord) GetRecordList(ctx context.Context, groupId, pageNo, pageSize int) (record []pkgRecord.GetRecordListRes, err error) { func (s *sRecord) GetRecordList(ctx context.Context, groupId, pageNo, pageSize int) (record []pkgRecord.GetRecordListRes, err error) {
//从record表中查询出jwcode,根据group_id //从record表中查询出jwcode,根据group_id
if pageNo == 0 && pageSize == 0 {
err = dao.ActivityInteractiveRecord.Ctx(ctx).Fields("jwcode").Where("group_id", groupId).Group("jwcode").Scan(&record)
} else {
err = dao.ActivityInteractiveRecord.Ctx(ctx).Fields("jwcode").Where("group_id", groupId).Group("jwcode"). err = dao.ActivityInteractiveRecord.Ctx(ctx).Fields("jwcode").Where("group_id", groupId).Group("jwcode").
Page(pageNo, pageSize).Scan(&record) Page(pageNo, pageSize).Scan(&record)
}
if err != nil { if err != nil {
return nil, errors.New("无条件查jwcode失败") return nil, errors.New("无条件查jwcode失败")
} }
@ -46,7 +50,7 @@ func (s *sRecord) GetRecordList(ctx context.Context, groupId, pageNo, pageSize i
//根据jwcode,groupId在record表中查询最新的提交记录进行存放 //根据jwcode,groupId在record表中查询最新的提交记录进行存放
var recordInfo []dto.RecordInfo var recordInfo []dto.RecordInfo
err = dao.ActivityInteractiveRecord.Ctx(ctx).Fields("content", "content_title", "updated_at", "form_id"). err = dao.ActivityInteractiveRecord.Ctx(ctx).Fields("content", "content_title", "updated_at", "form_id").
Where("jwcode", info.Jwcode).Where("group_id", groupId).Group("form_id").Order("updated_at desc").Scan(&recordInfo) //不是最新的,需要再改
Where("jwcode", info.Jwcode).Where("group_id", groupId).Group("form_id").Order("updated_at desc").Scan(&recordInfo)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -62,7 +66,7 @@ func (s *sRecord) GetRecordList(ctx context.Context, groupId, pageNo, pageSize i
return return
} }
// 根据条件查询
// 根据条件查询 对所有结果进行筛选
func (s *sRecord) GetRecordByCondition(ctx context.Context, groupId, jwcode int, deptId, shopId string, pageNo, pageSize int) (record []pkgRecord.GetRecordListRes, err error) { func (s *sRecord) GetRecordByCondition(ctx context.Context, groupId, jwcode int, deptId, shopId string, pageNo, pageSize int) (record []pkgRecord.GetRecordListRes, err error) {
//全查 //全查
recordList, err := s.GetRecordList(ctx, groupId, pageNo, pageSize) recordList, err := s.GetRecordList(ctx, groupId, pageNo, pageSize)

2
link_homework/internal/service/record.go

@ -14,7 +14,7 @@ type (
IRecord interface { IRecord interface {
// 无条件全查 // 无条件全查
GetRecordList(ctx context.Context, groupId int, pageNo int, pageSize int) (record []pkgRecord.GetRecordListRes, err error) GetRecordList(ctx context.Context, groupId int, pageNo int, pageSize int) (record []pkgRecord.GetRecordListRes, err error)
// 根据条件查询
// 根据条件查询 对所有结果进行筛选
GetRecordByCondition(ctx context.Context, groupId int, jwcode int, deptId string, shopId string, pageNo int, pageSize int) (record []pkgRecord.GetRecordListRes, err error) GetRecordByCondition(ctx context.Context, groupId int, jwcode int, deptId string, shopId string, pageNo int, pageSize int) (record []pkgRecord.GetRecordListRes, err error)
// 查询部门信息 // 查询部门信息
GetDeptInfo(ctx context.Context) (depts []pkgRecord.GetDeptInfoRes, err error) GetDeptInfo(ctx context.Context) (depts []pkgRecord.GetDeptInfoRes, err error)

Loading…
Cancel
Save