diff --git a/api/controller/export.go b/api/controller/export.go index 6af2ce4..d666d9d 100644 --- a/api/controller/export.go +++ b/api/controller/export.go @@ -1,11 +1,11 @@ package controller import ( - "fmt" "github.com/gin-gonic/gin" "hospital-admin-api/api/dao" "hospital-admin-api/api/requests" "hospital-admin-api/api/responses" + "hospital-admin-api/api/service" "hospital-admin-api/global" "hospital-admin-api/utils" ) @@ -28,44 +28,20 @@ func (r *Export) DoctorWithdrawal(c *gin.Context) { return } - if req.Page == 0 { - req.Page = 1 - } - - if req.PageSize == 0 { - req.PageSize = 20 - } - doctorWithdrawalDao := dao.DoctorWithdrawalDao{} - doctorWithdrawal, total, err := doctorWithdrawalDao.GetDoctorWithdrawalExportListSearch(req, req.Page, req.PageSize) + doctorWithdrawals, err := doctorWithdrawalDao.GetDoctorWithdrawalExportListSearch(req) if err != nil { responses.FailWithMessage(err.Error(), c) return } - fmt.Println(doctorWithdrawal) - fmt.Println(total) - - header := []utils.CellData{ - {Value: "医生姓名", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "医生手机号", CellType: "string", NumberFmt: "", ColWidth: 18}, - {Value: "申请提现金额", CellType: "float", NumberFmt: "0.0000", ColWidth: 18}, - {Value: "审核日期", CellType: "date", NumberFmt: "yyyy-mm-dd hh:mm:ss", ColWidth: 30}, - } - - _, err = utils.Export(header) + // 业务处理 + exportService := service.ExportService{} + ossAddress, err := exportService.DoctorWithdrawal(doctorWithdrawals) if err != nil { responses.FailWithMessage(err.Error(), c) return } - // 处理返回值 - // res := dto.GetDoctorWithdrawalListDto(doctorWithdrawal) - // - // result := make(map[string]interface{}) - // result["page"] = req.Page - // result["page_size"] = req.PageSize - // result["total"] = total - // result["data"] = res - responses.Ok(c) + responses.OkWithData(ossAddress, c) } diff --git a/api/dao/doctorWithdrawal.go b/api/dao/doctorWithdrawal.go index a6e7beb..a03f234 100644 --- a/api/dao/doctorWithdrawal.go +++ b/api/dao/doctorWithdrawal.go @@ -163,9 +163,7 @@ func (r *DoctorWithdrawalDao) GetDoctorWithdrawalPageSearch(req requests.GetDoct } // GetDoctorWithdrawalExportListSearch 获取医生提现列表-导出 -func (r *DoctorWithdrawalDao) GetDoctorWithdrawalExportListSearch(req requests.DoctorWithdrawalExportList, page, pageSize int) (m []*model.DoctorWithdrawal, total int64, err error) { - var totalRecords int64 - +func (r *DoctorWithdrawalDao) GetDoctorWithdrawalExportListSearch(req requests.DoctorWithdrawalExportList) (m []*model.DoctorWithdrawal, err error) { // 构建查询条件 query := global.Db.Model(&model.DoctorWithdrawal{}) @@ -174,6 +172,14 @@ func (r *DoctorWithdrawalDao) GetDoctorWithdrawalExportListSearch(req requests.D return db.Omit("open_id", "union_id", "wx_session_key") }) + // 用户-医生 + query = query.Preload("UserDoctor.User", func(db *gorm.DB) *gorm.DB { + return db.Omit("user_password", "salt") + }) + + // 医生详情 + query = query.Preload("UserDoctor.UserDoctorInfo") + // 提现关联银行卡 query = query.Preload("DoctorWithdrawalBank") @@ -249,7 +255,7 @@ func (r *DoctorWithdrawalDao) GetDoctorWithdrawalExportListSearch(req requests.D // 当前选择数据 if req.Type == 2 { if req.Id == "" { - return nil, 0, errors.New("未提供需导出数据编号") + return nil, errors.New("未提供需导出数据编号") } id := strings.Split(req.Id, ",") @@ -259,14 +265,9 @@ func (r *DoctorWithdrawalDao) GetDoctorWithdrawalExportListSearch(req requests.D // 排序 query = query.Order("created_at desc") - // 查询总数量 - if err := query.Count(&totalRecords).Error; err != nil { - return nil, 0, err - } - - err = query.Scopes(model.Paginate(page, pageSize)).Find(&m).Error + err = query.Find(&m).Error if err != nil { - return nil, 0, err + return nil, err } - return m, totalRecords, nil + return m, nil } diff --git a/api/requests/doctorWithdrawal.go b/api/requests/doctorWithdrawal.go index 322f7a4..4e6f0fb 100644 --- a/api/requests/doctorWithdrawal.go +++ b/api/requests/doctorWithdrawal.go @@ -41,9 +41,7 @@ type PutDoctorWithdrawalExamine struct { // DoctorWithdrawalExportList 提现记录列表-导出 type DoctorWithdrawalExportList struct { Type int `json:"type" form:"type" label:"类型" validate:"required,oneof=1 2 3"` // 1:当前搜索数据 2:当前选择数据 3:全部数据 - Id string `json:"id" form:"id" label:"id"` // 选择数据的id,当type为2时必填 - Page int `json:"page" form:"page" label:"页码"` - PageSize int `json:"page_size" form:"page_size" label:"每页个数"` + Id string `json:"id" form:"id" label:"id"` // 选择数据的id,逗号分隔,当type为2时必填 Mobile string `json:"mobile" form:"mobile" label:"手机号"` UserName string `json:"user_name" form:"user_name" label:"用户名"` ExamineTime string `json:"examine_time" form:"examine_time" label:"审核日期"` // 时间区间,数组形式,下标0为开始时间,下标1为结束时间 diff --git a/api/router/router.go b/api/router/router.go index dc2aa7d..c5a23d8 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -592,60 +592,60 @@ func privateRouter(r *gin.Engine, api controller.Api) { } } - // // 导出管理 - // exportGroup := adminGroup.Group("/export") - // { - // // 提现 - // withdrawalGroup := exportGroup.Group("/withdrawal") - // { - // // 提现记录 - // withdrawalGroup.POST("", api.Export.DoctorWithdrawal) - // - // // 提现记录-关联订单 - // withdrawalGroup.POST("/order", api.UserCaCert.RenewUserCloudCert) - // } - // - // // 医生 - // doctorGroup := exportGroup.Group("/doctor") - // { - // // 医生列表 - // doctorGroup.POST("", api.UserCaCert.RenewUserCloudCert) - // - // // 医生银行卡 - // doctorGroup.POST("/bank/card", api.UserCaCert.RenewUserCloudCert) - // - // // 医生账户 - // doctorGroup.POST("/account", api.UserCaCert.RenewUserCloudCert) - // - // // 医生账户-关联订单 - // doctorGroup.POST("/account/order", api.UserCaCert.RenewUserCloudCert) - // } - // - // // 患者 - // patientGroup := exportGroup.Group("/patient") - // { - // // 患者列表 - // patientGroup.POST("", api.UserCaCert.RenewUserCloudCert) - // - // // 就诊人列表 - // patientGroup.POST("/family", api.UserCaCert.RenewUserCloudCert) - // } - // - // // 订单 - // orderGroup := exportGroup.Group("/order") - // { - // // 问诊订单 - // orderGroup.POST("/inquiry", api.UserCaCert.RenewUserCloudCert) - // - // // 药品订单 - // orderGroup.POST("/product", api.UserCaCert.RenewUserCloudCert) - // } - // - // // 处方 - // prescriptionGroup := exportGroup.Group("/prescription") - // { - // // 处方 - // prescriptionGroup.POST("", api.UserCaCert.RenewUserCloudCert) - // } - // } + // 导出管理 + exportGroup := adminGroup.Group("/export") + { + // 提现 + withdrawalGroup := exportGroup.Group("/withdrawal") + { + // 提现记录 + withdrawalGroup.POST("", api.Export.DoctorWithdrawal) + + // 提现记录-关联订单 + withdrawalGroup.POST("/order", api.UserCaCert.RenewUserCloudCert) + } + + // 医生 + doctorGroup := exportGroup.Group("/doctor") + { + // 医生列表 + doctorGroup.POST("", api.UserCaCert.RenewUserCloudCert) + + // 医生银行卡 + doctorGroup.POST("/bank/card", api.UserCaCert.RenewUserCloudCert) + + // 医生账户 + doctorGroup.POST("/account", api.UserCaCert.RenewUserCloudCert) + + // 医生账户-关联订单 + doctorGroup.POST("/account/order", api.UserCaCert.RenewUserCloudCert) + } + + // 患者 + patientGroup := exportGroup.Group("/patient") + { + // 患者列表 + patientGroup.POST("", api.UserCaCert.RenewUserCloudCert) + + // 就诊人列表 + patientGroup.POST("/family", api.UserCaCert.RenewUserCloudCert) + } + + // 订单 + orderGroup := exportGroup.Group("/order") + { + // 问诊订单 + orderGroup.POST("/inquiry", api.UserCaCert.RenewUserCloudCert) + + // 药品订单 + orderGroup.POST("/product", api.UserCaCert.RenewUserCloudCert) + } + + // 处方 + prescriptionGroup := exportGroup.Group("/prescription") + { + // 处方 + prescriptionGroup.POST("", api.UserCaCert.RenewUserCloudCert) + } + } } diff --git a/api/service/export.go b/api/service/export.go index b1d3f92..00c2964 100644 --- a/api/service/export.go +++ b/api/service/export.go @@ -1,15 +1,188 @@ package service import ( + "fmt" + "hospital-admin-api/api/dao" "hospital-admin-api/api/model" + "hospital-admin-api/extend/aliyun" + "hospital-admin-api/utils" + "math/rand" + "time" ) // ExportService 导出 type ExportService struct { } -// DoctorWithdrawal 提现记录 -func (r *ExportService) DoctorWithdrawal([]*model.DoctorWithdrawal) (string, error) { - - return "", nil +// DoctorWithdrawalData 提现记录数据 +type DoctorWithdrawalData struct { + WithdrawalId string // 提现编号 + DoctorName string // 医生姓名 + Mobile string // 医生手机号 + DoctorCardNum string // 医生证件号码 + AppliedWithdrawalAmount float64 // 提现金额 + ActualWithdrawalAmount float64 // 实际提现金额 + IncomeTax float64 // 提现所得税金额 + BankName string // 提现银行名称 + BankCardCode string // 银行卡号 + BankCardAddress string // 银行地址 + ExamineStatus string // 审核状态(1:审核中 2:审核通过 3:审核未通过) + ExamineFailReason string // 审核失败原因 + ExamineTime time.Time // 审核日期 + ExamineBy string // 审核人员名称 + PaymentStatus string // 财务打款状态(0:否 1:是) + PaymentTime time.Time // 财务打款时间 + PaymentBy string // 财务打款人员id(后台用户id) + CreatedAt time.Time // 创建时间 +} + +// DoctorWithdrawal 提现记录 +func (r *ExportService) DoctorWithdrawal(doctorWithdrawals []*model.DoctorWithdrawal) (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: 25}, + {Value: "申请提现金额", CellType: "float", NumberFmt: "0.0000", ColWidth: 18}, + {Value: "实际提现金额", CellType: "float", NumberFmt: "0.0000", ColWidth: 18}, + {Value: "个人所得税", CellType: "float", NumberFmt: "0.0000", ColWidth: 18}, + {Value: "银行名称", CellType: "string", NumberFmt: "", ColWidth: 25}, + {Value: "银行卡号", CellType: "string", NumberFmt: "", ColWidth: 27}, + {Value: "银行地址", CellType: "string", NumberFmt: "", ColWidth: 46}, + {Value: "审核状态", CellType: "string", NumberFmt: "", ColWidth: 18}, + {Value: "审核失败原因", CellType: "string", NumberFmt: "", ColWidth: 33}, + {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: "date", NumberFmt: "yyyy-mm-dd hh:mm:ss", ColWidth: 30}, + } + + var interfaceSlice []interface{} + for _, v := range doctorWithdrawals { + // 审核状态 + examineStatus := "未知" + if v.ExamineStatus == 1 { + examineStatus = "审核中" + } else if v.ExamineStatus == 2 { + examineStatus = "审核通过" + } else if v.ExamineStatus == 3 { + examineStatus = "审核未通过" + } + + // 审核人员 + examineBy := "" + if v.ExamineBy != 0 { + adminUserDao := dao.AdminUserDao{} + adminUser, err := adminUserDao.GetAdminUserFirstById(v.ExamineBy) + if err == nil && adminUser != nil { + examineBy = adminUser.NickName + } + } + + // 打款状态 + paymentStatus := "未知" + if v.PaymentStatus == 0 { + paymentStatus = "未打款" + } else if v.ExamineStatus == 1 { + paymentStatus = "已打款" + } + + // 打款人员 + paymentBy := "" + if v.PaymentBy != 0 { + adminUserDao := dao.AdminUserDao{} + adminUser, err := adminUserDao.GetAdminUserFirstById(v.PaymentBy) + if err == nil && adminUser != nil { + paymentBy = adminUser.NickName + } + } + + // 银行名称 + bankName := "未知" + if v.DoctorWithdrawalBank != nil { + if v.DoctorWithdrawalBank.BasicBank != nil { + if v.DoctorWithdrawalBank.BasicBank.BankName != "" { + bankName = v.DoctorWithdrawalBank.BasicBank.BankName + } + } + } + + // 银行卡号 + bankCardCode := "未知" + + // 开户行地址 + bankCardAddress := "" + + if v.DoctorWithdrawalBank != nil { + if v.DoctorWithdrawalBank.BankCardCode != "" { + bankCardCode = v.DoctorWithdrawalBank.BankCardCode + } + + bankCardAddress = v.DoctorWithdrawalBank.Province + v.DoctorWithdrawalBank.City + v.DoctorWithdrawalBank.County + } + + // 时间处理 + var examineTime time.Time + if v.ExamineTime != (model.LocalTime{}) { + t := time.Time(v.ExamineTime) + examineTime = t + } + + var paymentTime time.Time + if v.PaymentTime != (model.LocalTime{}) { + t := time.Time(v.PaymentTime) + paymentTime = t + } + + var createdAt time.Time + if v.CreatedAt != (model.LocalTime{}) { + t := time.Time(v.CreatedAt) + createdAt = t + } + + doctorWithdrawalData := DoctorWithdrawalData{ + WithdrawalId: fmt.Sprintf("%d", v.WithdrawalId), + DoctorName: v.UserDoctor.UserName, + Mobile: v.UserDoctor.User.Mobile, + DoctorCardNum: v.UserDoctor.UserDoctorInfo.CardNum, + AppliedWithdrawalAmount: v.AppliedWithdrawalAmount, + ActualWithdrawalAmount: v.ActualWithdrawalAmount, + IncomeTax: v.IncomeTax, + BankName: bankName, + BankCardCode: bankCardCode, + BankCardAddress: bankCardAddress, + ExamineStatus: examineStatus, + ExamineFailReason: v.ExamineFailReason, + ExamineTime: examineTime, + ExamineBy: examineBy, + PaymentStatus: paymentStatus, + PaymentTime: paymentTime, + PaymentBy: paymentBy, + CreatedAt: createdAt, + } + + interfaceSlice = append(interfaceSlice, doctorWithdrawalData) + } + + file, err := utils.Export(header, interfaceSlice) + if err != nil { + return "", err + } + + // 设置文件名字 + now := time.Now() + dateTimeString := now.Format("20060102150405") // 当前时间字符串 + rand.Seed(time.Now().UnixNano()) // 设置随机数 + ossPath := "admin/export/output" + dateTimeString + fmt.Sprintf("%d", rand.Intn(9000)+1000) + ".xlsx" + + // 上传oss + _, err = aliyun.PutObjectByte(ossPath, file.Bytes()) + if err != nil { + return "", err + } + + ossPath = utils.AddOssDomain("/" + ossPath) + return ossPath, nil } diff --git a/extend/aliyun/oss.go b/extend/aliyun/oss.go index d2c689b..dcc4834 100644 --- a/extend/aliyun/oss.go +++ b/extend/aliyun/oss.go @@ -1,6 +1,7 @@ package aliyun import ( + "bytes" "crypto/hmac" "crypto/sha1" "encoding/base64" @@ -163,3 +164,23 @@ func GetObjectToLocal(filename, local string) (bool, error) { return true, nil } + +// PutObjectByte 上传文件 +func PutObjectByte(filename string, content []byte) (bool, error) { + ossClient, err := CreateClient() + if err != nil { + return false, err + } + + // yourBucketName填写存储空间名称。 + bucket, err := ossClient.Bucket(config.C.Oss.OssBucket) + if err != nil { + return false, err + } + + err = bucket.PutObject(filename, bytes.NewReader(content)) + if err != nil { + return false, err + } + return true, nil +} diff --git a/utils/export.go b/utils/export.go index 9461c48..50a43e5 100644 --- a/utils/export.go +++ b/utils/export.go @@ -1,7 +1,11 @@ package utils import ( + "bytes" + "fmt" "github.com/xuri/excelize/v2" + "reflect" + "strconv" ) // func Export(widths []int) (bool, error) { @@ -89,22 +93,24 @@ import ( // return true, nil // } -// CellData 列内容 -type CellData struct { - Value interface{} // 值 - CellType string // 类型 - NumberFmt string // 格式化方式 - ColWidth int // 列宽 +// HeaderCellData 表头内容 +type HeaderCellData struct { + Value string // 值 + CellType string // 类型 + NumberFmt string // 格式化方式 + ColWidth int // 列宽 } -func Export(header []CellData) ([]byte, error) { +func Export(header []HeaderCellData, data []interface{}) (*bytes.Buffer, error) { + sheetName := "Sheet1" + f := excelize.NewFile() defer func() { _ = f.Close() }() // 创建一个工作表 - index, err := f.NewSheet("Sheet1") + index, err := f.NewSheet(sheetName) if err != nil { return nil, err } @@ -125,7 +131,7 @@ func Export(header []CellData) ([]byte, error) { } // 设置行高 35-第一行 - err = f.SetRowHeight("Sheet1", 1, 35) + err = f.SetRowHeight(sheetName, 1, 35) if err != nil { return nil, err } @@ -139,7 +145,7 @@ func Export(header []CellData) ([]byte, error) { } // 添加单元格的值 - err = f.SetCellValue("Sheet1", colName+"1", cell.Value) + err = f.SetCellValue(sheetName, colName+"1", cell.Value) if err != nil { return nil, err } @@ -177,37 +183,114 @@ func Export(header []CellData) ([]byte, error) { Fill: fill, // 背景颜色 Border: border, // 边框 }) - err = f.SetCellStyle("Sheet1", colName+"1", colName+"1", style) - if err != nil { - return nil, err - } - - } - - // 处理工作表设置 - for c, cell := range header { - // 获取列名 - colName, err := excelize.ColumnNumberToName(c + 1) + err = f.SetCellStyle(sheetName, colName+"1", colName+"1", style) if err != nil { return nil, err } // 设置列宽 - err = f.SetColWidth("Sheet1", colName, colName, float64(cell.ColWidth)) + err = f.SetColWidth(sheetName, colName, colName, float64(cell.ColWidth)) if err != nil { return nil, err } } - // 保存文件 - if err := f.SaveAs("output.xlsx"); 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 + } } - // buffer, err := f.WriteToBuffer() - // if err != nil { + + // 填充数据 + 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 + } + } + } + + // 保存文件 + // if err := f.SaveAs("output.xlsx"); err != nil { // return nil, err // } - // - // buffer.Bytes() - return nil, nil + buffer, err := f.WriteToBuffer() + if err != nil { + return nil, err + } + + return buffer, nil }