From 3a124aa778273b0da6265b8529ea839bb1b7b346 Mon Sep 17 00:00:00 2001 From: wucongxing8150 <815046773@qq.com> Date: Wed, 25 Sep 2024 11:56:44 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AF=BC=E5=87=BA=E6=96=B0=E5=A2=9E=E4=BA=86?= =?UTF-8?q?=E5=90=88=E5=B9=B6=E5=8D=95=E5=85=83=E6=A0=BC=EF=BC=8C=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/dao/orderProduct.go | 28 +- api/model/orderProductItem.go | 1 + api/service/export.go | 237 ++++++++------- utils/export.go | 530 +++++++++++++++++++--------------- utils/exportbak.go | 298 +++++++++++++++++++ 5 files changed, 768 insertions(+), 326 deletions(-) create mode 100644 utils/exportbak.go diff --git a/api/dao/orderProduct.go b/api/dao/orderProduct.go index 91aeb9c..be4fdf5 100644 --- a/api/dao/orderProduct.go +++ b/api/dao/orderProduct.go @@ -68,6 +68,22 @@ func (r *OrderProductDao) GetOrderProductList(maps interface{}) (m []*model.Orde return m, nil } +// GetOrderProductNormalList 获取药品订单列表-正常订单 +func (r *OrderProductDao) GetOrderProductNormalList(maps interface{}, createdAt model.LocalTime) (m []*model.OrderProduct, err error) { + query := global.Db.Where(maps) + + // 格式化时间为 "Y-m-d H:i:s" + at := time.Time(createdAt).Format("2006-01-02 15:04:05") + query = query.Where("created_at < ?", at) // 有效使用时间 + + query = query.Where("order_product_status in (?)", []string{"1", "2", "3", "4"}) + err = query.Find(&m).Error + if err != nil { + return nil, err + } + return m, nil +} + // GetOrderProduct 获取药品订单 func (r *OrderProductDao) GetOrderProduct(maps interface{}) (m *model.OrderProduct, err error) { err = global.Db.Where(maps).First(&m).Error @@ -334,6 +350,10 @@ func (r *OrderProductDao) GetOrderProductExportListSearch(req requests.OrderProd return db.Omit("open_id", "union_id", "wx_session_key") }) + query = query.Preload("UserDoctor.User", func(db *gorm.DB) *gorm.DB { + return db.Select("user_id", "user_name", "mobile") + }) + // 处方 query = query.Preload("OrderPrescription", func(db *gorm.DB) *gorm.DB { return db.Select("order_prescription_id", "prescription_code") @@ -349,6 +369,12 @@ func (r *OrderProductDao) GetOrderProductExportListSearch(req requests.OrderProd return db.Select("user_id", "user_name", "mobile") }) + // 药品列表 + query = query.Preload("OrderProductItem") + + // 药品列表-药品 + query = query.Preload("OrderProductItem.Product") + // 当前搜索数据 if req.Type == 1 { // 医生姓名 @@ -534,7 +560,7 @@ func (r *OrderProductDao) GetOrderProductExportListSearch(req requests.OrderProd } // 排序 - query = query.Order("created_at desc") + query = query.Order("created_at asc") err = query.Find(&m).Error if err != nil { diff --git a/api/model/orderProductItem.go b/api/model/orderProductItem.go index 5b56c51..4cab3c0 100644 --- a/api/model/orderProductItem.go +++ b/api/model/orderProductItem.go @@ -21,6 +21,7 @@ type OrderProductItem struct { ProductCoverImg string `gorm:"column:product_cover_img;type:varchar(255);comment:商品封面图" json:"product_cover_img"` ProductSpec string `gorm:"column:product_spec;type:varchar(255);comment:商品规格" json:"product_spec"` Model + Product *Product `gorm:"foreignKey:ProductId;references:product_id" json:"product"` // 药品 } func (m *OrderProductItem) TableName() string { diff --git a/api/service/export.go b/api/service/export.go index c70155a..fd8d7e3 100644 --- a/api/service/export.go +++ b/api/service/export.go @@ -245,40 +245,54 @@ type OrderInquiry struct { // OrderProductData 药品订单 type OrderProductData struct { - OrderProductNo string // 订单编号 - DoctorName string // 医生姓名 - PatientName string // 患者姓名-就诊人 - PatientSex string // 患者性别-就诊人(0:未知 1:男 2:女) - PatientAge string // 患者年龄-就诊人 - PatientMobile string // 患者电话 - EscrowTradeNo string // 第三方支付流水号 - PrescriptionCode string // 处方编号 - OrderProductStatus string // 订单状态(1:待支付 2:待发货 3:已发货 4:已签收 5:已取消) - PayChannel string // 支付渠道(1:小程序支付 2:微信扫码支付);NOT NULL - PayStatus string // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) - CancelReason string // 订单取消原因(1:主动取消 2:复核失败/库存不足 3:支付超时 4:客服取消) - AmountTotal float64 // 订单金额 - PaymentAmountTotal float64 // 实际付款金额 - LogisticsFee float64 // 运费金额 - LogisticsNo string // 物流编号 - LogisticsCompanyCode string // 快递公司编码 - ProductNames string // 药品列表 - DeliveryTime time.Time // 发货时间 - PayTime time.Time // 支付时间 - Remarks string // 订单备注 - RefundStatus string // 商品订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常) - CancelTime time.Time // 订单取消时间 - CancelRemarks string // 订单取消备注(自动添加) - ReportPreStatus string // 上报处方平台状态(0:未上报 1:已上报 2:上报失败)) - ReportPreTime time.Time // 上报处方平台时间 - ReportPreFailReason string // 上报失败原因 - Province string // 省份 - City string // 城市 - County string // 区县 - Address string // 详细地址 - ConsigneeName string // 收货人姓名 - ConsigneeTel string // 收货人电话 - CreatedAt time.Time // 创建时间 + OrderProductNo string // 订单编号 + OrderProductStatus string // 订单状态(1:待支付 2:待发货 3:已发货 4:已签收 5:已取消) + AmountTotal float64 // 订单金额 + CouponAmountTotal float64 // 优惠卷总金额 + PaymentAmountTotal float64 // 实际付款金额 + LogisticsFee float64 // 运费金额 + CreatedAt string // 创建时间 + Remarks string // 订单备注 + DoctorName string // 医生姓名 + DoctorMobile string // 医生电话 + PatientName string // 患者姓名-就诊人 + PatientSex string // 患者性别-就诊人(0:未知 1:男 2:女) + PatientAge string // 患者年龄-就诊人 + PatientMobile string // 患者电话 + HistoryBuyCount string // 历史购买次数 + HistoryBuyOrderProductNo string // 历史购买单号(逗号分隔) + EscrowTradeNo string // 第三方支付流水号 + PayStatus string // 支付状态(1:未支付 2:已支付 3:支付中 4:支付失败 5:支付超时 6:支付关闭 7:已撤销 8:转入退款) + PayTime string // 支付时间 + PayChannel string // 支付渠道(1:小程序支付 2:微信扫码支付);NOT NULL + PrescriptionCode string // 处方编号 + ReportPreStatus string // 上报处方平台状态(0:未上报 1:已上报 2:上报失败)) + ReportPreTime string // 上报处方平台时间 + ReportPreFailReason string // 上报失败原因 + LogisticsNo string // 物流编号 + LogisticsCompanyCode string // 快递公司编码 + DeliveryTime string // 发货时间 + ProductItem []OrderProductItemData // 药品列表 + RefundStatus string // 商品订单退款状态(0:无退款 1:申请退款 2:退款中 3:退款成功 4:拒绝退款 5:退款关闭 6:退款异常) + CancelTime string // 订单取消时间 + CancelRemarks string // 订单取消备注(自动添加) + CancelReason string // 订单取消原因(1:主动取消 2:复核失败/库存不足 3:支付超时 4:客服取消) + ConsigneeName string // 收货人姓名 + ConsigneeTel string // 收货人电话 + Province string // 省份 + City string // 城市 + County string // 区县 + Address string // 详细地址 +} + +// OrderProductItemData 药品订单-药品列表 +type OrderProductItemData struct { + ProductName string // 商品名称 + ProductPlatformCode string // 处方平台商品编码 + ProductSpec string // 商品规格 + AvailableDays float64 // 可用天数(3) + ProductAmount string // 药品数量 + ProductPrice float64 // 商品价格 } // OrderServicePackageDto 订单-服务包 @@ -383,7 +397,7 @@ func (r *ExportService) DoctorWithdrawal(doctorWithdrawals []*model.DoctorWithdr {Value: "申请时间", CellType: "date", NumberFmt: "yyyy-mm-dd hh:mm:ss", ColWidth: 30}, } - var interfaceSlice []interface{} + var dataSlice []interface{} for _, v := range doctorWithdrawals { // 审核状态 examineStatus := utils.WithdrawalExamineStatusToString(v.ExamineStatus) @@ -480,10 +494,10 @@ func (r *ExportService) DoctorWithdrawal(doctorWithdrawals []*model.DoctorWithdr CreatedAt: createdAt, } - interfaceSlice = append(interfaceSlice, doctorWithdrawalData) + dataSlice = append(dataSlice, doctorWithdrawalData) } - file, err := utils.Export(header, interfaceSlice) + file, err := utils.Export(header, dataSlice) if err != nil { return "", err } @@ -522,7 +536,7 @@ func (r *ExportService) DoctorWithdrawalOrder(doctorWithdrawalOrders []*model.Do {Value: "医生收益", CellType: "float", NumberFmt: "0.0000", ColWidth: 18}, } - var interfaceSlice []interface{} + var dataSlice []interface{} for _, v := range doctorWithdrawalOrders { // 医生姓名 doctorName := "" @@ -594,10 +608,10 @@ func (r *ExportService) DoctorWithdrawalOrder(doctorWithdrawalOrders []*model.Do DoctorAmount: doctorAmount, } - interfaceSlice = append(interfaceSlice, doctorWithdrawalOrderData) + dataSlice = append(dataSlice, doctorWithdrawalOrderData) } - file, err := utils.Export(header, interfaceSlice) + file, err := utils.Export(header, dataSlice) if err != nil { return "", err } @@ -673,7 +687,7 @@ func (r *ExportService) UserDoctor(userDoctors []*model.UserDoctor) (string, err {Value: "银行地址", CellType: "string", NumberFmt: "", ColWidth: 46}, } - var interfaceSlice []interface{} + var dataSlice []interface{} for _, v := range userDoctors { userDoctorData := UserDoctorData{ UserName: v.UserName, @@ -914,10 +928,10 @@ func (r *ExportService) UserDoctor(userDoctors []*model.UserDoctor) (string, err } } - interfaceSlice = append(interfaceSlice, userDoctorData) + dataSlice = append(dataSlice, userDoctorData) } - file, err := utils.Export(header, interfaceSlice) + file, err := utils.Export(header, dataSlice) if err != nil { return "", err } @@ -1510,42 +1524,53 @@ func (r *ExportService) OrderInquiry(d []*model.OrderInquiry) (string, error) { // OrderProduct 药品订单 func (r *ExportService) OrderProduct(d []*model.OrderProduct) (string, error) { header := []utils.HeaderCellData{ - {Value: "系统订单编号", CellType: "string", NumberFmt: "", ColWidth: 25}, - {Value: "医生姓名", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "患者姓名-就诊人", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "患者性别-就诊人", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "患者年龄-就诊人", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "患者电话", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "第三方支付流水号", CellType: "string", NumberFmt: "", ColWidth: 35}, - {Value: "处方编号", CellType: "string", NumberFmt: "", ColWidth: 28}, - {Value: "订单状态", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "支付渠道", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "支付状态", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "订单取消原因", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "订单金额", CellType: "float64", NumberFmt: "0.0000", ColWidth: 18}, - {Value: "实际付款金额", CellType: "float64", NumberFmt: "0.0000", ColWidth: 18}, - {Value: "运费金额", CellType: "float64", NumberFmt: "0.0000", ColWidth: 18}, - {Value: "物流编号", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "快递公司编码", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "药品", CellType: "string", NumberFmt: "", ColWidth: 35}, - {Value: "发货时间", CellType: "date", NumberFmt: "yyyy-mm-dd hh:mm:ss", ColWidth: 30}, - {Value: "支付时间", CellType: "date", NumberFmt: "yyyy-mm-dd hh:mm:ss", ColWidth: 30}, - {Value: "订单备注", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "退款状态", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "订单取消时间", CellType: "date", NumberFmt: "yyyy-mm-dd hh:mm:ss", ColWidth: 30}, - {Value: "订单取消备注", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "上报处方平台状态", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "上报处方平台时间", CellType: "date", NumberFmt: "yyyy-mm-dd hh:mm:ss", ColWidth: 30}, - {Value: "上报失败原因", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "省份", CellType: "string", NumberFmt: "", ColWidth: 20}, - {Value: "城市", CellType: "string", NumberFmt: "", ColWidth: 22}, - {Value: "区县", CellType: "string", NumberFmt: "", ColWidth: 25}, - {Value: "详细地址", CellType: "string", NumberFmt: "", ColWidth: 35}, - {Value: "收货人姓名", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "收货人电话", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "创建时间", CellType: "date", NumberFmt: "yyyy-mm-dd hh:mm:ss", ColWidth: 30}, + {Value: "系统订单编号", CellType: "string", NumberFmt: "", ColWidth: 25, Colour: "#FFD700"}, + {Value: "订单状态", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#FFD700"}, + {Value: "订单金额", CellType: "float64", NumberFmt: "0.0000", ColWidth: 18, Colour: "#FFD700"}, + {Value: "优惠卷总金额", CellType: "float64", NumberFmt: "0.0000", ColWidth: 18, Colour: "#FFD700"}, + {Value: "实际付款金额", CellType: "float64", NumberFmt: "0.0000", ColWidth: 18, Colour: "#FFD700"}, + {Value: "运费金额", CellType: "float64", NumberFmt: "0.0000", ColWidth: 18, Colour: "#FFD700"}, + {Value: "创建时间", CellType: "date", NumberFmt: "yyyy-mm-dd hh:mm:ss", ColWidth: 30, Colour: "#FFD700"}, + {Value: "订单备注", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#FFD700"}, + {Value: "医生姓名", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#EE9A49"}, + {Value: "医生手机号", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#EE9A49"}, + {Value: "患者姓名-就诊人", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#40E0D0"}, + {Value: "患者性别-就诊人", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#40E0D0"}, + {Value: "患者年龄-就诊人", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#40E0D0"}, + {Value: "患者电话", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#40E0D0"}, + {Value: "历史购买次数", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#40E0D0"}, + {Value: "历史购买单号", CellType: "string", NumberFmt: "", ColWidth: 40, Colour: "#40E0D0"}, + {Value: "第三方支付流水号", CellType: "string", NumberFmt: "", ColWidth: 35, Colour: "#90EE90"}, + {Value: "支付状态", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#90EE90"}, + {Value: "支付时间", CellType: "date", NumberFmt: "yyyy-mm-dd hh:mm:ss", ColWidth: 30, Colour: "#90EE90"}, + {Value: "支付渠道", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#90EE90"}, + {Value: "处方编号", CellType: "string", NumberFmt: "", ColWidth: 28, Colour: "#FFE4E1"}, + {Value: "上报处方平台状态", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#FFE4E1"}, + {Value: "上报处方平台时间", CellType: "date", NumberFmt: "yyyy-mm-dd hh:mm:ss", ColWidth: 30, Colour: "#FFE4E1"}, + {Value: "上报失败原因", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#FFE4E1"}, + {Value: "物流编号", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#CAE1FF"}, + {Value: "快递公司编码", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#CAE1FF"}, + {Value: "发货时间", CellType: "date", NumberFmt: "yyyy-mm-dd hh:mm:ss", ColWidth: 30, Colour: "#CAE1FF"}, + {Value: "药品名称", CellType: "string", NumberFmt: "", ColWidth: 40, Colour: "#AB82FF"}, + {Value: "处方平台编码", CellType: "string", NumberFmt: "", ColWidth: 30, Colour: "#AB82FF"}, + {Value: "商品规格", CellType: "string", NumberFmt: "", ColWidth: 30, Colour: "#AB82FF"}, + {Value: "用药天数", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#AB82FF"}, + {Value: "药品数量", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#AB82FF"}, + {Value: "药品价格", CellType: "float64", NumberFmt: "0.0000", ColWidth: 18, Colour: "#AB82FF"}, + {Value: "退款状态", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#90EE90"}, + {Value: "订单取消时间", CellType: "date", NumberFmt: "yyyy-mm-dd hh:mm:ss", ColWidth: 30, Colour: "#90EE90"}, + {Value: "订单取消备注", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#90EE90"}, + {Value: "订单取消原因", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#90EE90"}, + {Value: "收货人姓名", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#FFDEAD"}, + {Value: "收货人电话", CellType: "string", NumberFmt: "", ColWidth: 18, Colour: "#FFDEAD"}, + {Value: "省份", CellType: "string", NumberFmt: "", ColWidth: 20, Colour: "#FFDEAD"}, + {Value: "城市", CellType: "string", NumberFmt: "", ColWidth: 22, Colour: "#FFDEAD"}, + {Value: "区县", CellType: "string", NumberFmt: "", ColWidth: 25, Colour: "#FFDEAD"}, + {Value: "详细地址", CellType: "string", NumberFmt: "", ColWidth: 35, Colour: "#FFDEAD"}, } + orderProductDao := dao.OrderProductDao{} + var dataSlice []interface{} for _, v := range d { data := OrderProductData{ @@ -1556,6 +1581,7 @@ func (r *ExportService) OrderProduct(d []*model.OrderProduct) (string, error) { PayStatus: utils.PayStatusToString(v.PayStatus), CancelReason: utils.ProductCancelReasonToString(v.CancelReason), AmountTotal: v.AmountTotal, + CouponAmountTotal: v.CouponAmountTotal, PaymentAmountTotal: v.PaymentAmountTotal, LogisticsFee: v.LogisticsFee, LogisticsNo: v.LogisticsNo, @@ -1576,6 +1602,11 @@ func (r *ExportService) OrderProduct(d []*model.OrderProduct) (string, error) { if v.UserDoctor != nil { // 医生姓名 data.DoctorName = v.UserDoctor.UserName + + if v.UserDoctor.User != nil { + // 医生电话 + data.DoctorMobile = v.UserDoctor.User.Mobile + } } if v.OrderInquiry != nil { @@ -1600,44 +1631,54 @@ func (r *ExportService) OrderProduct(d []*model.OrderProduct) (string, error) { } if v.DeliveryTime != (model.LocalTime{}) { - t := time.Time(v.DeliveryTime) - data.DeliveryTime = t + data.DeliveryTime = time.Time(v.DeliveryTime).Format("2006-01-02 15:04:05") } if v.PayTime != (model.LocalTime{}) { - t := time.Time(v.PayTime) - data.PayTime = t + data.PayTime = time.Time(v.PayTime).Format("2006-01-02 15:04:05") } if v.CancelTime != (model.LocalTime{}) { - t := time.Time(v.CancelTime) - data.CancelTime = t + data.CancelTime = time.Time(v.CancelTime).Format("2006-01-02 15:04:05") } if v.ReportPreTime != (model.LocalTime{}) { - t := time.Time(v.ReportPreTime) - data.ReportPreTime = t + data.ReportPreTime = time.Time(v.ReportPreTime).Format("2006-01-02 15:04:05") } if v.CreatedAt != (model.LocalTime{}) { - t := time.Time(v.CreatedAt) - data.CreatedAt = t + data.CreatedAt = time.Time(v.CreatedAt).Format("2006-01-02 15:04:05") } - // 处理药品列表 - orderProductItemDao := dao.OrderProductItemDao{} - orderProductItems, err := orderProductItemDao.GetOrderProductItemByOrderProductId(v.OrderProductId) - if err == nil || len(orderProductItems) > 0 { - var products []string - for _, v := range orderProductItems { - amount := fmt.Sprintf("%d", v.Amount) - productPrice := fmt.Sprintf("%.2f", v.ProductPrice) - product := v.ProductName + "(N:" + amount + " " + "P:" + productPrice + ")" + // 历史购买次数 + maps := make(map[string]interface{}) + maps["patient_id"] = v.PatientId + orderProducts, err := orderProductDao.GetOrderProductNormalList(maps, v.CreatedAt) + if err != nil { + return "", err + } + data.HistoryBuyCount = fmt.Sprintf("%d", len(orderProducts)) - products = append(products, product) + // 历史购买单号(逗号分隔) + var HistoryBuyOrderProductNo []string + for _, product := range orderProducts { + HistoryBuyOrderProductNo = append(HistoryBuyOrderProductNo, fmt.Sprintf("%s", product.OrderProductNo)) + } + + data.HistoryBuyOrderProductNo = strings.Join(HistoryBuyOrderProductNo, ",") + + // 处理药品列表 + for _, item := range v.OrderProductItem { + productItem := OrderProductItemData{ + ProductName: item.ProductName, + ProductPlatformCode: item.ProductPlatformCode, + ProductSpec: item.ProductSpec, + AvailableDays: item.Product.AvailableDays, + ProductAmount: fmt.Sprintf("%d", item.Amount), + ProductPrice: item.ProductPrice, } - data.ProductNames = strings.Join(products, "; ") + data.ProductItem = append(data.ProductItem, productItem) } dataSlice = append(dataSlice, data) diff --git a/utils/export.go b/utils/export.go index a726458..71c6e00 100644 --- a/utils/export.go +++ b/utils/export.go @@ -2,109 +2,40 @@ package utils import ( "bytes" + "errors" "fmt" "github.com/xuri/excelize/v2" "reflect" - "strconv" ) -// func Export(widths []int) (bool, error) { -// f := excelize.NewFile() -// defer func() { -// _ = f.Close() -// }() -// -// // 创建一个工作表 -// index, err := f.NewSheet("Sheet1") -// if err != nil { -// return false, err -// } -// -// // 设置工作簿的默认工作表 -// f.SetActiveSheet(index) -// -// // 单元格对齐样式 -// alignment := &excelize.Alignment{ -// Horizontal: "center", -// Vertical: "center", -// } -// -// // 单元格颜色填充样式 -// fill := excelize.Fill{ -// Type: "pattern", -// Pattern: 1, -// Color: []string{"#c9daf8"}, -// Shading: 0, -// } -// -// // 第一行的左、右、下边框 -// border := []excelize.Border{ -// { -// Type: "left,right,bottom", -// Color: "", -// Style: 1, -// }, -// } -// -// // 工作表样式 -// style, err := f.NewStyle( -// &excelize.Style{ -// Fill: fill, -// Alignment: alignment, -// Border: border, -// }, -// ) -// if err != nil { -// return false, err -// } -// -// // 依次设置每一列的列宽 -// widths = []int{18, 18, 18, 18, 18, 20, 23, 46, 18, 30, 30, 18, 18, 30, 18, 30} -// for col, width := range widths { -// // 获取列名 -// colName, err := excelize.ColumnNumberToName(col + 1) -// if err != nil { -// return false, err -// } -// -// // 设置列宽 -// err = f.SetColWidth("Sheet1", colName, colName, float64(width)) -// if err != nil { -// return false, err -// } -// -// // 设置列背景颜色 -// err = f.SetCellStyle("Sheet1", colName+"1", colName+"1", style) -// if err != nil { -// return false, err -// } -// } -// -// // 设置行高 -// err = f.SetRowStyle("Sheet1", 1, 10, style) -// if err != nil { -// return false, err -// } -// -// if err := f.SaveAs("output.xlsx"); err != nil { -// return false, err -// } -// -// return true, nil -// } - // HeaderCellData 表头内容 type HeaderCellData struct { Value string // 值 CellType string // 类型 NumberFmt string // 格式化方式 ColWidth int // 列宽 + Colour string // 颜色 } -func Export(header []HeaderCellData, data []interface{}) (*bytes.Buffer, error) { - sheetName := "Sheet1" +// GenerateSheet 生成表格 +func GenerateSheet(header []HeaderCellData, sheetName string) (f *excelize.File, err error) { + // 创建工作表 + err, f = createSheet(sheetName) + if err != nil { + return nil, err + } - f := excelize.NewFile() + // 设置表头 + if err := setHeader(f, sheetName, header); err != nil { + return nil, err + } + + return f, nil +} + +// 创建工作表 +func createSheet(sheetName string) (err error, f *excelize.File) { + f = excelize.NewFile() defer func() { _ = f.Close() }() @@ -112,7 +43,7 @@ func Export(header []HeaderCellData, data []interface{}) (*bytes.Buffer, error) // 创建一个工作表 index, err := f.NewSheet(sheetName) if err != nil { - return nil, err + return err, nil } // 设置工作簿的默认工作表 @@ -121,178 +52,323 @@ func Export(header []HeaderCellData, data []interface{}) (*bytes.Buffer, error) // 设置工作表默认字体 err = f.SetDefaultFont("宋体") if err != nil { - return nil, err - } - - // 统一单元格对齐样式 - alignment := &excelize.Alignment{ - Horizontal: "center", - Vertical: "center", + return err, nil } // 设置行高 35-第一行 err = f.SetRowHeight(sheetName, 1, 35) if err != nil { - return nil, err + return err, nil } - // 处理工作表表头 + return nil, f +} + +// 设置表头 +func setHeader(f *excelize.File, sheetName string, header []HeaderCellData) error { for c, cell := range header { // 获取列名 colName, err := excelize.ColumnNumberToName(c + 1) if err != nil { - return nil, err + return err } // 添加单元格的值 - err = f.SetCellValue(sheetName, colName+"1", cell.Value) - if err != nil { - return nil, err + if err := f.SetCellValue(sheetName, colName+"1", cell.Value); err != nil { + return err } // 单元格颜色填充样式 - fill := excelize.Fill{ - Type: "pattern", - Pattern: 1, - Color: []string{"#c9daf8"}, - Shading: 0, + var fill excelize.Fill + if cell.Colour != "" { + fill = excelize.Fill{ + Type: "pattern", + Pattern: 1, + Color: []string{cell.Colour}, + Shading: 0, + } } // 第一行的左、右、下边框 border := []excelize.Border{ - { - Type: "left", - Color: "#000000", - Style: 1, - }, - { - Type: "right", - Color: "#000000", - Style: 1, - }, - { - Type: "bottom", - Color: "#000000", - Style: 1, - }, + {Type: "left", Color: "#000000", Style: 1}, + {Type: "right", Color: "#000000", Style: 1}, + {Type: "bottom", Color: "#000000", Style: 1}, } // 设置单元格值类型和格式 - style, _ := f.NewStyle(&excelize.Style{ - Alignment: alignment, // 字体居中 - Fill: fill, // 背景颜色 - Border: border, // 边框 + style, err := f.NewStyle(&excelize.Style{ + Alignment: &excelize.Alignment{ + Horizontal: "center", + Vertical: "center", + }, + Fill: fill, + Border: border, }) - err = f.SetCellStyle(sheetName, colName+"1", colName+"1", style) if err != nil { - return nil, err + return err } - // 设置列宽 - err = f.SetColWidth(sheetName, colName, colName, float64(cell.ColWidth)) - if err != nil { - return nil, err + if err := f.SetCellStyle(sheetName, colName+"1", colName+"1", style); err != nil { + return err + } + + if err := f.SetColWidth(sheetName, colName, colName, float64(cell.ColWidth)); err != nil { + return err } } + return nil +} - // 设置单元格格式 - row := len(data) - for i, cell := range header { - // 获取列名 - colName, err := excelize.ColumnNumberToName(i + 1) - if err != nil { - return nil, err - } - - // 字体居中 - style := &excelize.Style{} - style = &excelize.Style{ - Alignment: alignment, - } - - if cell.CellType == "float" { - style.NumFmt = 2 - customNumFmt := "0.000" - style.CustomNumFmt = &customNumFmt - } - - if cell.CellType == "date" { - style.NumFmt = 22 - customNumFmt := "yyyy-mm-dd hh:mm:ss" - style.CustomNumFmt = &customNumFmt - } - - newStyle, _ := f.NewStyle(style) - err = f.SetCellStyle(sheetName, colName+"2", colName+strconv.Itoa(row), newStyle) - if err != nil { - return nil, err - } - } - - // 填充数据 - for r, rowData := range data { - rv := reflect.ValueOf(rowData) - for c := 0; c < rv.NumField(); c++ { - cellValue := rv.Field(c).Interface() - // 获取列名 - colName, err := excelize.ColumnNumberToName(c + 1) - if err != nil { - return nil, err - } - - axis := colName + fmt.Sprintf("%d", r+2) - - // 设置单元格值 - err = f.SetCellValue(sheetName, axis, cellValue) - if err != nil { - return nil, err - } - - // 设置单元格值类型 - cellType := header[c].CellType - - // 字体居中 - style := &excelize.Style{} - style = &excelize.Style{ - Alignment: alignment, - } - - if cellType == "float" { - style.NumFmt = 2 - customNumFmt := "0.000" - style.CustomNumFmt = &customNumFmt - } - - if cellType == "date" { - style.NumFmt = 22 - customNumFmt := "yyyy-mm-dd hh:mm:ss" - style.CustomNumFmt = &customNumFmt - } - - newStyle, _ := f.NewStyle(style) - err = f.SetCellStyle(sheetName, axis, axis, newStyle) - if err != nil { - return nil, err - } - - // 设置行高 35-第一行 - err = f.SetRowHeight(sheetName, r+2, 35) - if err != nil { - return nil, err - } - } - } - - buffer, err := f.WriteToBuffer() +// Export 导出 +func Export(header []HeaderCellData, data []interface{}) (b *bytes.Buffer, err error) { + sheetName := "Sheet1" + // 生成表格 + f, err := GenerateSheet(header, sheetName) if err != nil { return nil, err } - return buffer, nil + alignment := &excelize.Alignment{ + Horizontal: "center", + Vertical: "center", + } + + row := 1 + for _, item := range data { + var err error + row, err = fillDataWithMerge(f, sheetName, header, item, row, alignment) + if err != nil { + return nil, err + } + } + + //buffer, err := f.WriteToBuffer() + //if err != nil { + // return nil, err + //} + + //return buffer, nil // 保存文件 - // if err := f.SaveAs("output.xlsx"); err != nil { - // return nil, err - // } - // return nil, errors.New("已导出文件") + if err := f.SaveAs("output.xlsx"); err != nil { + return nil, err + } + return nil, errors.New("已导出文件") +} + +// 填充数据 +func fillDataWithMerge(f *excelize.File, sheetName string, header []HeaderCellData, data interface{}, row int, alignment *excelize.Alignment) (int, error) { + v := reflect.ValueOf(data) + + // 判断是否是结构体或指向结构体的指针 + if v.Kind() == reflect.Ptr { + v = v.Elem() + } + if v.Kind() != reflect.Struct { + panic("data must be a struct or a pointer to struct") + } + + var axis string // 坐标值 + + colOffset := 0 // 列偏移量 + mergeNum := 0 // 合并单元格数量。默认为0,每次+1,当存在切片时启用 + var mergeNumSlice []string // 需合并单元格列名。默认为空,当存在切片时启用["A","B"] + filedNumSlice := 1 // 切片数量,默认为1,此数量为其余需合并的数量 + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + + // 获取字段类型 + valueType := field.Kind() + if valueType != reflect.Slice { + // 获取字段值 + cellValue := field.Interface() + + // 获取列名 + colName, err := excelize.ColumnNumberToName(colOffset + 1) + if err != nil { + return 0, err + } + + // 获取坐标,+1表示去除header行 + axis = colName + fmt.Sprintf("%d", row+1) + + // 填入值 + err = f.SetCellValue(sheetName, axis, cellValue) + if err != nil { + return 0, err + } + + // 设置单元格样式 + err = setCellStyle(f, sheetName, axis, header[i], alignment) + if err != nil { + return 0, err + } + + // 设置行高 35-第一行 + err = f.SetRowHeight(sheetName, row+1, 35) + if err != nil { + return 0, err + } + + // 合并单元格数量 + mergeNum = mergeNum + 1 + mergeNumSlice = append(mergeNumSlice, colName) + } else { + var sliceFieldNum int // 切片字段数量 + + // 最大切片数量,此数量为其余需合并的数量 + if filedNumSlice < field.Len() { + filedNumSlice = field.Len() + } + + for j := 0; j < field.Len(); j++ { + item := field.Index(j).Interface() + v1 := reflect.ValueOf(item) + + for i2 := 0; i2 < field.Index(j).NumField(); i2++ { + // 获取字段值 + cellValue := v1.Field(i2).Interface() + + // 获取列名 + colName, err := excelize.ColumnNumberToName(i + i2 + 1) + if err != nil { + return 0, err + } + + // 获取坐标,+1表示去除header行 + axis = colName + fmt.Sprintf("%d", row+j+1) + + // 填入值 + err = f.SetCellValue(sheetName, axis, cellValue) + if err != nil { + return 0, err + } + + // 设置单元格样式 + err = setCellStyle(f, sheetName, axis, header[i+i2], alignment) + if err != nil { + return row, err + } + + // 设置行高 35-第一行 + err = f.SetRowHeight(sheetName, row+j+1, 35) + if err != nil { + return row, err + } + + sliceFieldNum = i2 + } + + } + + // 列偏移量需增加上切片字段数量 + colOffset = colOffset + sliceFieldNum + } + + colOffset++ + } + + // 合并单元格 + if filedNumSlice > 1 { + for _, s := range mergeNumSlice { + // 设置单元格样式 + for i := 0; i < filedNumSlice; i++ { + axis = s + fmt.Sprintf("%d", row+i+1) + err := setCellStyle(f, sheetName, axis, header[row+i], alignment) + if err != nil { + return row, err + } + } + + startCell := s + fmt.Sprintf("%d", row+1) + endCell := s + fmt.Sprintf("%d", row+filedNumSlice) + err := f.MergeCell(sheetName, startCell, endCell) + if err != nil { + return 0, err + } + } + } + + // 行数 = 切片数量,如不存在切片filedNumSlice默认为1 + row = row + filedNumSlice + + // 返回当前处理到的行数 + return row, nil +} + +// 设置单元格样式 +func setCellStyle(f *excelize.File, sheetName, axis string, header HeaderCellData, alignment *excelize.Alignment) error { + // 获取现有样式id + styleID, err := f.GetCellStyle(sheetName, axis) + if err != nil { + return err + } + + // 处理样式问题 + var style *excelize.Style + if styleID == 0 { + // 新样式 + style = &excelize.Style{ + Alignment: alignment, + } + } else { + // 存在现有样式,直接获取 + style, err = f.GetStyle(styleID) + if err != nil { + return err + } + } + + if header.CellType == "float" { + style.NumFmt = 2 + customNumFmt := "0.000" + style.CustomNumFmt = &customNumFmt + } + + if header.CellType == "date" { + style.NumFmt = 22 + customNumFmt := "yyyy-mm-dd hh:mm:ss" + style.CustomNumFmt = &customNumFmt + } + + // 颜色-可用,但是目前无需使用 + if header.Colour != "" { + // 单元格颜色 + //fill := excelize.Fill{ + // Type: "pattern", + // Pattern: 1, + // Color: []string{header.Colour}, + // Shading: 0, + //} + // + //style.Fill = fill + // + //// 字体颜色 + //font := &excelize.Font{ + // Color: header.Colour, + //} + // + //style.Font = font + } + + // 边框 + border := []excelize.Border{ + {Type: "left", Color: "#000000", Style: 1}, + {Type: "right", Color: "#000000", Style: 1}, + {Type: "bottom", Color: "#000000", Style: 1}, + } + + style.Border = border + + newStyle, err := f.NewStyle(style) + if err != nil { + return err + } + + if err := f.SetCellStyle(sheetName, axis, axis, newStyle); err != nil { + return err + } + + return nil } diff --git a/utils/exportbak.go b/utils/exportbak.go new file mode 100644 index 0000000..e6557a5 --- /dev/null +++ b/utils/exportbak.go @@ -0,0 +1,298 @@ +package utils + +import ( + "bytes" + "fmt" + "github.com/xuri/excelize/v2" + "reflect" + "strconv" +) + +// func Export(widths []int) (bool, error) { +// f := excelize.NewFile() +// defer func() { +// _ = f.Close() +// }() +// +// // 创建一个工作表 +// index, err := f.NewSheet("Sheet1") +// if err != nil { +// return false, err +// } +// +// // 设置工作簿的默认工作表 +// f.SetActiveSheet(index) +// +// // 单元格对齐样式 +// alignment := &excelize.Alignment{ +// Horizontal: "center", +// Vertical: "center", +// } +// +// // 单元格颜色填充样式 +// fill := excelize.Fill{ +// Type: "pattern", +// Pattern: 1, +// Color: []string{"#c9daf8"}, +// Shading: 0, +// } +// +// // 第一行的左、右、下边框 +// border := []excelize.Border{ +// { +// Type: "left,right,bottom", +// Color: "", +// Style: 1, +// }, +// } +// +// // 工作表样式 +// style, err := f.NewStyle( +// &excelize.Style{ +// Fill: fill, +// Alignment: alignment, +// Border: border, +// }, +// ) +// if err != nil { +// return false, err +// } +// +// // 依次设置每一列的列宽 +// widths = []int{18, 18, 18, 18, 18, 20, 23, 46, 18, 30, 30, 18, 18, 30, 18, 30} +// for col, width := range widths { +// // 获取列名 +// colName, err := excelize.ColumnNumberToName(col + 1) +// if err != nil { +// return false, err +// } +// +// // 设置列宽 +// err = f.SetColWidth("Sheet1", colName, colName, float64(width)) +// if err != nil { +// return false, err +// } +// +// // 设置列背景颜色 +// err = f.SetCellStyle("Sheet1", colName+"1", colName+"1", style) +// if err != nil { +// return false, err +// } +// } +// +// // 设置行高 +// err = f.SetRowStyle("Sheet1", 1, 10, style) +// if err != nil { +// return false, err +// } +// +// if err := f.SaveAs("output.xlsx"); err != nil { +// return false, err +// } +// +// return true, nil +// } + +// HeaderCellData1 表头内容 +type HeaderCellData1 struct { + Value string // 值 + CellType string // 类型 + NumberFmt string // 格式化方式 + ColWidth int // 列宽 +} + +func Export2(header []HeaderCellData1, data []interface{}) (*bytes.Buffer, error) { + sheetName := "Sheet1" + + f := excelize.NewFile() + defer func() { + _ = f.Close() + }() + + // 创建一个工作表 + index, err := f.NewSheet(sheetName) + if err != nil { + return nil, err + } + + // 设置工作簿的默认工作表 + f.SetActiveSheet(index) + + // 设置工作表默认字体 + err = f.SetDefaultFont("宋体") + if err != nil { + return nil, err + } + + // 统一单元格对齐样式 + alignment := &excelize.Alignment{ + Horizontal: "center", + Vertical: "center", + } + + // 设置行高 35-第一行 + err = f.SetRowHeight(sheetName, 1, 35) + if err != nil { + return nil, err + } + + // 处理工作表表头 + for c, cell := range header { + // 获取列名 + colName, err := excelize.ColumnNumberToName(c + 1) + if err != nil { + return nil, err + } + + // 添加单元格的值 + err = f.SetCellValue(sheetName, colName+"1", cell.Value) + if err != nil { + return nil, err + } + + // 单元格颜色填充样式 + fill := excelize.Fill{ + Type: "pattern", + Pattern: 1, + Color: []string{"#c9daf8"}, + Shading: 0, + } + + // 第一行的左、右、下边框 + border := []excelize.Border{ + { + Type: "left", + Color: "#000000", + Style: 1, + }, + { + Type: "right", + Color: "#000000", + Style: 1, + }, + { + Type: "bottom", + Color: "#000000", + Style: 1, + }, + } + + // 设置单元格值类型和格式 + style, _ := f.NewStyle(&excelize.Style{ + Alignment: alignment, // 字体居中 + Fill: fill, // 背景颜色 + Border: border, // 边框 + }) + err = f.SetCellStyle(sheetName, colName+"1", colName+"1", style) + if err != nil { + return nil, err + } + + // 设置列宽 + err = f.SetColWidth(sheetName, colName, colName, float64(cell.ColWidth)) + if err != nil { + return nil, err + } + } + + // 设置单元格格式 + row := len(data) + for i, cell := range header { + // 获取列名 + colName, err := excelize.ColumnNumberToName(i + 1) + if err != nil { + return nil, err + } + + // 字体居中 + style := &excelize.Style{} + style = &excelize.Style{ + Alignment: alignment, + } + + if cell.CellType == "float" { + style.NumFmt = 2 + customNumFmt := "0.000" + style.CustomNumFmt = &customNumFmt + } + + if cell.CellType == "date" { + style.NumFmt = 22 + customNumFmt := "yyyy-mm-dd hh:mm:ss" + style.CustomNumFmt = &customNumFmt + } + + newStyle, _ := f.NewStyle(style) + err = f.SetCellStyle(sheetName, colName+"2", colName+strconv.Itoa(row), newStyle) + if err != nil { + return nil, err + } + } + + // 填充数据 + for r, rowData := range data { + rv := reflect.ValueOf(rowData) + for c := 0; c < rv.NumField(); c++ { + cellValue := rv.Field(c).Interface() + // 获取列名 + colName, err := excelize.ColumnNumberToName(c + 1) + if err != nil { + return nil, err + } + + axis := colName + fmt.Sprintf("%d", r+2) + + // 设置单元格值 + err = f.SetCellValue(sheetName, axis, cellValue) + if err != nil { + return nil, err + } + + // 设置单元格值类型 + cellType := header[c].CellType + + // 字体居中 + style := &excelize.Style{} + style = &excelize.Style{ + Alignment: alignment, + } + + if cellType == "float" { + style.NumFmt = 2 + customNumFmt := "0.000" + style.CustomNumFmt = &customNumFmt + } + + if cellType == "date" { + style.NumFmt = 22 + customNumFmt := "yyyy-mm-dd hh:mm:ss" + style.CustomNumFmt = &customNumFmt + } + + newStyle, _ := f.NewStyle(style) + err = f.SetCellStyle(sheetName, axis, axis, newStyle) + if err != nil { + return nil, err + } + + // 设置行高 35-第一行 + err = f.SetRowHeight(sheetName, r+2, 35) + if err != nil { + return nil, err + } + } + } + + buffer, err := f.WriteToBuffer() + if err != nil { + return nil, err + } + + return buffer, nil + + // 保存文件 + // if err := f.SaveAs("output.xlsx"); err != nil { + // return nil, err + // } + // return nil, errors.New("已导出文件") +}