package service import ( "context" "fmt" "strconv" "time" camp_dao "dd_fiber_api/internal/camp/dao" "dd_fiber_api/internal/order" order_dao "dd_fiber_api/internal/order/dao" "dd_fiber_api/internal/payment" "dd_fiber_api/internal/scheduler" "dd_fiber_api/pkg/snowflake" ) // OrderService 订单服务 type OrderService struct { orderDAO *order_dao.OrderDAO sectionDAO *camp_dao.SectionDAO // 需要查询小节价格 accessDAO *camp_dao.UserSectionAccessDAO // 用户小节访问记录 DAO userCampDAO *camp_dao.UserCampDAO // 用户打卡营 DAO(用于更新当前小节) wechatPayService *payment.WechatPayV3Service // 微信支付服务 schedulerService *scheduler.Service // 调度器服务(用于创建定时任务) apiBaseURL string // API基础URL(用于构建回调URL) } // NewOrderService 创建订单服务 func NewOrderService(orderDAO *order_dao.OrderDAO, sectionDAO *camp_dao.SectionDAO) *OrderService { return &OrderService{ orderDAO: orderDAO, sectionDAO: sectionDAO, } } // SetAccessDAO 设置访问记录 DAO func (s *OrderService) SetAccessDAO(accessDAO *camp_dao.UserSectionAccessDAO) { s.accessDAO = accessDAO } // SetUserCampDAO 设置用户打卡营 DAO func (s *OrderService) SetUserCampDAO(userCampDAO *camp_dao.UserCampDAO) { s.userCampDAO = userCampDAO } // SetPaymentService 设置支付服务(用于创建订单后生成预支付ID) func (s *OrderService) SetPaymentService(wechatPayService *payment.WechatPayV3Service) { s.wechatPayService = wechatPayService } // SetSchedulerService 设置调度器服务(用于创建定时任务) func (s *OrderService) SetSchedulerService(schedulerService *scheduler.Service) { s.schedulerService = schedulerService } // SetAPIBaseURL 设置API基础URL(用于构建回调URL) func (s *OrderService) SetAPIBaseURL(baseURL string) { s.apiBaseURL = baseURL } // CreateOrder 创建订单(适配新的 orders 表,并集成支付功能) func (s *OrderService) CreateOrder(ctx context.Context, req *order.CreateOrderRequest) (*order.CreateOrderResponse, error) { // 查询小节信息,获取价格 section, err := s.sectionDAO.GetByID(req.SectionID) if err != nil { return &order.CreateOrderResponse{ Success: false, Message: fmt.Sprintf("查询小节失败: %v", err), }, nil } // 验证小节是否属于指定的打卡营 if section.CampID != req.CampID { return &order.CreateOrderResponse{ Success: false, Message: "小节不属于指定的打卡营", }, nil } // 生成订单ID orderID := snowflake.GetInstance().NextIDString() // 确定订单金额 originalAmount := section.PriceFen if originalAmount < 0 { originalAmount = 0 } // 计算优惠金额(暂时为0,后续可以集成优惠券逻辑) discountAmount := int32(0) // 计算实际支付金额 actualAmount := originalAmount - discountAmount if actualAmount < 0 { actualAmount = 0 } // 确定订单状态:如果实际支付金额为0,直接设为已支付;否则设为待支付 orderStatus := order.OrderStatusPending var paymentTime *time.Time if actualAmount == 0 { // 0元订单直接完成,设置支付时间为当前时间 orderStatus = order.OrderStatusPaid now := time.Now() paymentTime = &now } // 确定支付方式(如果未指定,0元订单不需要支付方式,其他订单默认为微信支付) paymentMethod := req.PaymentMethod if paymentMethod == order.PaymentMethodUnknown { if actualAmount > 0 { paymentMethod = order.PaymentMethodWechat } // 0元订单保持 PaymentMethodUnknown } // 创建订单(使用新的 orders 表) err = s.orderDAO.CreateOrder( orderID, req.UserID, order.OrderTypeCampSection, originalAmount, discountAmount, actualAmount, req.CouponID, orderStatus, paymentMethod, paymentTime, // 0元订单的支付时间 ) if err != nil { return &order.CreateOrderResponse{ Success: false, Message: fmt.Sprintf("创建订单失败: %v", err), }, nil } // 创建订单业务数据(关联订单和小节) // 注意:如果 order_business_data 表不存在,这个调用会失败但不影响订单创建 if req.CampID != "" && req.SectionID != "" { _ = s.orderDAO.CreateOrderBusinessData(orderID, req.CampID, req.SectionID) } // 如果是0元订单(已支付),创建访问记录 if actualAmount == 0 && orderStatus == order.OrderStatusPaid && s.accessDAO != nil { accessID := snowflake.GetInstance().NextIDString() paidPriceFen := int32(0) if section.PriceFen > 0 { paidPriceFen = section.PriceFen } // 创建访问记录(0元订单) if err := s.accessDAO.Create( accessID, req.UserID, req.CampID, req.SectionID, paidPriceFen, camp_dao.AccessSourcePurchase, ); err != nil { // 记录错误但不影响订单创建 fmt.Printf("创建访问记录失败(0元订单): %v\n", err) } else { // 更新当前小节 if s.userCampDAO != nil && req.CampID != "" { if err := s.userCampDAO.UpdateCurrentSection(req.UserID, req.CampID, req.SectionID); err != nil { fmt.Printf("更新当前小节失败(0元订单): %v\n", err) } else { fmt.Printf("✅ 当前小节更新成功(0元订单): user_id=%s, camp_id=%s, section_id=%s\n", req.UserID, req.CampID, req.SectionID) } } } } // 构建响应 resp := &order.CreateOrderResponse{ Success: true, Message: "订单创建成功", OrderID: orderID, OrderStatus: orderStatus, ActualAmount: actualAmount, } // 如果订单状态是待支付,创建15分钟后自动关闭订单的定时任务 if orderStatus == order.OrderStatusPending && s.schedulerService != nil && s.apiBaseURL != "" { // 构建回调URL callbackURL := fmt.Sprintf("%s/api/v1/order/auto-cancel?order_id=%s", s.apiBaseURL, orderID) // 创建定时任务(15分钟后执行) taskReq := &scheduler.AddTaskRequest{ BusinessKey: fmt.Sprintf("order_auto_cancel_%s", orderID), TaskType: scheduler.TaskTypeOnce, DelayMs: 15 * 60 * 1000, // 15分钟 = 15 * 60 * 1000 毫秒 CallbackURL: callbackURL, Metadata: map[string]string{ "order_id": orderID, "type": "order_auto_cancel", }, } taskResp, err := s.schedulerService.AddTask(taskReq) if err == nil && taskResp != nil && taskResp.Success { fmt.Printf("✅ 创建订单自动关闭任务成功: order_id=%s, task_id=%s\n", orderID, taskResp.TaskID) } else { fmt.Printf("⚠️ 创建订单自动关闭任务失败: order_id=%s, error=%v\n", orderID, err) } } // 如果订单需要支付且是微信支付,生成预支付ID if actualAmount > 0 && orderStatus == order.OrderStatusPending && paymentMethod == order.PaymentMethodWechat { if s.wechatPayService != nil && req.OpenID != "" { // 生成商品描述 description := fmt.Sprintf("打卡营小节-%s", section.Title) if len(description) > 127 { description = description[:127] // 微信支付描述最长127字符 } // 调用微信支付服务生成预支付ID payReq := &payment.CreateWechatPayV3Request{ OrderID: orderID, Description: description, OpenID: req.OpenID, Amount: actualAmount, } payResp, err := s.wechatPayService.CreateWechatPayV3(ctx, payReq) if err == nil && payResp != nil && payResp.Success { // 将支付信息添加到响应中 resp.PrepayID = payResp.PrepayID resp.AppID = payResp.AppID resp.TimeStamp = payResp.TimeStamp resp.NonceStr = payResp.NonceStr resp.Package = payResp.Package resp.SignType = payResp.SignType resp.PaySign = payResp.PaySign } else { // 支付创建失败,但不影响订单创建 // 可以记录日志,但不返回错误 } } } return resp, nil } // GetOrder 获取订单详情 func (s *OrderService) GetOrder(orderID, orderNo string) (*order.GetOrderResponse, error) { if orderID == "" && orderNo == "" { return &order.GetOrderResponse{ Success: false, Message: "参数缺失:order_id 和 order_no 至少需要提供一个", }, nil } var orderObj *order.Order var err error if orderID != "" { orderObj, err = s.orderDAO.GetOrderByID(orderID) } else { orderObj, err = s.orderDAO.GetOrderByOrderNo(orderNo) } if err != nil { return &order.GetOrderResponse{ Success: false, Message: fmt.Sprintf("查询订单失败: %v", err), }, nil } if orderObj == nil { return &order.GetOrderResponse{ Success: false, Message: "订单不存在", }, nil } return &order.GetOrderResponse{ Success: true, Message: "查询成功", Order: orderObj, }, nil } // ListOrders 查询订单列表 func (s *OrderService) ListOrders(req *order.ListOrdersRequest) (*order.ListOrdersResponse, error) { // 设置默认分页参数 page := req.Page if page <= 0 { page = 1 } pageSize := req.PageSize if pageSize <= 0 { pageSize = 20 } if pageSize > 100 { pageSize = 100 // 限制最大每页数量 } orders, total, err := s.orderDAO.ListOrders( req.UserID, req.CampID, req.SectionID, req.OrderStatus, req.PaymentMethod, page, pageSize, ) if err != nil { return &order.ListOrdersResponse{ Success: false, Message: fmt.Sprintf("查询订单列表失败: %v", err), }, nil } return &order.ListOrdersResponse{ Success: true, Message: "查询成功", Orders: orders, Total: total, }, nil } // UpdateOrderStatus 更新订单状态(用于支付回调) func (s *OrderService) UpdateOrderStatus(req *order.UpdateOrderStatusRequest) (*order.UpdateOrderStatusResponse, error) { if req.OrderID == "" && req.OrderNo == "" { return &order.UpdateOrderStatusResponse{ Success: false, Message: "参数缺失:order_id 和 order_no 至少需要提供一个", }, nil } // 解析支付时间 var paymentTime *time.Time if req.PaymentTime != "" { // 尝试解析 Unix 时间戳 if sec, err := strconv.ParseInt(req.PaymentTime, 10, 64); err == nil { t := time.Unix(sec, 0) paymentTime = &t } else { // 尝试解析 RFC3339 格式 if t, err := time.Parse(time.RFC3339, req.PaymentTime); err == nil { paymentTime = &t } } } // 更新订单状态 err := s.orderDAO.UpdateOrderStatus( req.OrderID, req.OrderNo, req.OrderStatus, req.PaymentMethod, req.ThirdPartyOrderNo, paymentTime, ) if err != nil { return &order.UpdateOrderStatusResponse{ Success: false, Message: fmt.Sprintf("更新订单状态失败: %v", err), }, nil } // 查询更新后的订单 var orderObj *order.Order if req.OrderID != "" { orderObj, err = s.orderDAO.GetOrderByID(req.OrderID) } else { orderObj, err = s.orderDAO.GetOrderByOrderNo(req.OrderNo) } if err != nil { return &order.UpdateOrderStatusResponse{ Success: false, Message: fmt.Sprintf("查询订单失败: %v", err), }, nil } // 如果订单状态更新为已支付,取消自动关闭订单的定时任务 if req.OrderStatus == order.OrderStatusPaid && s.schedulerService != nil && orderObj != nil { // 通过 business_key 查找任务(需要调度器服务支持通过 business_key 查询) // 目前调度器服务只支持通过 task_id 删除,所以这里先记录日志 // 后续可以优化调度器服务,支持通过 business_key 删除任务 businessKey := fmt.Sprintf("order_auto_cancel_%s", orderObj.OrderID) fmt.Printf("订单已支付,需要取消自动关闭任务: order_id=%s, business_key=%s\n", orderObj.OrderID, businessKey) // TODO: 实现通过 business_key 删除任务的逻辑 } // 如果订单状态更新为已支付,且访问记录DAO已设置,创建访问记录 if req.OrderStatus == order.OrderStatusPaid && s.accessDAO != nil && orderObj != nil && orderObj.SectionID != "" { // 检查是否已存在访问记录(幂等性检查) hasAccess, err := s.accessDAO.GetByUserAndSection(orderObj.UserID, orderObj.SectionID) if err == nil && !hasAccess { // 生成访问记录ID(使用雪花算法) accessID := snowflake.GetInstance().NextIDString() // 创建访问记录 paidPriceFen := int32(orderObj.ActualAmount) if paidPriceFen < 0 { paidPriceFen = 0 } err = s.accessDAO.Create( accessID, orderObj.UserID, orderObj.CampID, orderObj.SectionID, paidPriceFen, camp_dao.AccessSourcePurchase, ) if err != nil { // 记录错误但不影响订单状态更新 fmt.Printf("创建访问记录失败: %v\n", err) } else { fmt.Printf("✅ 访问记录创建成功: user_id=%s, section_id=%s\n", orderObj.UserID, orderObj.SectionID) // 更新当前小节 if s.userCampDAO != nil && orderObj.CampID != "" { if err := s.userCampDAO.UpdateCurrentSection(orderObj.UserID, orderObj.CampID, orderObj.SectionID); err != nil { fmt.Printf("更新当前小节失败: %v\n", err) } else { fmt.Printf("✅ 当前小节更新成功: user_id=%s, camp_id=%s, section_id=%s\n", orderObj.UserID, orderObj.CampID, orderObj.SectionID) } } } } } return &order.UpdateOrderStatusResponse{ Success: true, Message: "更新成功", Order: orderObj, }, nil } // CancelOrder 取消订单 func (s *OrderService) CancelOrder(req *order.CancelOrderRequest) (*order.CancelOrderResponse, error) { if req.OrderID == "" && req.OrderNo == "" { return &order.CancelOrderResponse{ Success: false, Message: "参数缺失:order_id 和 order_no 至少需要提供一个", }, nil } // 使用 UpdateOrderStatus 来取消订单 err := s.orderDAO.UpdateOrderStatus( req.OrderID, req.OrderNo, order.OrderStatusCancelled, order.PaymentMethodUnknown, "", nil, ) if err != nil { return &order.CancelOrderResponse{ Success: false, Message: fmt.Sprintf("取消订单失败: %v", err), }, nil } return &order.CancelOrderResponse{ Success: true, Message: "取消订单成功", }, nil }