mh_goadmin_server/app/admin/models/repair.go
chenlin c07d7d6722 1、新增配合商场检查的相关接口;
2、修改短信提醒定时任务,修改文案添加门店联系方式,并将发送的短信记录到数据库;
3、新增维修明细接口;
4、门店表新增"固定电话"landline字段;
5、短信记录表增加"短信类型"sms_type字段;
6、维修订单表增加express_type、express_fee字段;
2025-06-19 15:28:30 +08:00

958 lines
32 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package models
import (
"errors"
"fmt"
"github.com/gin-gonic/gin"
"github.com/xuri/excelize/v2"
orm "go-admin/common/global"
"go-admin/logger"
"go-admin/tools"
"go-admin/tools/config"
"gorm.io/gorm"
"math/rand"
"time"
)
const (
ErpRepairOrderUnAudit = 1 // 待审核
ErpRepairOrderWaitRepair = 2 // 待送修
ErpRepairOrderBingRepaired = 3 // 维修中
ErpRepairOrderFinished = 4 // 已完成
)
type RepairRecord struct {
Model
SerialNumber string `json:"serial_number"` // 订单编号
Uid uint32 `json:"uid"` // 用户ID
Tel string `json:"tel"` // 手机号
MemberLevel uint32 `json:"member_level"` // 会员等级1-普通用户6-尊享会员
StoreId uint32 `json:"store_id"` // 门店ID
StoreName string `json:"store_name" gorm:"-"` // 门店名称
HandlerId uint32 `json:"handler_id"` // 经手人ID
HandlerName string `json:"handler_name"` // 经手人名称
State uint8 `json:"state"` // 状态1-待审核2-待送修3-维修中4-已完成)
MakerId uint32 `json:"maker_id"` // 制单人ID
MakerName string `json:"maker_name"` // 制单人名称
MakerTime *time.Time `json:"maker_time"` // 制单时间
AuditorId uint32 `json:"auditor_id"` // 审核人ID
AuditorName string `json:"auditor_name"` // 审核人名称
AuditTime *time.Time `json:"audit_time"` // 审核时间
SendTime *time.Time `json:"send_time"` // 送修时间
SendRemark string `json:"send_remark"` // 送修备注
ExpressType uint8 `json:"express_type"` // 邮寄方1-个人2-门店
ExpressNo string `json:"express_no"` // 快递单号
ExpressFee float64 `json:"express_fee"` // 快递费用
RepairUnitId uint32 `json:"repair_unit_id"` // 维修单位ID供应商
RepairUnitName string `json:"repair_unit_name" gorm:"-"` // 维修单位名称(供应商)
RepairFee float64 `json:"repair_fee"` // 维修费用
CompletedTime *time.Time `json:"completed_time"` // 完成时间
CompleteRemark string `json:"complete_remark"` // 完成备注
Commodities []RepairRecordCommodity `json:"commodities" gorm:"-"` // 维修商品信息
}
type RepairRecordCommodity struct {
Model
RepairRecordId uint32 `json:"repair_record_id"` // 维修记录ID
CommodityId uint32 `json:"commodity_id"` // 商品ID
CommodityName string `json:"commodity_name"` // 商品名称
OldIMEI string `json:"old_imei"` // 旧机器编码
NewIMEI string `json:"new_imei"` // 新机器编码
Count uint32 `json:"count"` // 数量
FaultImage string `json:"fault_image"` // 故障图片
FaultDesc string `json:"fault_desc"` // 故障描述
}
type RepairCreateReq struct {
Uid uint32 `json:"uid"`
Tel string `json:"tel"`
StoreId uint32 `json:"store_id" binding:"required"`
HandlerId uint32 `json:"handler_id" binding:"required"` // 经手人id
HandlerName string `json:"handler_name"` // 经手人名称
Commodities []RepairRecordCommodity `json:"commodities"`
}
type RepairEditReq struct {
SerialNumber string `json:"serial_number" binding:"required"` // 订单编号
Uid uint32 `json:"uid"` // 用户ID
Tel string `json:"tel"` // 手机号
MemberLevel uint32 `json:"member_level"` // 会员等级:普通用户、尊享会员
StoreId uint32 `json:"store_id" binding:"required"` // 门店ID
HandlerId uint32 `json:"handler_id" binding:"required"` // 经手人ID
HandlerName string `json:"handler_name"` // 经手人名称
State uint8 `json:"state"` // 状态1-待审核2-已审核3-维修中4-已完成)
MakerId uint32 `json:"maker_id" binding:"required"` // 制单人ID
MakerName string `json:"maker_name"` // 制单人名称
MakerTime *time.Time `json:"maker_time"` // 制单时间
AuditorId uint32 `json:"auditor_id"` // 审核人ID
AuditorName string `json:"auditor_name"` // 审核人名称
AuditTime *time.Time `json:"audit_time"` // 审核时间
SendTime *time.Time `json:"send_time"` // 送修时间
SendRemark string `json:"send_remark"` // 送修备注
ExpressNo string `json:"express_no"` // 快递单号
RepairUnitId uint32 `json:"repair_unit_id"` // 维修单位ID
RepairFee float64 `json:"repair_fee"` // 维修费用
CompletedTime *time.Time `json:"completed_time"` // 完成时间
CompleteRemark string `json:"complete_remark"` // 完成备注
Commodities []RepairRecordCommodity `json:"commodities"`
}
// ErpRepairDeleteReq 删除维修订单入参
type ErpRepairDeleteReq struct {
SerialNumber string `json:"serial_number" binding:"required"` // 单据编号
}
// ErpRepairOrderListReq 查询维修订单列表入参
type ErpRepairOrderListReq struct {
SerialNumber string `json:"serial_number"` // 单据编号
Uid uint32 `json:"uid"` // 用户ID
Tel string `json:"tel"` // 手机号
MemberLevel uint32 `json:"member_level"` // 会员等级:普通用户、尊享会员
StoreId []uint32 `json:"store_id"` // 门店id
HandlerId uint32 `json:"handler_id"` // 经手人id
State []uint32 `json:"state"` // 状态1-待审核2-已审核3-维修中4-已完成
RepairUnitId uint32 `json:"repair_unit_id"` // 维修单位ID供应商
MakeTimeStart string `json:"make_time_start"` // 制单开始时间
MakeTimeEnd string `json:"make_time_end"` // 制单结束时间
AuditTimeStart string `json:"audit_time_start"` // 审核开始时间
AuditTimeEnd string `json:"audit_time_end"` // 审核结束时间
SendTimeStart string `json:"send_time_start"` // 送修开始时间
SendTimeEnd string `json:"send_time_end"` // 送修结束时间
CompleteTimeStart string `json:"complete_time_start"` // 维修完成开始时间
CompleteTimeEnd string `json:"complete_time_end"` // 维修完成结束时间
PageIndex int `json:"pageIndex"` // 页码
PageSize int `json:"pageSize"` // 页面条数
}
// ErpRepairOrderListResp 查询维修订单列表出参
type ErpRepairOrderListResp struct {
List []RepairRecord `json:"list"`
Total int `json:"total"` // 总条数
PageIndex int `json:"pageIndex"` // 页码
PageSize int `json:"pageSize"` // 页面条数
}
// ErpRepairDetailReq 查询维修订单详情入参
type ErpRepairDetailReq struct {
SerialNumber string `json:"serial_number" binding:"required"` // 单据编号
}
// ErpRepairAuditReq 审核维修订单入参
type ErpRepairAuditReq struct {
SerialNumber string `json:"serial_number" binding:"required"` // 单据编号
State int `json:"state" binding:"required"` // 审核操作: 1-审核 2-取消审核
}
// ErpRepairSendReq 维修订单送修入参
type ErpRepairSendReq struct {
SerialNumber string `json:"serial_number" binding:"required"` // 单据编号
RepairUnitId uint32 `json:"repair_unit_id" binding:"required"` // 维修单位ID供应商
ExpressType uint8 `json:"express_type" binding:"required"` // 邮寄方1-个人2-门店
ExpressNo string `json:"express_no"` // 快递单号
ExpressFee float64 `json:"express_fee"` // 快递费用
SendRemark string `json:"send_remark"` // 送修备注
}
// ErpRepairCompleteReq 维修订单完成入参
type ErpRepairCompleteReq struct {
SerialNumber string `json:"serial_number" binding:"required"` // 单据编号
RepairFee float64 `json:"repair_fee"` // 维修费用
CompleteRemark string `json:"complete_remark"` // 完成备注
}
// NewRepairBillSn 生成维修订单号
func NewRepairBillSn() string {
nowTime := time.Now()
rand.Seed(nowTime.UnixNano())
max := 1
for {
if max > 5 {
logger.Error("create sn err")
return ""
}
random := rand.Int31n(9999) + 1000
sn := fmt.Sprintf("wx%s%d", nowTime.Format("060102"), random)
exist, err := QueryRecordExist(fmt.Sprintf("SELECT * FROM repair_record WHERE serial_number='%s'", sn))
if err != nil {
logger.Error("exist sn err")
}
if !exist {
return sn
}
max++
}
}
// CreateRepairOrder 创建维修订单
func CreateRepairOrder(req *RepairCreateReq, sysUser *SysUser) (*RepairRecord, error) {
// 开始事务
tx := orm.Eloquent.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
return
}
}()
userMemberLevel := 1
// 判断是否为尊享会员
if IsValidPrivilegeMember(req.Uid) {
userMemberLevel = 6
}
nowTime := time.Now()
// 写入主表
repair := RepairRecord{
SerialNumber: NewRepairBillSn(),
Uid: req.Uid,
Tel: req.Tel,
MemberLevel: uint32(userMemberLevel),
StoreId: req.StoreId,
HandlerId: req.HandlerId,
HandlerName: req.HandlerName,
MakerId: uint32(sysUser.UserId),
MakerName: sysUser.NickName,
MakerTime: &nowTime,
State: ErpRepairOrderUnAudit, // 初始状态:待审核
}
if err := tx.Create(&repair).Error; err != nil {
tx.Rollback()
logger.Error("Create RepairRecord err:", logger.Field("err", err))
return nil, err
}
// 写入子表
for _, com := range req.Commodities {
item := RepairRecordCommodity{
RepairRecordId: repair.ID,
CommodityId: com.CommodityId,
CommodityName: com.CommodityName,
OldIMEI: com.OldIMEI,
NewIMEI: com.NewIMEI,
Count: com.Count,
FaultImage: com.FaultImage,
FaultDesc: com.FaultDesc,
}
if err := tx.Create(&item).Error; err != nil {
tx.Rollback()
logger.Error("Create RepairCommodity err:", logger.Field("err", err))
return nil, err
}
}
if err := tx.Commit().Error; err != nil {
tx.Rollback()
logger.Error("commit err:", logger.Field("err", err))
return nil, err
}
repair.Commodities = req.Commodities
return &repair, nil
}
// EditRepairOrder 编辑维修订单
func EditRepairOrder(req *RepairEditReq, sysUser *SysUser) (*RepairRecord, error) {
// 查询订单信息
var repairOrder RepairRecord
err := orm.Eloquent.Table("repair_record").Where("serial_number=?", req.SerialNumber).Find(&repairOrder).Error
if err != nil {
logger.Error("repair order err:", logger.Field("err", err))
return nil, err
}
if repairOrder.State != ErpRepairOrderUnAudit { // 只有待审核的订单才能编辑
return nil, errors.New("订单不是待审核状态")
}
begin := orm.Eloquent.Begin()
// 1-更新维修订单信息
repairOrder.Uid = req.Uid
repairOrder.Tel = req.Tel
repairOrder.MemberLevel = req.MemberLevel
repairOrder.StoreId = req.StoreId
repairOrder.HandlerId = req.HandlerId
repairOrder.HandlerName = req.HandlerName
repairOrder.MakerId = uint32(sysUser.UserId)
repairOrder.MakerName = sysUser.NickName
err = begin.Model(&RepairRecord{}).Where("serial_number=?", req.SerialNumber).
Omit("created_at").Save(repairOrder).Error
if err != nil {
begin.Rollback()
logger.Error("update erp_order err:", logger.Field("err", err))
return nil, err
}
// 2-更新维修订单商品表
err = updateRepairCommodityData(begin, repairOrder.ID, req)
if err != nil {
begin.Rollback()
logger.Error("update erp_purchase_commodity err:", logger.Field("err", err))
return nil, err
}
err = begin.Commit().Error
if err != nil {
begin.Rollback()
logger.Error("commit err:", logger.Field("err", err))
return nil, err
}
return &repairOrder, nil
}
// updateRepairCommodityData 更新维修订单商品信息
func updateRepairCommodityData(gdb *gorm.DB, orderId uint32, req *RepairEditReq) error {
// 查询现有的零售订单信息
var commodities []RepairRecordCommodity
err := orm.Eloquent.Table("repair_record_commodity").Where("repair_record_id = ?", orderId).Find(&commodities).Error
if err != nil {
logger.Error("query repair_record_commodity err:", logger.Field("err", err))
return err
}
// 1-删除所有的商品信息
if len(commodities) != 0 {
err = gdb.Delete(&commodities).Error
if err != nil {
logger.Error("更新商品订单信息-删除 error")
return errors.New("操作失败:" + err.Error())
}
}
for i, _ := range req.Commodities {
req.Commodities[i].ID = 0
if req.Commodities[i].RepairRecordId == 0 { //发现前端有时会没传,后端打补丁
req.Commodities[i].RepairRecordId = orderId
}
}
// 2-更新商品订单信息-新增
if len(req.Commodities) != 0 {
err = gdb.Create(&req.Commodities).Error
if err != nil {
logger.Error("更新商品订单信息-新增 error")
return errors.New("操作失败:" + err.Error())
}
}
return nil
}
// List 查询维修订单列表
func (m *ErpRepairOrderListReq) List(c *gin.Context) (*ErpRepairOrderListResp, error) {
resp := &ErpRepairOrderListResp{
PageIndex: m.PageIndex,
PageSize: m.PageSize,
}
page := m.PageIndex - 1
if page < 0 {
page = 0
}
if m.PageSize == 0 {
m.PageSize = 10
}
qs := orm.Eloquent.Table("repair_record")
if m.SerialNumber != "" {
qs = qs.Where("serial_number = ?", m.SerialNumber)
} else if m.Uid != 0 {
qs = qs.Where("uid = ?", m.Uid)
} else if m.Tel != "" {
qs = qs.Where("tel = ?", m.Tel)
} else {
if m.RepairUnitId != 0 {
qs = qs.Where("repair_unit_id = ?", m.RepairUnitId)
}
if len(m.StoreId) != 0 {
qs = qs.Where("store_id in ?", m.StoreId)
}
// 非管理员才判断所属门店
if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") {
sysUser, err := GetSysUserByCtx(c)
if err != nil {
return nil, errors.New("操作失败:" + err.Error())
}
// 返回sysUser未过期的门店id列表
storeList := GetValidStoreIDs(sysUser.StoreData)
if len(storeList) > 0 {
if len(storeList) == 1 {
qs = qs.Where("store_id = ?", storeList[0])
} else {
qs = qs.Where("store_id IN (?)", storeList)
}
} else {
return nil, errors.New("用户未绑定门店")
}
}
if m.MemberLevel != 0 {
qs = qs.Where("member_level = ?", m.MemberLevel)
}
if len(m.State) > 0 {
qs = qs.Where("state IN (?)", m.State)
}
if m.HandlerId != 0 {
qs = qs.Where("handler_id=?", m.HandlerId)
}
if m.AuditTimeStart != "" {
parse, err := time.Parse(QueryTimeFormat, m.AuditTimeStart)
if err != nil {
logger.Errorf("erpRepairOrderList err:", err)
return nil, err
}
qs = qs.Where("audit_time > ?", parse)
}
if m.AuditTimeEnd != "" {
parse, err := time.Parse(QueryTimeFormat, m.AuditTimeEnd)
if err != nil {
logger.Errorf("erpRepairOrderList err:", err)
return nil, err
}
//parse = parse.AddDate(0, 0, 1)
qs = qs.Where("audit_time < ?", parse)
}
if m.MakeTimeStart != "" {
parse, err := time.Parse(QueryTimeFormat, m.MakeTimeStart)
if err != nil {
logger.Errorf("err:", err)
}
qs = qs.Where("maker_time > ?", parse)
}
if m.MakeTimeEnd != "" {
parse, err := time.Parse(QueryTimeFormat, m.MakeTimeEnd)
if err != nil {
logger.Errorf("err:", err)
}
qs = qs.Where("maker_time < ?", parse)
}
if m.SendTimeStart != "" {
parse, err := time.Parse(QueryTimeFormat, m.SendTimeStart)
if err != nil {
logger.Errorf("err:", err)
}
qs = qs.Where("send_time > ?", parse)
}
if m.SendTimeEnd != "" {
parse, err := time.Parse(QueryTimeFormat, m.SendTimeEnd)
if err != nil {
logger.Errorf("err:", err)
}
qs = qs.Where("send_time < ?", parse)
}
if m.CompleteTimeStart != "" {
parse, err := time.Parse(QueryTimeFormat, m.CompleteTimeStart)
if err != nil {
logger.Errorf("err:", err)
}
qs = qs.Where("complete_time > ?", parse)
}
if m.CompleteTimeEnd != "" {
parse, err := time.Parse(QueryTimeFormat, m.CompleteTimeEnd)
if err != nil {
logger.Errorf("err:", err)
}
qs = qs.Where("complete_time < ?", parse)
}
}
var count int64
err := qs.Count(&count).Error
if err != nil {
logger.Error("count err:", logger.Field("err", err))
return resp, err
}
resp.Total = int(count)
var orders []RepairRecord
err = qs.Order("id DESC").Offset(page * m.PageSize).Limit(m.PageSize).Find(&orders).Error
if err != nil && err != RecordNotFound {
logger.Error("erp commodity list err:", logger.Field("err", err))
return resp, err
}
// 校验时间如果为01-01-01 08:05则赋值为空
for i, v := range orders {
if v.MakerTime != nil && v.MakerTime.IsZero() {
orders[i].MakerTime = nil
}
if v.AuditTime != nil && v.AuditTime.IsZero() {
orders[i].AuditTime = nil
}
if v.SendTime != nil && v.SendTime.IsZero() {
orders[i].SendTime = nil
}
if v.CompletedTime != nil && v.CompletedTime.IsZero() {
orders[i].CompletedTime = nil
}
// 添加维修单位名称
supplier, _ := GetSupplierById(v.RepairUnitId)
orders[i].RepairUnitName = supplier.Name
}
resp.List = orders
return resp, nil
}
// SendToRepair 维修订单送修
func SendToRepair(req *ErpRepairSendReq, c *gin.Context) error {
// 查询订单信息
var repairRecord RepairRecord
err := orm.Eloquent.Table("repair_record").Where("serial_number = ?", req.SerialNumber).
Find(&repairRecord).Error
if err != nil {
logger.Error("order err:", logger.Field("err", err))
return err
}
sysUser, err := GetSysUserByCtx(c)
if err != nil {
logger.Error("sys user err:", logger.Field("err", err))
return err
}
// 校验入参门店是否包含在用户所有门店中,是否过期
if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") {
if !CheckUserStore(repairRecord.StoreId, sysUser) {
return errors.New("操作失败:您没有该门店权限")
}
}
if repairRecord.ID == 0 {
return errors.New("未查询到订单信息")
}
if repairRecord.State != ErpRepairOrderWaitRepair {
return errors.New("订单不是待送修状态")
}
nowTime := time.Now()
repairRecord.RepairUnitId = req.RepairUnitId
repairRecord.ExpressType = req.ExpressType
repairRecord.ExpressNo = req.ExpressNo
repairRecord.ExpressFee = req.ExpressFee
repairRecord.SendRemark = req.SendRemark
repairRecord.State = ErpRepairOrderBingRepaired
repairRecord.SendTime = &nowTime
err = orm.Eloquent.Model(&RepairRecord{}).Where("id = ?", repairRecord.ID).
Updates(repairRecord).Error
if err != nil {
return err
}
return nil
}
// CompleteRepairOrder 维修订单完成
func CompleteRepairOrder(req *ErpRepairCompleteReq, c *gin.Context) error {
// 查询订单信息
var repairRecord RepairRecord
err := orm.Eloquent.Table("repair_record").Where("serial_number = ?", req.SerialNumber).
Find(&repairRecord).Error
if err != nil {
logger.Error("order err:", logger.Field("err", err))
return err
}
sysUser, err := GetSysUserByCtx(c)
if err != nil {
logger.Error("sys user err:", logger.Field("err", err))
return err
}
// 校验入参门店是否包含在用户所有门店中,是否过期
if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") {
if !CheckUserStore(repairRecord.StoreId, sysUser) {
return errors.New("操作失败:您没有该门店权限")
}
}
if repairRecord.ID == 0 {
return errors.New("未查询到订单信息")
}
if repairRecord.State != ErpRepairOrderBingRepaired {
return errors.New("订单不是待送修状态")
}
nowTime := time.Now()
repairRecord.RepairFee = req.RepairFee
repairRecord.CompleteRemark = req.CompleteRemark
repairRecord.State = ErpRepairOrderFinished
repairRecord.CompletedTime = &nowTime
err = orm.Eloquent.Model(&RepairRecord{}).Where("id = ?", repairRecord.ID).
Updates(repairRecord).Error
if err != nil {
return err
}
return nil
}
// ErpRepairDetailReportReq 维修明细汇总入参
type ErpRepairDetailReportReq struct {
SerialNumber string `json:"serial_number"` // 单据编号
Uid uint32 `json:"uid"` // 用户ID
Tel string `json:"tel"` // 手机号
MemberLevel uint32 `json:"member_level"` // 会员等级:普通用户、尊享会员
StoreId []uint32 `json:"store_id"` // 门店id
HandlerId uint32 `json:"handler_id"` // 经手人id
RepairUnitId uint32 `json:"repair_unit_id"` // 维修单位ID供应商
MakeTimeStart string `json:"make_time_start"` // 制单开始时间
MakeTimeEnd string `json:"make_time_end"` // 制单结束时间
AuditTimeStart string `json:"audit_time_start"` // 审核开始时间
AuditTimeEnd string `json:"audit_time_end"` // 审核结束时间
SendTimeStart string `json:"send_time_start"` // 送修开始时间
SendTimeEnd string `json:"send_time_end"` // 送修结束时间
CompleteTimeStart string `json:"complete_time_start"` // 维修完成开始时间
CompleteTimeEnd string `json:"complete_time_end"` // 维修完成结束时间
PageIndex int `json:"pageIndex"` // 页码
PageSize int `json:"pageSize"` // 页面条数
IsExport uint32 `json:"is_export"` // 1-导出
//State []uint32 `json:"state"` // 状态1-待审核2-已审核3-维修中4-已完成
}
// ErpRepairDetailReportResp 维修明细汇总出参
type ErpRepairDetailReportResp struct {
Total int `json:"total"` // 总条数(总订单数)
PageIndex int `json:"pageIndex"` // 页码
PageSize int `json:"pageSize"` // 每页展示条数
ExportUrl string `json:"export_url"` // 导出excel地址
List []RepairRecord `json:"list"` // 零售明细
SumData RepairDetailTotalData `json:"sumData"` // 汇总数据
}
// RepairDetailTotalData 维修明细相关金额汇总
type RepairDetailTotalData struct {
Count int32 `json:"count"` // 维修数量
TotalExpressFee float64 `json:"total_express_fee"` // 快递费用
TotalRepairFee float64 `json:"total_repair_fee"` // 维修费用
}
// RepairDetailReport 维修明细
func RepairDetailReport(req *ErpRepairDetailReportReq, c *gin.Context) (*ErpRepairDetailReportResp, error) {
page := req.PageIndex - 1
if page < 0 {
page = 0
}
if req.PageSize == 0 {
req.PageSize = 10
}
db := orm.Eloquent.Table("repair_record AS rr").
Select("rr.*, s.name AS store_name, ru.name AS repair_unit_name").
Joins("LEFT JOIN store s ON rr.store_id = s.id").
Joins("LEFT JOIN erp_supplier ru ON rr.repair_unit_id = ru.id").
Where("state in (3,4)")
// 条件过滤
db = buildRepairDetailQuery(db, req)
sumQuery := orm.Eloquent.Table("repair_record AS rr").
Select("COUNT(*) AS count, COALESCE(SUM(rr.express_fee), 0) AS total_express_fee, COALESCE(SUM(rr.repair_fee), 0) AS total_repair_fee").
Where("state in (3,4)")
sumQuery = buildRepairDetailQuery(sumQuery, req)
// 非管理员才判断所属门店
if !(tools.GetRoleName(c) == "admin" || tools.GetRoleName(c) == "系统管理员") {
sysUser, err := GetSysUserByCtx(c)
if err != nil {
return nil, errors.New("操作失败:" + err.Error())
}
// 返回sysUser未过期的门店id列表
storeList := GetValidStoreIDs(sysUser.StoreData)
if len(storeList) > 0 {
if len(storeList) == 1 {
db = db.Where("store_id = ?", storeList[0])
sumQuery = sumQuery.Where("store_id = ?", storeList[0])
} else {
db = db.Where("store_id IN (?)", storeList)
sumQuery = sumQuery.Where("store_id IN (?)", storeList)
}
} else {
return nil, errors.New("用户未绑定门店")
}
}
var total int64
if err := db.Count(&total).Error; err != nil {
return nil, err
}
var records []RepairRecord
query := db.Order("rr.id DESC")
if req.IsExport != 1 {
query = query.Offset(page * req.PageSize).Limit(req.PageSize)
}
if err := query.Find(&records).Error; err != nil {
return nil, err
}
// 查询商品明细
for i := range records {
var commodities []RepairRecordCommodity
err := orm.Eloquent.Table("repair_record_commodity AS rc").
Select("rc.*, ec.name AS commodity_name").
Joins("LEFT JOIN erp_commodity ec ON rc.commodity_id = ec.id").
Where("rc.repair_record_id = ?", records[i].ID).
Find(&commodities).Error
if err != nil {
return nil, err
}
records[i].Commodities = commodities
}
// 汇总数据
var sumData RepairDetailTotalData
if err := sumQuery.Scan(&sumData).Error; err != nil {
return nil, err
}
resp := &ErpRepairDetailReportResp{
Total: int(total),
PageIndex: req.PageIndex,
PageSize: req.PageSize,
List: records,
SumData: sumData,
}
// 处理导出逻辑
if req.IsExport == 1 {
exportUrl, err := ExportRepairDetailExcel(records, sumData)
if err != nil {
return nil, err
}
resp.ExportUrl = exportUrl
resp.List = nil
}
return resp, nil
}
func buildRepairDetailQuery(base *gorm.DB, req *ErpRepairDetailReportReq) *gorm.DB {
if req.SerialNumber != "" {
base = base.Where("rr.serial_number = ?", req.SerialNumber)
}
if req.Uid > 0 {
base = base.Where("rr.uid = ?", req.Uid)
}
if req.Tel != "" {
base = base.Where("rr.tel = ?", req.Tel)
}
if req.MemberLevel > 0 {
base = base.Where("rr.member_level = ?", req.MemberLevel)
}
if len(req.StoreId) > 0 {
base = base.Where("rr.store_id IN (?)", req.StoreId)
}
if req.HandlerId > 0 {
base = base.Where("rr.handler_id = ?", req.HandlerId)
}
//if len(req.State) > 0 {
// base = base.Where("rr.state IN (?)", req.State)
//}
if req.RepairUnitId > 0 {
base = base.Where("rr.repair_unit_id = ?", req.RepairUnitId)
}
if req.MakeTimeStart != "" && req.MakeTimeEnd != "" {
base = base.Where("rr.maker_time BETWEEN ? AND ?", req.MakeTimeStart, req.MakeTimeEnd)
}
if req.AuditTimeStart != "" && req.AuditTimeEnd != "" {
base = base.Where("rr.audit_time BETWEEN ? AND ?", req.AuditTimeStart, req.AuditTimeEnd)
}
if req.SendTimeStart != "" && req.SendTimeEnd != "" {
base = base.Where("rr.send_time BETWEEN ? AND ?", req.SendTimeStart, req.SendTimeEnd)
}
if req.CompleteTimeStart != "" && req.CompleteTimeEnd != "" {
base = base.Where("rr.completed_time BETWEEN ? AND ?", req.CompleteTimeStart, req.CompleteTimeEnd)
}
return base
}
// ExportRepairDetailExcel 导出维修明细报表excel
func ExportRepairDetailExcel(list []RepairRecord, sumData RepairDetailTotalData) (string, error) {
file := excelize.NewFile()
sheet := "Sheet1"
url := config.ExportConfig.Url
fileName := time.Now().Format(TimeFormat) + "维修明细.xlsx"
savePath := config.ExportConfig.Path + fileName
fmt.Println("生成路径:", url+fileName)
storeMap, err := GetAllStoreData()
if err != nil {
return "", err
}
// 表头
title := []interface{}{
"维修单号", "门店", "用户ID", "联系电话", "会员等级", "状态", "制单人", "制单时间", "审核人", "审核时间",
"送修时间", "送修备注", "邮寄方式", "快递单号", "快递费用", "维修单位", "维修费用", "完成时间", "完成备注",
"商品名称", "旧机器编码", "新机器编码", "数量", "故障描述",
}
// 写表头
for i, v := range title {
cell, _ := excelize.CoordinatesToCellName(i+1, 1)
_ = file.SetCellValue(sheet, cell, v)
}
rowIndex := 2
for _, record := range list {
memberLevelStr := GetUserTypeString(int(record.MemberLevel))
stateMap := map[uint8]string{
1: "待审核",
2: "待送修",
3: "维修中",
4: "已完成",
}
stateStr := stateMap[record.State]
sendTypeStr := map[uint8]string{
1: "个人",
2: "门店",
}[record.ExpressType]
nFlag := 0
for _, comm := range record.Commodities {
var row []interface{}
if nFlag >= 1 {
row = []interface{}{
record.SerialNumber,
storeMap[record.StoreId].Name,
record.Uid,
record.Tel,
memberLevelStr,
stateStr,
record.MakerName,
formatDateTime(record.MakerTime),
record.AuditorName,
formatTime(record.AuditTime),
formatTime(record.SendTime),
record.SendRemark,
sendTypeStr,
record.ExpressNo,
"",
record.RepairUnitName,
"",
formatTime(record.CompletedTime),
record.CompleteRemark,
comm.CommodityName,
comm.OldIMEI,
comm.NewIMEI,
comm.Count,
comm.FaultDesc,
}
} else {
row = []interface{}{
record.SerialNumber,
storeMap[record.StoreId].Name,
record.Uid,
record.Tel,
memberLevelStr,
stateStr,
record.MakerName,
formatDateTime(record.MakerTime),
record.AuditorName,
formatTime(record.AuditTime),
formatTime(record.SendTime),
record.SendRemark,
sendTypeStr,
record.ExpressNo,
record.ExpressFee,
record.RepairUnitName,
record.RepairFee,
formatTime(record.CompletedTime),
record.CompleteRemark,
comm.CommodityName,
comm.OldIMEI,
comm.NewIMEI,
comm.Count,
comm.FaultDesc,
}
}
for j, val := range row {
cell, _ := excelize.CoordinatesToCellName(j+1, rowIndex)
_ = file.SetCellValue(sheet, cell, val)
}
rowIndex++
nFlag++
}
}
// 汇总行
summary := []interface{}{
fmt.Sprintf("维修单数: %d", len(list)),
"", "", "", "", "", "", "", "", "", "", "", "", "", sumData.TotalExpressFee, "", sumData.TotalRepairFee,
}
for i, val := range summary {
cell, _ := excelize.CoordinatesToCellName(i+1, rowIndex)
_ = file.SetCellValue(sheet, cell, val)
}
// 设置样式
style, _ := file.NewStyle(`{
"alignment":{"horizontal":"center","vertical":"center"},
"border":[
{"type":"left","color":"000000","style":1},
{"type":"top","color":"000000","style":1},
{"type":"right","color":"000000","style":1},
{"type":"bottom","color":"000000","style":1}
]
}`)
lastCol, _ := excelize.ColumnNumberToName(len(title))
endCell := fmt.Sprintf("%s%d", lastCol, rowIndex)
_ = file.SetCellStyle(sheet, "A1", endCell, style)
// 设置列宽(根据字段名称微调)
file.SetColWidth(sheet, "A", "A", 18)
file.SetColWidth(sheet, "B", "B", 25)
file.SetColWidth(sheet, "C", "C", 10)
file.SetColWidth(sheet, "D", "D", 15)
file.SetColWidth(sheet, "E", "E", 15)
file.SetColWidth(sheet, "J", "J", 18)
file.SetColWidth(sheet, "K", "K", 18)
file.SetColWidth(sheet, "N", "N", 20)
file.SetColWidth(sheet, "S", "S", 18)
file.SetColWidth(sheet, "T", "T", 25)
file.SetColWidth(sheet, "X", "X", 30)
// 保存
if err := file.SaveAs(savePath); err != nil {
logger.Error("保存文件失败:", logger.Field("err", err))
return "", err
}
return url + fileName, nil
}
// formatTime 格式化时间为空判断
func formatTime(t *time.Time) string {
if t == nil {
return ""
}
return t.Format("2006-01-02 15:04:05")
}
// formatDateTime 格式化时间为空判断(仅保留日期)
func formatDateTime(t *time.Time) string {
if t == nil {
return ""
}
return t.Format("2006-01-02")
}