package refundOrderSer import ( "context" "errors" "fmt" "stat/api/proto" "stat/models/rechargemod" "stat/models/refundOrderMod" "go.mongodb.org/mongo-driver/bson" "go.mongodb.org/mongo-driver/bson/primitive" "go.mongodb.org/mongo-driver/mongo" "go.mongodb.org/mongo-driver/mongo/options" ) // 只回填付款成功的订单。status=3 为付款成功,与 models/rechargemod/stat.go 中 // 各统计口径一致;付款失败(status=2)的订单不存在退款场景。 const rechargeStatusSuccess = 3 // List 退款订单查询,支持 appId / userId / cid / channelName / districtCode 任意组合 func List(ctx context.Context, req *proto.RefundOrderListReq) (*proto.RefundOrderListResp, error) { resp := &proto.RefundOrderListResp{ Total: 0, List: make(refundOrderMod.RefundOrderList, 0), } filter := bson.M{} if req.AppId != nil { filter[refundOrderMod.FN.RefundOrder.AppId] = *req.AppId } if req.UserId != nil { filter[refundOrderMod.FN.RefundOrder.UserId] = *req.UserId } if req.CID != nil && *req.CID != "" { filter[refundOrderMod.FN.RefundOrder.CID] = *req.CID } if req.TransNo != nil && *req.TransNo != "" { filter[refundOrderMod.FN.RefundOrder.TransNo] = *req.TransNo } if req.IP != nil && *req.IP != "" { filter[refundOrderMod.FN.RefundOrder.IP] = *req.IP } if req.ChannelName != nil && *req.ChannelName != "" { filter[refundOrderMod.FN.RefundOrder.ChannelName] = *req.ChannelName } if req.DistrictCode != nil && *req.DistrictCode != "" { filter[refundOrderMod.FN.RefundOrder.DistrictCode] = *req.DistrictCode } total, err := refundOrderMod.CountRefundOrders(ctx, filter) if err != nil { return nil, err } opt := &options.FindOptions{} opt.SetSort(bson.D{{Key: refundOrderMod.FN.RefundOrder.CreatedAt, Value: -1}}) opt.SetSkip((req.PageNum - 1) * req.PageSize) opt.SetLimit(req.PageSize) list, err := refundOrderMod.FindManyRefundOrders(ctx, filter, opt) if err != nil { return nil, err } resp.Total = total resp.List = list return resp, nil } // Add 按单个 cid 回填 func Add(ctx context.Context, req *proto.AddRefundOrderReq) (*proto.AddRefundOrderResp, error) { return addItems(ctx, []proto.RefundOrderItem{{CID: req.CID, TransNo: req.TransNo, IP: req.IP}}) } // BatchAdd 批量回填 func BatchAdd(ctx context.Context, req *proto.BatchAddRefundOrderReq) (*proto.AddRefundOrderResp, error) { return addItems(ctx, req.Items) } // addItems 逐个 cid 查询 log_recharge 的成功订单,按 (appId, orderId) 判重后落库。 // transNo 不参与查询,只随记录一起写入用于追溯。 func addItems(ctx context.Context, items []proto.RefundOrderItem) (*proto.AddRefundOrderResp, error) { resp := &proto.AddRefundOrderResp{ Details: make([]proto.RefundCidStat, 0, len(items)), } seen := make(map[string]bool, len(items)) // 同一次请求里 cid 去重 for _, item := range items { if item.CID == "" || seen[item.CID] { continue } seen[item.CID] = true stat, err := addOneCid(ctx, item.CID, item.TransNo, item.IP) if err != nil { return nil, fmt.Errorf("回填订单[%s]失败: %w", item.CID, err) } resp.Matched += stat.Matched resp.Inserted += stat.Inserted resp.Skipped += stat.Skipped resp.Details = append(resp.Details, *stat) } return resp, nil } func addOneCid(ctx context.Context, cid, transNo, ip string) (*proto.RefundCidStat, error) { stat := &proto.RefundCidStat{CID: cid, TransNo: transNo} // cid 唯一标识一笔订单,只回填付款成功的那条 src, err := rechargemod.FindOneRecharge(ctx, bson.M{ "cid": cid, "status": rechargeStatusSuccess, }) if err != nil { if errors.Is(err, mongo.ErrNoDocuments) { return stat, nil // 该 cid 没有对应的成功订单,matched 记 0,不算失败 } return nil, err } stat.Matched = 1 // 按 (appId, orderId) 判重,已存在则跳过 exist, err := refundOrderMod.FindOneRefundOrder(ctx, bson.M{ refundOrderMod.FN.RefundOrder.AppId: src.AppId, refundOrderMod.FN.RefundOrder.OrderId: src.OrderId, }) if err != nil { return nil, err } if exist != nil && !exist.ID.IsZero() { stat.Skipped = 1 return stat, nil } doc := &refundOrderMod.RefundOrder{ AppId: src.AppId, CID: src.CID, TransNo: transNo, IP: ip, DistrictCode: src.DistrictCode, Money: src.Money, OrderId: src.OrderId, UserId: src.UserId, Type: src.Type, ChannelName: src.ChannelName, CreatedAt: src.CreatedAt, } if err := refundOrderMod.InsertOneRefundOrder(ctx, doc); err != nil { // 并发下两个请求可能同时通过上面的判重检查,由唯一索引兜底。 // 撞上即视为重复添加,与"重复跳过不报错"的口径一致。 if refundOrderMod.IsDuplicateKey(err) { stat.Skipped = 1 return stat, nil } return nil, err } stat.Inserted = 1 return stat, nil } // Del 按 _id 删除单条 func Del(ctx context.Context, req *proto.DelRefundOrderReq) error { oid, err := primitive.ObjectIDFromHex(req.Id) if err != nil { return fmt.Errorf("id 格式错误: %w", err) } count, err := refundOrderMod.DelOneRefundOrder(ctx, bson.M{ refundOrderMod.FN.RefundOrder.ID: oid, }) if err != nil { return err } if count == 0 { return fmt.Errorf("记录不存在") } return nil }