|
|
@@ -9,6 +9,9 @@ import (
|
|
|
"go-policy-service/models"
|
|
|
"go-policy-service/structs"
|
|
|
"go-policy-service/utils"
|
|
|
+ "math"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
)
|
|
|
|
|
|
type HangguanService struct {
|
|
|
@@ -75,20 +78,159 @@ func (h *HangguanService) RequestSFlightData(ctx context.Context, task models.Hg
|
|
|
}
|
|
|
|
|
|
// 处理响应数据
|
|
|
- err = processResponse(srchResp.Data)
|
|
|
+ err = h.processResponse(ctx, &task, &srchResp.Data)
|
|
|
if err != nil {
|
|
|
utils.Logger.WithField("task_id", task.ID).Error("Response processing failed: ", err)
|
|
|
return
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-func processResponse(response structs.ResSearchFlightData) error {
|
|
|
+func (h *HangguanService) processResponse(ctx context.Context, task *models.HgFlightSearchTask, response *structs.ResSearchFlightData) error {
|
|
|
// 实现具体的响应处理逻辑
|
|
|
if response.Total <= 0 {
|
|
|
return fmt.Errorf("返回数据为空")
|
|
|
}
|
|
|
|
|
|
-
|
|
|
+ pushDataList := make([]structs.Datum, 0)
|
|
|
+ for _, fltData := range response.Datas {
|
|
|
+ if fltData.FlightInfo.IsCancel == 1 ||
|
|
|
+ len(fltData.CabinInfos) == 0 { //航班取消 或者 无舱位信息 或者 无实际承运航司
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ var minPrice float64 = 0
|
|
|
+ var minPricePushData structs.Datum
|
|
|
+ CabinLoop:
|
|
|
+ for _, cabinPrice := range fltData.CabinInfos {
|
|
|
+ //无售卖控制 或者 产品类型为 py
|
|
|
+ if cabinPrice.AdtPrice.ProductType == "py" {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ if len(cabinPrice.SaleControls) > 0 {
|
|
|
+ for _, saleControls := range cabinPrice.SaleControls {
|
|
|
+ // ... existing code ...
|
|
|
+ if saleControls.PsNum != "" ||
|
|
|
+ saleControls.PsType == "ADT+CHD" ||
|
|
|
+ saleControls.PsIDType != "" ||
|
|
|
+ saleControls.PsIDNo != "" ||
|
|
|
+ saleControls.PsAdIdType != "" ||
|
|
|
+ saleControls.NewMember == 1 ||
|
|
|
+ saleControls.CheckThreeElement != 0 ||
|
|
|
+ saleControls.HasAge != "" {
|
|
|
+ continue CabinLoop
|
|
|
+ }
|
|
|
+ if saleControls.PsAge != "" ||
|
|
|
+ !isRangeClose(saleControls.PsAge, 12, 100, 2, 0) {
|
|
|
+ continue CabinLoop
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
+ if minPrice == 0 || cabinPrice.AdtPrice.Price < minPrice { //取最低价格的舱位信息
|
|
|
+ minPrice = cabinPrice.AdtPrice.Price
|
|
|
+ minPricePushData = structs.Datum{
|
|
|
+ DepAir: fltData.FlightInfo.DepCode,
|
|
|
+ ArrAir: fltData.FlightInfo.ArrCode,
|
|
|
+ FlightStartDate: utils.DateFormmat(fltData.FlightInfo.DepDateTime, "2006-01-02"),
|
|
|
+ PrintPrice: cabinPrice.AdtPrice.Price,
|
|
|
+ Stock: cabinPrice.Left,
|
|
|
+ FlightNo: fltData.FlightInfo.FlyNo,
|
|
|
+ FlightEndDate: utils.DateFormmat(fltData.FlightInfo.ArrDateTime, "2006-01-02"),
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if minPricePushData == (structs.Datum{}) { //无可用舱位
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ pushDataList = append(pushDataList, minPricePushData)
|
|
|
+ }
|
|
|
+ // 构建推送数据
|
|
|
+ pushData := structs.PushData{
|
|
|
+ Data: pushDataList,
|
|
|
+ IsPublishImmediately: 1,
|
|
|
+ Query: structs.Query{
|
|
|
+ DepAir: task.Dep,
|
|
|
+ ArrAir: task.Arr,
|
|
|
+ DepDate: utils.TimestampToString(int64(task.Date), "2006-01-02"),
|
|
|
+ },
|
|
|
+ ServiceTag: "hgSpecail",
|
|
|
+ }
|
|
|
+ // 推送数据
|
|
|
+ if err := h.PushPolicyData(ctx, pushData); err != nil {
|
|
|
+ utils.Logger.WithField("task_id", task.ID).Error("推送失败: ", err)
|
|
|
+ return err
|
|
|
+ }
|
|
|
return nil
|
|
|
}
|
|
|
+
|
|
|
+func (h *HangguanService) PushPolicyData(ctx context.Context, data structs.PushData) error {
|
|
|
+ pushData, err := json.Marshal(data)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("推送数据JSON 编码失败: %w", err)
|
|
|
+ }
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("推送数据序列化失败: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ url := h.cfg.PushUrl
|
|
|
+ resp, err := h.httpClient.RequestJSON(ctx, "POST", url, pushData)
|
|
|
+ if err != nil {
|
|
|
+ return fmt.Errorf("推送请求失败: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ var result struct {
|
|
|
+ Code int `json:"code"`
|
|
|
+ Message string `json:"message"`
|
|
|
+ }
|
|
|
+ if err := json.Unmarshal(resp, &result); err != nil {
|
|
|
+ return fmt.Errorf("响应解析失败: %w", err)
|
|
|
+ }
|
|
|
+
|
|
|
+ if result.Code != 200 {
|
|
|
+ return fmt.Errorf("推送接口返回错误: %s", result.Message)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+* 年龄限制
|
|
|
+* 儿童期 1-12岁
|
|
|
+* 青年 15-24
|
|
|
+* 老年 60/65+
|
|
|
+* 成年 18岁+
|
|
|
+* 目标范围:[10, 100]
|
|
|
+ */
|
|
|
+// isRangeClose 检查 ranges 字符串中,是否存在一个子区间 [s,e]:
|
|
|
+// 1) 与 [targetStart,targetEnd] 有交集;
|
|
|
+// 2) |s - targetStart| ≤ startTol 且 |e - targetEnd| ≤ endTol
|
|
|
+//
|
|
|
+// ranges 格式示例:"[25,60]|[23,60]|[70,80]"
|
|
|
+// startTol、endTol 单位同年龄(比如 startTol=5 表示起点最多相差 5 岁,endTol=10 表示终点最多相差 10 岁)
|
|
|
+func isRangeClose(ranges string, targetStart, targetEnd, startTol, endTol int) bool {
|
|
|
+ parts := strings.Split(ranges, "|")
|
|
|
+ if len(parts) >= 4 { // 有4段年龄范围以上的过滤
|
|
|
+ return false
|
|
|
+ }
|
|
|
+ for _, part := range parts {
|
|
|
+ part = strings.Trim(part, "[]")
|
|
|
+ bounds := strings.Split(part, ",")
|
|
|
+ if len(bounds) != 2 {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ s, err1 := strconv.Atoi(strings.TrimSpace(bounds[0]))
|
|
|
+ e, err2 := strconv.Atoi(strings.TrimSpace(bounds[1]))
|
|
|
+ if err1 != nil || err2 != nil {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+
|
|
|
+ // 1) 必须有交集
|
|
|
+ if e < targetStart || s > targetEnd {
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ // 2) 分别判断起点、终点容差
|
|
|
+ if math.Abs(float64(s-targetStart)) <= float64(startTol) &&
|
|
|
+ math.Abs(float64(e-targetEnd)) <= float64(endTol) {
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|