初始
This commit is contained in:
513
third/excel/export.go
Normal file
513
third/excel/export.go
Normal file
@ -0,0 +1,513 @@
|
||||
package excel
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"github.com/tiger1103/gfast/v3/internal/app/system/dao"
|
||||
"github.com/tiger1103/gfast/v3/internal/app/system/model"
|
||||
"github.com/tiger1103/gfast/v3/internal/app/system/model/entity"
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
// 记录累计完成量和下一周计划量
|
||||
type ExcelInfo struct {
|
||||
// 累计完成量
|
||||
Accumulated int
|
||||
// 下一周计划量
|
||||
NextWeek int
|
||||
}
|
||||
|
||||
// 导出单个方阵
|
||||
func ExportExcel(fangZhenID string, startAt, endAt string) ([]*model.WorkStatusProgressRes, map[string]ExcelInfo, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
type scheduler struct {
|
||||
// 实体
|
||||
WorkStatus *entity.WorkSchedule
|
||||
// 总进度
|
||||
FinishedPtr int
|
||||
// 总量
|
||||
Total int
|
||||
}
|
||||
|
||||
type Schdule map[string]scheduler
|
||||
|
||||
Get := func(scheduleMap Schdule, workID string) (int, int) {
|
||||
if scheduler, ok := scheduleMap[workID]; ok {
|
||||
return scheduler.FinishedPtr, scheduler.Total
|
||||
}
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
// 获取指定方阵ID的所有计划
|
||||
ScheduleData := []entity.WorkSchedule{}
|
||||
if err := dao.WorkSchedule.Ctx(ctx).Where(dao.WorkSchedule.Columns().FangzhenId, fangZhenID).
|
||||
Scan(&ScheduleData); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// 存储指定 workID 的累计完成量
|
||||
workMap := make(map[string]ExcelInfo)
|
||||
|
||||
// 计算下一周的日期
|
||||
nextWeekStart := lo.Must(time.Parse("2006-01-02", startAt)).AddDate(0, 0, 7)
|
||||
nextWeekEnd := lo.Must(time.Parse("2006-01-02", endAt)).AddDate(0, 0, 7)
|
||||
|
||||
scheduleMap := make(Schdule)
|
||||
// 遍历 ScheduleData 将数据按照 WorkID 分组
|
||||
lo.ForEach(ScheduleData, func(item entity.WorkSchedule, _ int) {
|
||||
if work, ok := workMap[item.WorkId]; ok {
|
||||
// 累加完成量
|
||||
work.Accumulated += item.FinishedNum
|
||||
workMap[item.WorkId] = work
|
||||
} else {
|
||||
workMap[item.WorkId] = ExcelInfo{
|
||||
Accumulated: item.FinishedNum,
|
||||
}
|
||||
}
|
||||
|
||||
// 如果计划处于下一周,则添加到下一周计划量中
|
||||
stime := lo.Must(time.Parse("2006-01-02", item.StartAt.Format("2006-01-02")))
|
||||
if stime.After(nextWeekStart) && stime.Before(nextWeekEnd) {
|
||||
var scheduleDetails []model.WorkScheduleDetail
|
||||
if err := json.Unmarshal([]byte(item.Detail), &scheduleDetails); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
nextweekPlanNum := 0
|
||||
|
||||
for _, detail := range scheduleDetails {
|
||||
// 如果 Date 大于开始时间并小于结束时间
|
||||
if detail.Date <= endAt {
|
||||
nextweekPlanNum += detail.PlanNum
|
||||
}
|
||||
}
|
||||
|
||||
work := workMap[item.WorkId]
|
||||
work.NextWeek += nextweekPlanNum
|
||||
workMap[item.WorkId] = work
|
||||
}
|
||||
|
||||
// 如果开始时间大于当前时间,则跳过
|
||||
startTime := item.StartAt.Format("Y-m-d")
|
||||
if startTime > startAt {
|
||||
return
|
||||
}
|
||||
|
||||
// 如果不存在则直接添加
|
||||
_, ok := scheduleMap[item.WorkId]
|
||||
if !ok {
|
||||
scheduleMap[item.WorkId] = scheduler{
|
||||
WorkStatus: &item,
|
||||
}
|
||||
}
|
||||
|
||||
// 反序列化 item.Detail 字段
|
||||
var scheduleDetails []model.WorkScheduleDetail
|
||||
if err := json.Unmarshal([]byte(item.Detail), &scheduleDetails); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
lo.ForEach(scheduleDetails, func(detail model.WorkScheduleDetail, _ int) {
|
||||
// 如果 Date 大于开始时间并小于结束时间
|
||||
if detail.Date <= endAt {
|
||||
scheduler := scheduleMap[item.WorkId]
|
||||
scheduler.Total += detail.PlanNum
|
||||
scheduler.FinishedPtr += detail.FinishedNum
|
||||
scheduleMap[item.WorkId] = scheduler
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
// 获取指定方阵ID的所有项目
|
||||
workStatusList := []entity.WorkStatus{}
|
||||
if err := dao.WorkStatus.Ctx(ctx).Where(dao.WorkStatus.Columns().FangzhenId, fangZhenID).Scan(&workStatusList); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// 父列表
|
||||
parentList := []entity.WorkStatus{}
|
||||
// 子列表,使用 map 存储,键为父元素的 ID
|
||||
childrenMap := make(map[int][]entity.WorkStatus)
|
||||
|
||||
// 遍历所有数据,将父级和子集分开
|
||||
for i := 0; i < len(workStatusList); i++ {
|
||||
item := workStatusList[i]
|
||||
if item.Parent == 0 {
|
||||
parentList = append(parentList, item)
|
||||
} else {
|
||||
childrenMap[item.Parent] = append(childrenMap[item.Parent], item)
|
||||
}
|
||||
}
|
||||
|
||||
index := 1
|
||||
|
||||
projectList := make([]*model.WorkStatusProgressRes, 0, len(parentList))
|
||||
// 遍历父级,将子集添加到父级的字段中
|
||||
for _, parent := range parentList {
|
||||
projectItem := model.WorkStatusProgressRes{
|
||||
ID: index,
|
||||
WorkID: parent.WorkId,
|
||||
Startat: parent.StartAt.String(),
|
||||
Endat: parent.EndAt.String(),
|
||||
Name: parent.WorkName,
|
||||
Total: parent.Total,
|
||||
Finished: parent.Finished,
|
||||
Status: parent.Status,
|
||||
WorkType: parent.Type,
|
||||
Children: make([]model.WorkStatusProgressRes, 0, len(workStatusList)),
|
||||
}
|
||||
index++
|
||||
|
||||
// 子类的总量和完成量
|
||||
var subTotal, subDone int
|
||||
|
||||
// 从 map 中获取子元素,将子元素添加到父元素的字段中
|
||||
for _, child := range childrenMap[int(parent.Id)] {
|
||||
// 如果计划数量等于实际数量,且原状态非3,则更新状态为2
|
||||
if child.Total == child.Finished && child.Status != 3 {
|
||||
// 并且 total,finished 不为 0
|
||||
if child.Total != 0 && child.Finished != 0 {
|
||||
child.Status = 2
|
||||
}
|
||||
}
|
||||
|
||||
// 根据 WorkID 获取计划总量和实际进度
|
||||
finishedPtr, total := Get(scheduleMap, child.WorkId)
|
||||
|
||||
subTotal += child.Total
|
||||
subDone += child.Finished
|
||||
|
||||
projectItem.Children = append(projectItem.Children, model.WorkStatusProgressRes{
|
||||
ID: index,
|
||||
WorkID: child.WorkId,
|
||||
Startat: child.StartAt.String(),
|
||||
Endat: child.EndAt.String(),
|
||||
Name: child.WorkName,
|
||||
Total: child.Total,
|
||||
Finished: child.Finished,
|
||||
IsPercent: child.IsPercent,
|
||||
Status: child.Status,
|
||||
WorkType: child.Type,
|
||||
PlanProgress: &model.PlanProgress{
|
||||
Finished: &finishedPtr,
|
||||
Total: &total,
|
||||
},
|
||||
})
|
||||
index++
|
||||
}
|
||||
|
||||
// 修改父项目的总量和进度
|
||||
projectItem.Total = subTotal
|
||||
projectItem.Finished = subDone
|
||||
|
||||
projectList = append(projectList, &projectItem)
|
||||
}
|
||||
|
||||
return projectList, workMap, nil
|
||||
}
|
||||
|
||||
// 导出一个子项目
|
||||
func AccumulateProject(subProjectID string, startAt, endAt string) ([]*model.WorkStatusProgressRes, map[string]ExcelInfo, error) {
|
||||
fangzhenList := []entity.QianqiFangzhen{}
|
||||
if err := dao.QianqiFangzhen.Ctx(context.Background()).
|
||||
Where(dao.QianqiFangzhen.Columns().ProjectId, subProjectID).Scan(&fangzhenList); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
cumulativeProjects := []*model.WorkStatusProgressRes{}
|
||||
|
||||
// 用于判断是否为第一次迭代
|
||||
firstTime := true
|
||||
|
||||
excelMap := make(map[string]ExcelInfo)
|
||||
|
||||
// 遍历项目列表
|
||||
for index := 0; index < len(fangzhenList); index++ {
|
||||
// 获取当前项目
|
||||
project := fangzhenList[index]
|
||||
|
||||
// 获取当前项目的子项目
|
||||
childProjects, emap, err := ExportExcel(strconv.Itoa(project.Id), startAt, endAt)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
excelMap = lo.Assign(emap, excelMap)
|
||||
|
||||
// 如果是第一次迭代,将子项目列表赋值给累积项目列表
|
||||
if firstTime {
|
||||
cumulativeProjects = childProjects
|
||||
// 更新标志变量,表示已经不是第一次迭代
|
||||
firstTime = false
|
||||
continue
|
||||
}
|
||||
|
||||
// 遍历子项目列表
|
||||
for childIndex := 0; childIndex < len(childProjects); childIndex++ {
|
||||
// 获取当前子项目和对应的累积项目
|
||||
singleChild := childProjects[childIndex]
|
||||
cumulativeChild := cumulativeProjects[childIndex]
|
||||
|
||||
// 更新累积项目的总数和完成数
|
||||
cumulativeChild.Total += singleChild.Total
|
||||
cumulativeChild.Finished += singleChild.Finished
|
||||
|
||||
// 如果当前子项目还有子项目,也进行同样的操作
|
||||
if len(singleChild.Children) > 0 {
|
||||
for subChildIndex := 0; subChildIndex < len(singleChild.Children); subChildIndex++ {
|
||||
// 获取当前子项目的子项目和对应的累积项目
|
||||
singleSubChild := singleChild.Children[subChildIndex]
|
||||
cumulativeSubChild := cumulativeChild.Children[subChildIndex]
|
||||
|
||||
// 更新累积项目的总数和完成数
|
||||
cumulativeSubChild.Total += singleSubChild.Total
|
||||
cumulativeSubChild.Finished += singleSubChild.Finished
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return cumulativeProjects, excelMap, nil
|
||||
}
|
||||
|
||||
// 传递一个 []*model.WorkStatusProgressRes,将数据写入到 excel 中
|
||||
func WriteExcel(tableName string, list []*model.WorkStatusProgressRes, aiResult map[string]int) (*excelize.File, error) {
|
||||
f, err := excelize.OpenFile("resource/template.xlsx")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// 创建一个工作表
|
||||
sheetName := "Sheet1"
|
||||
|
||||
headerPrefixes := []string{"一、", "二、", "三、", "四、", "五、", "六、", "七、", "八、", "九、", "十、", "十一、", "十二、"}
|
||||
childPrefixes := []string{"1、", "2、", "3、", "4、", "5、", "6、", "7、", "8、", "9、", "10、", "11、", "12、"}
|
||||
|
||||
// 修改表头
|
||||
f.SetCellValue(sheetName, "A1", tableName)
|
||||
|
||||
// 遍历 list
|
||||
rowIndex := 3
|
||||
lo.ForEach(list, func(item *model.WorkStatusProgressRes, index int) {
|
||||
setExcelValues(f, sheetName, rowIndex, item, headerPrefixes[index], aiResult)
|
||||
rowIndex++
|
||||
|
||||
// 遍历子集
|
||||
lo.ForEach(item.Children, func(child model.WorkStatusProgressRes, childIndex int) {
|
||||
setExcelValues(f, sheetName, rowIndex, &child, childPrefixes[childIndex], aiResult)
|
||||
rowIndex++
|
||||
})
|
||||
})
|
||||
|
||||
return f, nil
|
||||
}
|
||||
|
||||
func setExcelValues(f *excelize.File, sheetName string, i int, item *model.WorkStatusProgressRes, prefix string, aiResult map[string]int) {
|
||||
// 单元格 A
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("A%d", i), prefix+item.Name) // 父名
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("B%d", i), item.Total) // 总量
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("C%d", i), item.Finished) // 完成
|
||||
|
||||
var percentComplete float64
|
||||
if item.Total != 0 {
|
||||
percentComplete = float64(item.Finished) / float64(item.Total) * 100
|
||||
} else {
|
||||
percentComplete = 0
|
||||
}
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("D%d", i), fmt.Sprintf("%.f%%", percentComplete)) // 总量完成百分比
|
||||
|
||||
if item.PlanProgress != nil {
|
||||
total := 0
|
||||
if item.PlanProgress.Total != nil {
|
||||
total = *item.PlanProgress.Total
|
||||
}
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("E%d", i), total) // 计划量
|
||||
|
||||
// AI 填报数量 F
|
||||
// 用 workID 作为 key,获取 aiResult 中的值,如果不存在则为 0
|
||||
aiResultValue := aiResult[item.WorkID]
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("F%d", i), aiResultValue)
|
||||
|
||||
finished := 0
|
||||
if item.PlanProgress.Finished != nil {
|
||||
finished = *item.PlanProgress.Finished
|
||||
}
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("G%d", i), finished) // 计划完成量
|
||||
|
||||
var plannedPercentComplete float64
|
||||
if item.PlanProgress != nil && item.PlanProgress.Finished != nil && item.PlanProgress.Total != nil && *item.PlanProgress.Total != 0 {
|
||||
plannedPercentComplete = float64(*item.PlanProgress.Finished) / float64(*item.PlanProgress.Total) * 100
|
||||
} else {
|
||||
plannedPercentComplete = 0
|
||||
}
|
||||
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("H%d", i), fmt.Sprintf("%.f%%", plannedPercentComplete)) // 计划完成百分比
|
||||
}
|
||||
}
|
||||
|
||||
// 根据方阵ID获取子项目名
|
||||
func GetSubProjectName(fangZhenID string) (string, string) {
|
||||
// select sb.project_name
|
||||
// from sub_project as sb
|
||||
// left join qianqi_fangzhen as qf on qf.project_id = sb.id
|
||||
// where qf.id = 1959;
|
||||
|
||||
result, err := dao.SubProject.Ctx(context.Background()).As("sb").
|
||||
Where("qf.id", fangZhenID).
|
||||
InnerJoin("qianqi_fangzhen as qf", "qf.project_id = sb.id").
|
||||
Fields("sb.project_name,qf.name").All()
|
||||
if err != nil {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// 获取子项目名
|
||||
subProjectName := result[0].GMap().Get("project_name").(string)
|
||||
// 方阵名
|
||||
fangZhenName := result[0].GMap().Get("name").(string)
|
||||
|
||||
return subProjectName, fangZhenName
|
||||
}
|
||||
|
||||
// 根据子项目ID获取其主项目名和子项目名
|
||||
func GetNameById(subProjectID string) (string, string) {
|
||||
// select sp.project_name as projectName,sb.project_name as subProjectName
|
||||
// from sys_project as sp
|
||||
// inner join sub_project as sb on sb.project_id = sp.id
|
||||
// where sb.id = 23;
|
||||
|
||||
result, err := dao.SubProject.Ctx(context.Background()).As("sb").
|
||||
Where("sb.id", subProjectID).
|
||||
InnerJoin("sys_project as sp", "sb.project_id = sp.id").
|
||||
Fields("sp.project_name as projectName,sb.project_name as subProjectName").All()
|
||||
if err != nil {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
// 获取主项目名
|
||||
projectName := result[0].GMap().Get("projectName").(string)
|
||||
// 获取子项目名
|
||||
subProjectName := result[0].GMap().Get("subProjectName").(string)
|
||||
|
||||
return projectName, subProjectName
|
||||
}
|
||||
|
||||
func SetExcelValue(f *excelize.File, sheetName string, index int, item model.WorkStatusProgressRes, excelmap map[string]ExcelInfo, postionName string) {
|
||||
// 设计数量 F10
|
||||
total := item.Total
|
||||
// 本周完成量 G10
|
||||
finished := item.Finished
|
||||
|
||||
// 累计完成量和下周计划量
|
||||
accumulated, nextWeek := 0, 0
|
||||
|
||||
if data, exists := excelmap[item.WorkID]; exists {
|
||||
accumulated = data.Accumulated
|
||||
nextWeek = data.NextWeek
|
||||
}
|
||||
|
||||
// 累计完成百分比 单元格 I10
|
||||
percentComplete := 0.0
|
||||
if total != 0 {
|
||||
percentComplete = float64(accumulated) / float64(total) * 100
|
||||
}
|
||||
|
||||
// 名称
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", "C", index), item.Name)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", "C", index), fmt.Sprintf("%s%d", "C", index), lo.Must(f.NewStyle(Style)))
|
||||
|
||||
// 位置
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", "D", index), postionName)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", "D", index), fmt.Sprintf("%s%d", "D", index), lo.Must(f.NewStyle(Style)))
|
||||
|
||||
// 单位 样式
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", "E", index), fmt.Sprintf("%s%d", "E", index), lo.Must(f.NewStyle(Style)))
|
||||
|
||||
// 设计数量
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", "F", index), total)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", "F", index), fmt.Sprintf("%s%d", "F", index), lo.Must(f.NewStyle(Style)))
|
||||
|
||||
// 本周
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", "G", index), finished)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", "G", index), fmt.Sprintf("%s%d", "G", index), lo.Must(f.NewStyle(Style)))
|
||||
|
||||
// 累计完成量
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", "H", index), accumulated)
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", "H", index), fmt.Sprintf("%s%d", "H", index), lo.Must(f.NewStyle(Style)))
|
||||
|
||||
// 累计完成百分比
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", "I", index), fmt.Sprintf("%.f%%", percentComplete))
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", "I", index), fmt.Sprintf("%s%d", "I", index), lo.Must(f.NewStyle(Style)))
|
||||
|
||||
// 合并 jk 单元格
|
||||
_ = f.MergeCell(sheetName, fmt.Sprintf("J%d", index), fmt.Sprintf("K%d", index))
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("J%d", index), fmt.Sprintf("K%d", index), lo.Must(f.NewStyle(Style)))
|
||||
|
||||
// 水平居中 jk 单元格
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("J%d", index), fmt.Sprintf("K%d", index), lo.Must(f.NewStyle(&excelize.Style{
|
||||
Border: []excelize.Border{
|
||||
{Type: "left", Color: "000000", Style: 1},
|
||||
{Type: "top", Color: "000000", Style: 1},
|
||||
{Type: "bottom", Color: "000000", Style: 1},
|
||||
{Type: "right", Color: "000000", Style: 1},
|
||||
},
|
||||
Fill: excelize.Fill{},
|
||||
Font: nil,
|
||||
Alignment: &excelize.Alignment{
|
||||
Horizontal: "center", // 水平居中
|
||||
Indent: 0,
|
||||
JustifyLastLine: false,
|
||||
ReadingOrder: 0,
|
||||
RelativeIndent: 0,
|
||||
ShrinkToFit: false,
|
||||
TextRotation: 0,
|
||||
Vertical: "", // 垂直居中
|
||||
WrapText: false,
|
||||
},
|
||||
})))
|
||||
|
||||
// 下周计划量
|
||||
f.SetCellValue(sheetName, fmt.Sprintf("%s%d", "J", index), nextWeek)
|
||||
|
||||
// L
|
||||
f.SetCellStyle(sheetName, fmt.Sprintf("%s%d", "L", index), fmt.Sprintf("%s%d", "L", index), lo.Must(f.NewStyle(Style)))
|
||||
}
|
||||
|
||||
func GetWeekNumbers(endTime string) (int, error) {
|
||||
end, err := time.Parse(time.DateOnly, endTime)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("解析结束时间错误: %w", err)
|
||||
}
|
||||
|
||||
_, endWeek := end.ISOWeek()
|
||||
|
||||
return endWeek, nil
|
||||
}
|
||||
|
||||
func CalculateTotalProgress(list []*model.WorkStatusProgressRes) float64 {
|
||||
var totalFinished, total int
|
||||
|
||||
var calculate func(w model.WorkStatusProgressRes)
|
||||
calculate = func(w model.WorkStatusProgressRes) {
|
||||
totalFinished += w.Finished
|
||||
total += w.Total
|
||||
for _, child := range w.Children {
|
||||
calculate(child)
|
||||
}
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
calculate(*item)
|
||||
}
|
||||
|
||||
if total == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return (float64(totalFinished) / float64(total)) * 100
|
||||
}
|
Reference in New Issue
Block a user