package app import ( "context" "encoding/json" "strings" "git.5iot.com/iotlcm-public/iotlcm-app/internal/consts" "git.5iot.com/iotlcm-public/iotlcm-app/internal/dao" "git.5iot.com/iotlcm-public/iotlcm-app/internal/model" "git.5iot.com/iotlcm-public/iotlcm-app/internal/model/entity" "git.5iot.com/iotlcm-public/iotlcm-app/internal/service" "github.com/gogf/gf/v2/database/gdb" "github.com/gogf/gf/v2/errors/gerror" ) type sAppOrder struct{} func NewAppOrder() *sAppOrder { return &sAppOrder{} } func init() { service.RegisterAppOrder(NewAppOrder()) } func (s *sAppOrder) List(ctx context.Context, param *model.AppOrderListParam) (items []*model.AppOrderModel, total int, err error) { if param == nil { param = &model.AppOrderListParam{} } if param.Page <= 0 { param.Page = 1 } if param.PageSize <= 0 { param.PageSize = 10 } if param.PageSize > 100 { param.PageSize = 100 } user, err := appCurrentUser(ctx) if err != nil { return nil, 0, err } mod := s.ownerOrderModel(ctx, user) mod = s.applyOrderFilters(mod, param) total, err = mod.Count() if err != nil { return nil, 0, gerror.Wrap(err, "查询订单数量失败") } if total == 0 { return []*model.AppOrderModel{}, 0, nil } var rows []*entity.TOrder if err = mod. Page(param.Page, param.PageSize). OrderDesc(dao.TOrder.Columns().CreatedAt). OrderDesc(dao.TOrder.Columns().Id). Scan(&rows); err != nil { return nil, 0, gerror.Wrap(err, "查询订单列表失败") } orderSns := make([]string, 0, len(rows)) for _, row := range rows { if row != nil && strings.TrimSpace(row.OrderSn) != "" { orderSns = append(orderSns, row.OrderSn) } } itemMap, err := s.orderItemMap(ctx, user.TenantId, orderSns) if err != nil { return nil, 0, err } items = make([]*model.AppOrderModel, 0, len(rows)) for _, row := range rows { items = append(items, s.mapOrder(ctx, row, itemMap[row.OrderSn], false)) } return items, total, nil } func (s *sAppOrder) Detail(ctx context.Context, param *model.AppOrderDetailParam) (*model.AppOrderModel, error) { if param == nil { param = &model.AppOrderDetailParam{} } orderSn := strings.TrimSpace(param.OrderSn) if orderSn == "" { return nil, gerror.New("订单号不能为空") } user, err := appCurrentUser(ctx) if err != nil { return nil, err } var order *entity.TOrder if err = s.ownerOrderModel(ctx, user). Where(dao.TOrder.Columns().OrderSn, orderSn). Limit(1). Scan(&order); err != nil { return nil, gerror.Wrap(err, "查询订单详情失败") } if order == nil || order.Id == 0 { return nil, gerror.New("订单不存在或无权查看") } itemMap, err := s.orderItemMap(ctx, user.TenantId, []string{order.OrderSn}) if err != nil { return nil, err } return s.mapOrder(ctx, order, itemMap[order.OrderSn], true), nil } func (s *sAppOrder) ownerOrderModel(ctx context.Context, user *entity.SysUser) *gdb.Model { orderTable := dao.TOrder.Table() mod := dao.TOrder.Ctx(ctx). Where(orderTable+"."+dao.TOrder.Columns().TenantId, user.TenantId) if appIsTerminalUser(ctx, user) { return mod.Where(orderTable+"."+dao.TOrder.Columns().CreatedBy, user.UserId) } return appApplyEnterpriseDataScope( ctx, user, mod, orderTable+"."+dao.TOrder.Columns().CreatedDept, orderTable+"."+dao.TOrder.Columns().CreatedBy, func(legacyMod *gdb.Model) *gdb.Model { return legacyMod.Where(orderTable+"."+dao.TOrder.Columns().CreatedBy, user.UserId) }, ) } func (s *sAppOrder) applyOrderFilters(mod *gdb.Model, param *model.AppOrderListParam) *gdb.Model { if param.OrderType > 0 { mod = mod.Where(dao.TOrder.Columns().OrderType, param.OrderType) } if param.PayStatus != nil && *param.PayStatus >= 0 { mod = mod.Where(dao.TOrder.Columns().PayStatus, *param.PayStatus) } keyword := strings.TrimSpace(param.Keyword) if keyword == "" { keyword = strings.TrimSpace(param.PageInfo.Keyword) } if keyword != "" { like := "%" + keyword + "%" orderTable := dao.TOrder.Table() itemTable := dao.TOrderItem.Table() mod = mod.Where( "("+orderTable+"."+dao.TOrder.Columns().OrderSn+" LIKE ? OR "+ orderTable+"."+dao.TOrder.Columns().PayOrderSn+" LIKE ? OR "+ orderTable+"."+dao.TOrder.Columns().Remark+" LIKE ? OR EXISTS (SELECT 1 FROM "+itemTable+" oi WHERE oi."+dao.TOrderItem.Columns().OrderSn+" = "+orderTable+"."+dao.TOrder.Columns().OrderSn+" AND oi."+dao.TOrderItem.Columns().ProductName+" LIKE ?))", like, like, like, like, ) } return mod } func (s *sAppOrder) orderItemMap(ctx context.Context, tenantId string, orderSns []string) (map[string][]*entity.TOrderItem, error) { result := make(map[string][]*entity.TOrderItem) if len(orderSns) == 0 { return result, nil } var rows []*entity.TOrderItem if err := dao.TOrderItem.Ctx(ctx). Where(dao.TOrderItem.Columns().TenantId, tenantId). WhereIn(dao.TOrderItem.Columns().OrderSn, orderSns). OrderAsc(dao.TOrderItem.Columns().ItemId). Scan(&rows); err != nil { return nil, gerror.Wrap(err, "查询订单项失败") } for _, row := range rows { if row == nil { continue } result[row.OrderSn] = append(result[row.OrderSn], row) } return result, nil } func (s *sAppOrder) mapOrder(ctx context.Context, row *entity.TOrder, items []*entity.TOrderItem, includeItems bool) *model.AppOrderModel { if row == nil { return nil } mappedItems := make([]*model.AppOrderItemModel, 0, len(items)) for _, item := range items { if item == nil { continue } mappedItems = append(mappedItems, &model.AppOrderItemModel{ ItemId: item.ItemId, ProductName: item.ProductName, ProductSn: item.ProductSn, Price: appRoundAmount(item.Price), PriceText: appFormatMoney(item.Price), Quantity: item.Quantity, OriginPrice: appRoundAmount(item.OriginPrice), OriginPriceText: appFormatMoney(item.OriginPrice), ProductType: item.ProductType, ServiceBeginTime: item.ServiceBeginTime, ServiceEndTime: item.ServiceEndTime, }) } itemSummary := "订单商品" if len(mappedItems) > 0 && strings.TrimSpace(mappedItems[0].ProductName) != "" { itemSummary = strings.TrimSpace(mappedItems[0].ProductName) if len(mappedItems) > 1 { itemSummary += " 等" } } orderTypeText := appOrderTypeText(row.OrderType) switch appOrderBusinessFromItems(items) { case "miniapp_card_change_batch": orderTypeText = "套餐变更" case "miniapp_card_renew_batch": orderTypeText = "套餐续费" } paymentExpireAt, paymentRemainingSeconds, paymentExpired := appBalanceRechargePaymentTiming(ctx, row) res := &model.AppOrderModel{ OrderId: row.Id, OrderSn: row.OrderSn, OrderType: row.OrderType, OrderTypeText: orderTypeText, OrderStatus: row.OrderStatus, OrderStatusText: appOrderStatusText(row.OrderStatus), PayType: row.PayType, PayTypeText: appOrderPayTypeText(row.PayType), PayStatus: row.PayStatus, PayStatusText: appOrderPayStatusText(row.PayStatus), PayOrderSn: row.PayOrderSn, ReturnStatus: row.ReturnStatus, ReturnStatusText: appOrderReturnStatusText(row.ReturnStatus), PaymentExpireAt: paymentExpireAt, PaymentRemainingSeconds: paymentRemainingSeconds, PaymentExpired: paymentExpired, TotalAmount: appRoundAmount(row.TotalAmount), TotalAmountText: appFormatMoney(row.TotalAmount), PaidAmount: appRoundAmount(row.PaidAmount), PaidAmountText: appFormatMoney(row.PaidAmount), OnlinePaidAmount: appRoundAmount(row.OnlinePaidAmount), OnlinePaidAmountText: appFormatMoney(row.OnlinePaidAmount), Balance: appRoundAmount(row.Balance), BalanceText: appFormatMoney(row.Balance), UnpaidAmount: appRoundAmount(row.UnpaidAmount), UnpaidAmountText: appFormatMoney(row.UnpaidAmount), UnrefundAmount: appRoundAmount(row.UnrefundAmount), UnrefundAmountText: appFormatMoney(row.UnrefundAmount), BuyerNote: row.BuyerNote, Remark: row.Remark, CreatedAt: row.CreatedAt, PayTime: row.PayTime, ItemSummary: itemSummary, ItemCount: len(mappedItems), } if includeItems { res.Items = mappedItems } return res } func appOrderBusinessFromItems(items []*entity.TOrderItem) string { for _, item := range items { if item == nil || strings.TrimSpace(item.ExtraSkuData) == "" { continue } var extra struct { Business string `json:"business"` } if err := json.Unmarshal([]byte(item.ExtraSkuData), &extra); err != nil { continue } if strings.TrimSpace(extra.Business) != "" { return strings.TrimSpace(extra.Business) } } return "" } func appOrderTypeText(orderType int) string { switch orderType { case consts.OrderTypeCardSubscribe: return "套餐续费" case consts.OrderTypeBalanceRecharge: return "余额充值" default: return "其他订单" } } func appOrderStatusText(status int) string { switch status { case consts.OrderStatusPendingPay: return "待支付" case consts.OrderStatusCompleted: return "已完成" default: return "处理中" } } func appOrderPayTypeText(payType int) string { switch payType { case consts.PayTypeBalance: return "余额" case consts.PayTypeOnline: return "线上支付" default: return "未支付" } } func appOrderPayStatusText(payStatus int) string { switch payStatus { case consts.PayStatusUnpaid: return "待支付" case consts.PayStatusPaid: return "已支付" default: return "处理中" } } func appOrderReturnStatusText(status int) string { switch status { case consts.OrderReturnStatusPending: return "退款申请中" case consts.OrderReturnStatusRunning: return "退款处理中" case consts.OrderReturnStatusPartial: return "退款异常" case consts.OrderReturnStatusSuccess: return "已退款" default: return "未退款" } }