package biz import ( "context" "encoding/json" "fmt" "net/http" "strconv" "strings" "time" "xy_sh/internal/config" "xy_sh/internal/data/impl" "xy_sh/internal/data/model" "xy_sh/pkg" "xy_sh/pkg/crypto" "xy_sh/ymt_v3" "gitea.cdlsxd.cn/self-tools/l_request" "xy_sh/internal/entities" ) type OrderStore struct { orders *impl.OrderImpl ymtClient *ymt_v3.Client cfg *config.Config } func NewOrderStore(orders *impl.OrderImpl, ymtClient *ymt_v3.Client, cfg *config.Config) *OrderStore { return &OrderStore{ orders: orders, ymtClient: ymtClient, cfg: cfg, } } func (o *OrderStore) CreateOrder(ctx context.Context, req *entities.OrderRequest) (*entities.OrderResponseData, error) { var orderInfo model.XyShOrder err := o.orders.GetByKey(ctx, "act_order_num", req.ActOrderNum, &orderInfo) if err != nil { return nil, err } if orderInfo.ID == 0 { orderInfo = model.XyShOrder{ ActOrderNum: req.ActOrderNum, GoodsCode: req.GoodsCode, ActCode: req.ActCode, Account: req.Account, Status: entities.StatusSuccess, CallBackURL: req.CallbackUrl, CreatedAt: time.Now(), UpdatedAt: time.Now(), } } selfPublicIP, err := pkg.GetPublicIP() if err != nil { selfPublicIP = "120.55.12.245" } result, err := o.ymtClient.KeyOrder(&ymt_v3.KeyOrderRequest{ OutBizNo: req.ActOrderNum, ActivityNo: o.cfg.Ymt.ActivityNo, Account: req.Account, NotifyURL: selfPublicIP + ":" + o.cfg.ServerPort + "/callback/notify", }) if err != nil { return nil, err } orderInfo.CouponNo = result.URL orderInfo.CouponCode = result.Key orderInfo.OrderNo = result.TradeNo orderInfo.Status = int32(result.Status) var validTime time.Time if len(result.ValidEndTime) != 0 { validTime, err = time.Parse(time.DateTime, result.ValidEndTime) if err != nil { return nil, fmt.Errorf("解析失败: %v\n", err) } } orderInfo.ExpireTime = validTime if orderInfo.ID == 0 { err = o.orders.Add(ctx, &orderInfo) } if err != nil { return nil, err } return &entities.OrderResponseData{ OrderNo: result.TradeNo, CouponNo: result.URL, CouponCode: result.Key, Status: o.getStatus(result.Status, result.ValidEndTime, err), ExpireTime: result.ValidEndTime, }, nil } func (o *OrderStore) getStatus(ymtStatus uint32, ymtValidTime string, err error) int { if err != nil { return entities.StatusFailed } switch ymtStatus { case 2: return entities.StatusRedeemed case 3: return entities.StatusFailed default: t, err := time.Parse(time.DateTime, ymtValidTime) if err != nil { fmt.Printf("解析失败: %v\n", err) return entities.StatusFailed } if t.Unix() <= time.Now().Unix() { return entities.StatusExpired } return entities.StatusSuccess } } func (o *OrderStore) QueryOrder(req *entities.QueryOrderRequest) (*entities.QueryOrderResponseData, error) { result, err := o.ymtClient.KeyQuery(&ymt_v3.KeyQueryRequest{ TradeNo: req.OrderNo, }) if err != nil { return nil, err } resp := &entities.QueryOrderResponseData{ OrderNo: result.TradeNo, Status: o.getStatus(result.Status, result.ValidEndTime, err), Account: result.Account, } if result.URL != "" { resp.CardInfo = &entities.CardInfo{ CouponNo: result.URL, CouponCode: result.Key, ExpireTime: result.ValidEndTime, } } return resp, nil } func (o *OrderStore) HandleCallback(req *entities.CallBack) error { callBackData := &entities.CallbackRequest{ OrderNo: req.Data.TradeNo, ActOrderNum: req.Data.OutBizNo, Status: o.getStatus(req.Data.Status, req.Data.ValidEndTime, nil), Account: req.Data.Account, CardInfo: &entities.CardInfo{ CouponNo: req.Data.Url, CouponCode: req.Data.Key, ExpireTime: req.Data.ValidEndTime, }, } sign, jsonData, err := makeEncryptedRequest(callBackData) if err != nil { return err } requset := &l_request.Request{ Method: http.MethodPost, Json: jsonData, Url: o.cfg.NotifyUrl, Headers: map[string]string{ "Content-Type": "application/json", "timestamp": fmt.Sprintf("%d", time.Now().UnixMilli()), "sign": sign, }, } res, err := requset.Send() if err != nil { return err } if strings.ToLower(res.Text) != "ok" { } return nil } func makeEncryptedRequest(bizData interface{}) (string, map[string]interface{}, error) { bizJSON, err := json.Marshal(bizData) if err != nil { return "", nil, fmt.Errorf("业务数据序列化失败: %v", err) } encryptedData, err := crypto.SM4CBCEncrypt(bizJSON) if err != nil { return "", nil, fmt.Errorf("加密失败: %v", err) } timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10) sign := crypto.GenerateSign(timestamp, encryptedData) reqBody := entities.EncryptedRequest{ EncryptedData: encryptedData, } reqJSON, _ := pkg.StructToMap(reqBody) return sign, reqJSON, err }