f
This commit is contained in:
@@ -86,6 +86,23 @@ var productHandlers = map[string]queryHandlerFunc{
|
||||
"toc_VehicleMaintenanceDetail": runVehicleMaintenanceDetailReq,
|
||||
"toc_VehicleClaimDetail": runVehicleClaimDetailReq,
|
||||
"toc_VehicleClaimVerify": runVehicleClaimVerifyReq,
|
||||
// 核验工具(verify feature.md)
|
||||
"toc_PoliceTwoFactors": runVerifyAuthTwoReq,
|
||||
"toc_PoliceThreeFactors": runVerifyAuthThreeReq,
|
||||
"toc_ProfessionalCertificate": runVerifyCertReq,
|
||||
"toc_PersonalConsumptionCapacityLevel": runVerifyConsumptionReq, // 个人消费能力(沿用现有 product_en)
|
||||
"toc_OperatorTwoFactors": runVerifyYysTwoReq,
|
||||
"toc_MobileThreeFactors": runVerifyYysThreeReq,
|
||||
"toc_NumberRecycle": runVerifyMobileOnlyReq,
|
||||
"toc_MobileEmptyCheck": runVerifyMobileOnlyReq,
|
||||
"toc_MobilePortability": runVerifyMobileOnlyReq,
|
||||
"toc_MobileOnlineStatus": runVerifyMobileOnlyReq,
|
||||
"toc_MobileOnlineDuration": runVerifyMobileOnlyReq,
|
||||
"toc_MobileAttribution": runVerifyMobileOnlyReq,
|
||||
"toc_MobileConsumptionRange": runVerifyYysConsumptionReq,
|
||||
"toc_EnterpriseRelation": runVerifyEntRelationReq,
|
||||
"toc_BankcardFourFactors": runVerifyBankFourReq,
|
||||
"toc_BankcardBlacklist": runVerifyBankBlackReq,
|
||||
}
|
||||
|
||||
func (l *QueryServiceLogic) PreprocessLogic(req *types.QueryServiceReq, product string) (*types.QueryServiceResp, error) {
|
||||
@@ -242,8 +259,10 @@ func runVehicleVinCodeReq(l *QueryServiceLogic, decryptData []byte, product stri
|
||||
if validatorErr := validator.Validate(data); validatorErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
|
||||
}
|
||||
if verifyCodeErr := l.VerifyCode(data.Mobile, data.Code); verifyCodeErr != nil {
|
||||
return nil, verifyCodeErr
|
||||
if data.Mobile != "" && data.Code != "" {
|
||||
if verifyCodeErr := l.VerifyCode(data.Mobile, data.Code); verifyCodeErr != nil {
|
||||
return nil, verifyCodeErr
|
||||
}
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"vin_code": data.VinCode,
|
||||
@@ -259,13 +278,15 @@ func runVehicleMileageMixedReq(l *QueryServiceLogic, decryptData []byte, product
|
||||
if validatorErr := validator.Validate(data); validatorErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
|
||||
}
|
||||
if verifyCodeErr := l.VerifyCode(data.Mobile, data.Code); verifyCodeErr != nil {
|
||||
return nil, verifyCodeErr
|
||||
if data.Mobile != "" && data.Code != "" {
|
||||
if verifyCodeErr := l.VerifyCode(data.Mobile, data.Code); verifyCodeErr != nil {
|
||||
return nil, verifyCodeErr
|
||||
}
|
||||
}
|
||||
// 回调地址由后端在 ApiRequestService 中统一生成,此处不再下发 return_url
|
||||
return map[string]interface{}{
|
||||
"vin_code": data.VinCode,
|
||||
"return_url": data.ReturnURL,
|
||||
"image_url": data.ImageURL,
|
||||
"vin_code": data.VinCode,
|
||||
"image_url": data.ImageURL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -278,8 +299,10 @@ func runVehicleVinValuationReq(l *QueryServiceLogic, decryptData []byte, product
|
||||
if validatorErr := validator.Validate(data); validatorErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
|
||||
}
|
||||
if verifyCodeErr := l.VerifyCode(data.Mobile, data.Code); verifyCodeErr != nil {
|
||||
return nil, verifyCodeErr
|
||||
if data.Mobile != "" && data.Code != "" {
|
||||
if verifyCodeErr := l.VerifyCode(data.Mobile, data.Code); verifyCodeErr != nil {
|
||||
return nil, verifyCodeErr
|
||||
}
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"vin_code": data.VinCode,
|
||||
@@ -297,13 +320,15 @@ func runVehicleTransferSimpleReq(l *QueryServiceLogic, decryptData []byte, produ
|
||||
if validatorErr := validator.Validate(data); validatorErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
|
||||
}
|
||||
if verifyCodeErr := l.VerifyCode(data.Mobile, data.Code); verifyCodeErr != nil {
|
||||
return nil, verifyCodeErr
|
||||
if data.Mobile != "" && data.Code != "" {
|
||||
if verifyCodeErr := l.VerifyCode(data.Mobile, data.Code); verifyCodeErr != nil {
|
||||
return nil, verifyCodeErr
|
||||
}
|
||||
}
|
||||
return map[string]interface{}{"vin_code": data.VinCode}, nil
|
||||
}
|
||||
|
||||
// runVehicleMaintenanceSimpleReq 车辆维保简版 QCXG3Y6B(仅必填 vin_code, return_url)
|
||||
// runVehicleMaintenanceSimpleReq 车辆维保简版 QCXG3Y6B(仅必填 vin_code;回调地址后端自动生成)
|
||||
func runVehicleMaintenanceSimpleReq(l *QueryServiceLogic, decryptData []byte, product string) (map[string]interface{}, error) {
|
||||
var data types.TocVehicleMaintenanceSimpleReq
|
||||
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
|
||||
@@ -312,16 +337,18 @@ func runVehicleMaintenanceSimpleReq(l *QueryServiceLogic, decryptData []byte, pr
|
||||
if validatorErr := validator.Validate(data); validatorErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
|
||||
}
|
||||
if verifyCodeErr := l.VerifyCode(data.Mobile, data.Code); verifyCodeErr != nil {
|
||||
return nil, verifyCodeErr
|
||||
if data.Mobile != "" && data.Code != "" {
|
||||
if verifyCodeErr := l.VerifyCode(data.Mobile, data.Code); verifyCodeErr != nil {
|
||||
return nil, verifyCodeErr
|
||||
}
|
||||
}
|
||||
// 回调地址由后端在 ApiRequestService 中统一生成,此处不再下发 return_url
|
||||
return map[string]interface{}{
|
||||
"vin_code": data.VinCode,
|
||||
"return_url": data.ReturnURL,
|
||||
"vin_code": data.VinCode,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// runVehicleMaintenanceDetailReq 车辆维保详细版 QCXG3Z3L(仅必填 vin_code, return_url)
|
||||
// runVehicleMaintenanceDetailReq 车辆维保详细版 QCXG3Z3L(仅必填 vin_code;回调地址后端自动生成)
|
||||
func runVehicleMaintenanceDetailReq(l *QueryServiceLogic, decryptData []byte, product string) (map[string]interface{}, error) {
|
||||
var data types.TocVehicleMaintenanceDetailReq
|
||||
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
|
||||
@@ -330,16 +357,18 @@ func runVehicleMaintenanceDetailReq(l *QueryServiceLogic, decryptData []byte, pr
|
||||
if validatorErr := validator.Validate(data); validatorErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
|
||||
}
|
||||
if verifyCodeErr := l.VerifyCode(data.Mobile, data.Code); verifyCodeErr != nil {
|
||||
return nil, verifyCodeErr
|
||||
if data.Mobile != "" && data.Code != "" {
|
||||
if verifyCodeErr := l.VerifyCode(data.Mobile, data.Code); verifyCodeErr != nil {
|
||||
return nil, verifyCodeErr
|
||||
}
|
||||
}
|
||||
// 回调地址由后端在 ApiRequestService 中统一生成,此处不再下发 return_url
|
||||
return map[string]interface{}{
|
||||
"vin_code": data.VinCode,
|
||||
"return_url": data.ReturnURL,
|
||||
"vin_code": data.VinCode,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// runVehicleClaimDetailReq 车辆出险详版 QCXGP00W(仅必填 vin_code, return_url, vlphoto_data),vlphoto_data 由 API 层加密为 data
|
||||
// runVehicleClaimDetailReq 车辆出险详版 QCXGP00W(仅必填 vin_code, vlphoto_data;回调地址后端自动生成),vlphoto_data 由 API 层加密为 data
|
||||
func runVehicleClaimDetailReq(l *QueryServiceLogic, decryptData []byte, product string) (map[string]interface{}, error) {
|
||||
var data types.TocVehicleClaimDetailReq
|
||||
if unmarshalErr := json.Unmarshal(decryptData, &data); unmarshalErr != nil {
|
||||
@@ -348,12 +377,14 @@ func runVehicleClaimDetailReq(l *QueryServiceLogic, decryptData []byte, product
|
||||
if validatorErr := validator.Validate(data); validatorErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
|
||||
}
|
||||
if verifyCodeErr := l.VerifyCode(data.Mobile, data.Code); verifyCodeErr != nil {
|
||||
return nil, verifyCodeErr
|
||||
if data.Mobile != "" && data.Code != "" {
|
||||
if verifyCodeErr := l.VerifyCode(data.Mobile, data.Code); verifyCodeErr != nil {
|
||||
return nil, verifyCodeErr
|
||||
}
|
||||
}
|
||||
// 回调地址由后端在 ApiRequestService 中统一生成,此处不再下发 return_url
|
||||
return map[string]interface{}{
|
||||
"vin_code": data.VinCode,
|
||||
"return_url": data.ReturnURL,
|
||||
"vlphoto_data": data.VlphotoData,
|
||||
}, nil
|
||||
}
|
||||
@@ -367,12 +398,151 @@ func runVehicleClaimVerifyReq(l *QueryServiceLogic, decryptData []byte, product
|
||||
if validatorErr := validator.Validate(data); validatorErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, validatorErr.Error()), "查询服务, 参数不正确: %+v", validatorErr)
|
||||
}
|
||||
if verifyCodeErr := l.VerifyCode(data.Mobile, data.Code); verifyCodeErr != nil {
|
||||
return nil, verifyCodeErr
|
||||
if data.Mobile != "" && data.Code != "" {
|
||||
if verifyCodeErr := l.VerifyCode(data.Mobile, data.Code); verifyCodeErr != nil {
|
||||
return nil, verifyCodeErr
|
||||
}
|
||||
}
|
||||
auth := data.Authorized
|
||||
if auth == "" {
|
||||
auth = "1"
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"vin_code": data.VINCode,
|
||||
"authorized": data.Authorized,
|
||||
"authorized": auth,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// --------------- 核验工具 handlers ---------------
|
||||
func runVerifyAuthTwoReq(l *QueryServiceLogic, decryptData []byte, product string) (map[string]interface{}, error) {
|
||||
var data types.TocVerifyAuthTwoReq
|
||||
if err := json.Unmarshal(decryptData, &data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", err)
|
||||
}
|
||||
if err := validator.Validate(data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, err.Error()), "查询服务, 参数不正确: %+v", err)
|
||||
}
|
||||
return map[string]interface{}{"mobile_no": data.MobileNo, "id_card": data.IDCard, "name": data.Name}, nil
|
||||
}
|
||||
|
||||
func runVerifyAuthThreeReq(l *QueryServiceLogic, decryptData []byte, product string) (map[string]interface{}, error) {
|
||||
var data types.TocVerifyAuthThreeReq
|
||||
if err := json.Unmarshal(decryptData, &data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", err)
|
||||
}
|
||||
if err := validator.Validate(data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, err.Error()), "查询服务, 参数不正确: %+v", err)
|
||||
}
|
||||
return map[string]interface{}{"photo_data": data.PhotoData, "id_card": data.IDCard, "name": data.Name}, nil
|
||||
}
|
||||
|
||||
func runVerifyCertReq(l *QueryServiceLogic, decryptData []byte, product string) (map[string]interface{}, error) {
|
||||
var data types.TocVerifyCertReq
|
||||
if err := json.Unmarshal(decryptData, &data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", err)
|
||||
}
|
||||
if err := validator.Validate(data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, err.Error()), "查询服务, 参数不正确: %+v", err)
|
||||
}
|
||||
return map[string]interface{}{"id_card": data.IDCard, "name": data.Name}, nil
|
||||
}
|
||||
|
||||
func runVerifyConsumptionReq(l *QueryServiceLogic, decryptData []byte, product string) (map[string]interface{}, error) {
|
||||
var data types.TocVerifyConsumptionReq
|
||||
if err := json.Unmarshal(decryptData, &data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", err)
|
||||
}
|
||||
if err := validator.Validate(data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, err.Error()), "查询服务, 参数不正确: %+v", err)
|
||||
}
|
||||
return map[string]interface{}{"mobile_no": data.MobileNo, "id_card": data.IDCard, "name": data.Name}, nil
|
||||
}
|
||||
|
||||
func runVerifyYysTwoReq(l *QueryServiceLogic, decryptData []byte, product string) (map[string]interface{}, error) {
|
||||
var data types.TocVerifyYysTwoReq
|
||||
if err := json.Unmarshal(decryptData, &data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", err)
|
||||
}
|
||||
if err := validator.Validate(data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, err.Error()), "查询服务, 参数不正确: %+v", err)
|
||||
}
|
||||
return map[string]interface{}{"mobile_no": data.MobileNo, "name": data.Name}, nil
|
||||
}
|
||||
|
||||
func runVerifyYysThreeReq(l *QueryServiceLogic, decryptData []byte, product string) (map[string]interface{}, error) {
|
||||
var data types.TocVerifyYysThreeReq
|
||||
if err := json.Unmarshal(decryptData, &data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", err)
|
||||
}
|
||||
if err := validator.Validate(data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, err.Error()), "查询服务, 参数不正确: %+v", err)
|
||||
}
|
||||
return map[string]interface{}{"mobile_no": data.MobileNo, "id_card": data.IDCard, "name": data.Name}, nil
|
||||
}
|
||||
|
||||
func runVerifyMobileOnlyReq(l *QueryServiceLogic, decryptData []byte, product string) (map[string]interface{}, error) {
|
||||
var data types.TocVerifyMobileOnlyReq
|
||||
if err := json.Unmarshal(decryptData, &data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", err)
|
||||
}
|
||||
if err := validator.Validate(data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, err.Error()), "查询服务, 参数不正确: %+v", err)
|
||||
}
|
||||
return map[string]interface{}{"mobile_no": data.MobileNo}, nil
|
||||
}
|
||||
|
||||
func runVerifyYysConsumptionReq(l *QueryServiceLogic, decryptData []byte, product string) (map[string]interface{}, error) {
|
||||
var data types.TocVerifyYysConsumptionReq
|
||||
if err := json.Unmarshal(decryptData, &data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", err)
|
||||
}
|
||||
if err := validator.Validate(data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, err.Error()), "查询服务, 参数不正确: %+v", err)
|
||||
}
|
||||
return map[string]interface{}{"mobile_no": data.MobileNo, "authorized": data.Authorized}, nil
|
||||
}
|
||||
|
||||
func runVerifyEntRelationReq(l *QueryServiceLogic, decryptData []byte, product string) (map[string]interface{}, error) {
|
||||
var data types.TocVerifyEntRelationReq
|
||||
if err := json.Unmarshal(decryptData, &data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", err)
|
||||
}
|
||||
out := map[string]interface{}{}
|
||||
if data.IDCard != "" {
|
||||
out["id_card"] = data.IDCard
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func runVerifyBankFourReq(l *QueryServiceLogic, decryptData []byte, product string) (map[string]interface{}, error) {
|
||||
var data types.TocVerifyBankFourReq
|
||||
if err := json.Unmarshal(decryptData, &data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", err)
|
||||
}
|
||||
if err := validator.Validate(data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, err.Error()), "查询服务, 参数不正确: %+v", err)
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"mobile_no": data.MobileNo,
|
||||
"id_card": data.IDCard,
|
||||
"bank_card": data.BankCard,
|
||||
"name": data.Name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func runVerifyBankBlackReq(l *QueryServiceLogic, decryptData []byte, product string) (map[string]interface{}, error) {
|
||||
var data types.TocVerifyBankBlackReq
|
||||
if err := json.Unmarshal(decryptData, &data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "查询服务, 解密后的数据格式不正确: %+v", err)
|
||||
}
|
||||
if err := validator.Validate(data); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCodeMsg(xerr.PARAM_VERIFICATION_ERROR, err.Error()), "查询服务, 参数不正确: %+v", err)
|
||||
}
|
||||
return map[string]interface{}{
|
||||
"mobile_no": data.MobileNo,
|
||||
"id_card": data.IDCard,
|
||||
"name": data.Name,
|
||||
"bank_card": data.BankCard,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
164
app/main/api/internal/logic/tianyuan/vehiclecallbacklogic.go
Normal file
164
app/main/api/internal/logic/tianyuan/vehiclecallbacklogic.go
Normal file
@@ -0,0 +1,164 @@
|
||||
package tianyuan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"tyc-server/app/main/api/internal/service"
|
||||
"tyc-server/app/main/api/internal/svc"
|
||||
"tyc-server/app/main/model"
|
||||
"tyc-server/common/xerr"
|
||||
"tyc-server/pkg/lzkit/crypto"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
// VehicleCallbackLogic 处理天远车辆类接口的异步回调
|
||||
// 设计目标:
|
||||
// - 按 order_no + api_id 找到对应的查询记录
|
||||
// - 解密原有 query_data([]APIResponseData),更新或追加当前 api_id 的结果
|
||||
// - 再次加密写回 query.query_data,必要时将 query_state 从 pending 更新为 success
|
||||
type VehicleCallbackLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewVehicleCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *VehicleCallbackLogic {
|
||||
return &VehicleCallbackLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
// 与 paySuccessNotify 中一致:仅通过异步回调回写结果的车辆接口
|
||||
var asyncVehicleApiIDs = map[string]bool{
|
||||
"QCXG1U4U": true, "QCXG3Y6B": true, "QCXG3Z3L": true, "QCXGP00W": true,
|
||||
}
|
||||
|
||||
// allAsyncVehicleReceived 判断 apiList 中所有“异步车辆接口”是否均已收到回调(Success 为 true)
|
||||
func allAsyncVehicleReceived(apiList []service.APIResponseData) bool {
|
||||
for _, item := range apiList {
|
||||
if asyncVehicleApiIDs[item.ApiID] && !item.Success {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Handle 入口:直接接收原始 *http.Request,方便读取 query / body
|
||||
func (l *VehicleCallbackLogic) Handle(r *http.Request) error {
|
||||
apiID := r.URL.Query().Get("api_id")
|
||||
orderNo := r.URL.Query().Get("order_no")
|
||||
|
||||
if apiID == "" || orderNo == "" {
|
||||
return errors.Wrapf(
|
||||
xerr.NewErrMsg("缺少 api_id 或 order_no"),
|
||||
"tianyuan vehicle callback, api_id=%s, order_no=%s", apiID, orderNo,
|
||||
)
|
||||
}
|
||||
|
||||
bodyBytes, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "读取回调 Body 失败: %v", err)
|
||||
}
|
||||
|
||||
// 1. 根据订单号找到订单
|
||||
order, err := l.svcCtx.OrderModel.FindOneByOrderNo(l.ctx, orderNo)
|
||||
if err != nil {
|
||||
if err == model.ErrNotFound {
|
||||
return errors.Wrapf(xerr.NewErrMsg("未找到订单"), "order_no=%s", orderNo)
|
||||
}
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询订单失败, order_no=%s, err=%v", orderNo, err)
|
||||
}
|
||||
|
||||
// 2. 根据订单ID找到对应查询记录
|
||||
query, err := l.svcCtx.QueryModel.FindOneByOrderId(l.ctx, order.Id)
|
||||
if err != nil {
|
||||
if err == model.ErrNotFound {
|
||||
return errors.Wrapf(xerr.NewErrMsg("未找到查询记录"), "order_no=%s, order_id=%d", orderNo, order.Id)
|
||||
}
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "查询 query 记录失败, order_id=%d, err=%v", order.Id, err)
|
||||
}
|
||||
|
||||
// 3. 获取加密密钥
|
||||
secretKey := l.svcCtx.Config.Encrypt.SecretKey
|
||||
key, decodeErr := hex.DecodeString(secretKey)
|
||||
if decodeErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解析 AES 密钥失败: %v", decodeErr)
|
||||
}
|
||||
// 4. 解密 query_data,反序列化为 []APIResponseData
|
||||
var apiList []service.APIResponseData
|
||||
if query.QueryData.Valid && query.QueryData.String != "" {
|
||||
decrypted, decErr := crypto.AesDecrypt(query.QueryData.String, key)
|
||||
if decErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解密 query_data 失败: %v", decErr)
|
||||
}
|
||||
if len(decrypted) > 0 {
|
||||
if err := json.Unmarshal(decrypted, &apiList); err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "解析 query_data 为 APIResponseData 列表失败: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 5. 更新或追加当前 api_id 的结果
|
||||
nowStr := time.Now().Format("2006-01-02 15:04:05")
|
||||
updated := false
|
||||
for i := range apiList {
|
||||
if apiList[i].ApiID == apiID {
|
||||
apiList[i].Data = json.RawMessage(bodyBytes)
|
||||
apiList[i].Success = true
|
||||
apiList[i].Timestamp = nowStr
|
||||
apiList[i].Error = ""
|
||||
updated = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !updated {
|
||||
apiList = append(apiList, service.APIResponseData{
|
||||
ApiID: apiID,
|
||||
Data: json.RawMessage(bodyBytes),
|
||||
Success: true,
|
||||
Timestamp: nowStr,
|
||||
})
|
||||
}
|
||||
|
||||
// 6. 重新序列化并加密,写回查询记录
|
||||
merged, err := json.Marshal(apiList)
|
||||
if err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "序列化合并后的 query_data 失败: %v", err)
|
||||
}
|
||||
enc, encErr := crypto.AesEncrypt(merged, key)
|
||||
if encErr != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "加密合并后的 query_data 失败: %v", encErr)
|
||||
}
|
||||
|
||||
query.QueryData.String = enc
|
||||
query.QueryData.Valid = true
|
||||
// 仅当所有异步车辆接口均已有回调结果时,才将 pending 置为 success,并触发代理结算
|
||||
wasPending := query.QueryState == "pending"
|
||||
didSetSuccess := wasPending && allAsyncVehicleReceived(apiList)
|
||||
if didSetSuccess {
|
||||
query.QueryState = "success"
|
||||
}
|
||||
|
||||
if err := l.svcCtx.QueryModel.UpdateWithVersion(l.ctx, nil, query); err != nil {
|
||||
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "更新查询记录失败, query_id=%d, err=%v", query.Id, err)
|
||||
}
|
||||
|
||||
if didSetSuccess {
|
||||
if agentErr := l.svcCtx.AgentService.AgentProcess(l.ctx, order); agentErr != nil {
|
||||
l.Errorf("tianyuan vehicle callback, AgentProcess failed, order_no=%s, err=%v", orderNo, agentErr)
|
||||
// 不因代理处理失败而整体失败,回调已成功落库
|
||||
}
|
||||
}
|
||||
|
||||
l.Infof("tianyuan vehicle callback handled, order_no=%s, api_id=%s, query_id=%d", orderNo, apiID, query.Id)
|
||||
return nil
|
||||
}
|
||||
66
app/main/api/internal/logic/upload/serveuploadedfilelogic.go
Normal file
66
app/main/api/internal/logic/upload/serveuploadedfilelogic.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package upload
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"tyc-server/app/main/api/internal/svc"
|
||||
"tyc-server/app/main/api/internal/types"
|
||||
"tyc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
type ServeUploadedFileLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewServeUploadedFileLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ServeUploadedFileLogic {
|
||||
return &ServeUploadedFileLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *ServeUploadedFileLogic) ServeUploadedFile(req *types.ServeUploadedFileReq) (resp *types.ServeUploadedFileResp, err error) {
|
||||
fileName := strings.TrimSpace(req.FileName)
|
||||
if fileName == "" {
|
||||
return nil, errors.Wrap(xerr.NewErrMsg("缺少文件名"), "fileName empty")
|
||||
}
|
||||
// 只允许文件名,禁止路径穿越
|
||||
if strings.Contains(fileName, "..") || filepath.Base(fileName) != fileName {
|
||||
return nil, errors.Wrap(xerr.NewErrMsg("非法文件名"), fileName)
|
||||
}
|
||||
|
||||
candidates := []string{
|
||||
"data/uploads",
|
||||
"../data/uploads",
|
||||
"../../data/uploads",
|
||||
"../../../data/uploads",
|
||||
}
|
||||
for _, c := range candidates {
|
||||
abs, _ := filepath.Abs(c)
|
||||
fullPath := filepath.Join(abs, fileName)
|
||||
if info, err := os.Stat(fullPath); err == nil && !info.IsDir() {
|
||||
contentType := "image/jpeg"
|
||||
if strings.HasSuffix(strings.ToLower(fileName), ".png") {
|
||||
contentType = "image/png"
|
||||
} else if strings.HasSuffix(strings.ToLower(fileName), ".gif") {
|
||||
contentType = "image/gif"
|
||||
} else if strings.HasSuffix(strings.ToLower(fileName), ".webp") {
|
||||
contentType = "image/webp"
|
||||
}
|
||||
return &types.ServeUploadedFileResp{
|
||||
FilePath: fullPath,
|
||||
ContentType: contentType,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("文件不存在"), "fileName=%s", fileName)
|
||||
}
|
||||
138
app/main/api/internal/logic/upload/uploadimagelogic.go
Normal file
138
app/main/api/internal/logic/upload/uploadimagelogic.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package upload
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"tyc-server/app/main/api/internal/svc"
|
||||
"tyc-server/app/main/api/internal/types"
|
||||
"tyc-server/common/xerr"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/zeromicro/go-zero/core/logx"
|
||||
)
|
||||
|
||||
const maxImageSize = 3 * 1024 * 1024 // 3MB
|
||||
const defaultTempFileMaxAgeH = 24
|
||||
|
||||
type UploadImageLogic struct {
|
||||
logx.Logger
|
||||
ctx context.Context
|
||||
svcCtx *svc.ServiceContext
|
||||
}
|
||||
|
||||
func NewUploadImageLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UploadImageLogic {
|
||||
return &UploadImageLogic{
|
||||
Logger: logx.WithContext(ctx),
|
||||
ctx: ctx,
|
||||
svcCtx: svcCtx,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *UploadImageLogic) UploadImage(req *types.UploadImageReq) (resp *types.UploadImageResp, err error) {
|
||||
decoded, decErr := base64.StdEncoding.DecodeString(req.ImageBase64)
|
||||
if decErr != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("图片 base64 格式错误"), "%v", decErr)
|
||||
}
|
||||
if len(decoded) > maxImageSize {
|
||||
return nil, errors.Wrapf(xerr.NewErrMsg("图片不能超过 3M"), "size=%d", len(decoded))
|
||||
}
|
||||
|
||||
// 按文件内容 hash 命名,相同文件复用同一 URL,避免重复传输与刷流量
|
||||
hashSum := sha256.Sum256(decoded)
|
||||
hashHex := hex.EncodeToString(hashSum[:])
|
||||
fileName := hashHex + ".jpg"
|
||||
|
||||
dir := l.uploadStoragePath()
|
||||
if err := os.MkdirAll(dir, 0755); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "创建上传目录失败: %v", err)
|
||||
}
|
||||
|
||||
filePath := filepath.Join(dir, fileName)
|
||||
// 若已存在同 hash 文件,直接返回 URL,不重复写入
|
||||
if _, statErr := os.Stat(filePath); statErr == nil {
|
||||
url := l.buildURL(fileName)
|
||||
logx.Infof("upload image dedup by hash, file=%s", fileName)
|
||||
return &types.UploadImageResp{Url: url}, nil
|
||||
}
|
||||
|
||||
if err := os.WriteFile(filePath, decoded, 0644); err != nil {
|
||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "保存图片失败: %v", err)
|
||||
}
|
||||
|
||||
// 异步清理过期临时文件,不阻塞响应
|
||||
go l.deleteOldUploads(dir)
|
||||
|
||||
url := l.buildURL(fileName)
|
||||
logx.Infof("upload image ok, file=%s", fileName)
|
||||
return &types.UploadImageResp{Url: url}, nil
|
||||
}
|
||||
|
||||
func (l *UploadImageLogic) buildURL(fileName string) string {
|
||||
baseURL := l.svcCtx.Config.Upload.FileBaseURL
|
||||
if baseURL == "" {
|
||||
baseURL = l.svcCtx.Config.AdminPromotion.URLDomain
|
||||
if baseURL != "" {
|
||||
baseURL = baseURL + "/api/v1/upload/file"
|
||||
}
|
||||
}
|
||||
if baseURL == "" {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf("%s/%s", baseURL, fileName)
|
||||
}
|
||||
|
||||
func (l *UploadImageLogic) uploadStoragePath() string {
|
||||
candidates := []string{
|
||||
"data/uploads",
|
||||
"../data/uploads",
|
||||
"../../data/uploads",
|
||||
"../../../data/uploads",
|
||||
}
|
||||
for _, c := range candidates {
|
||||
abs, _ := filepath.Abs(c)
|
||||
if err := os.MkdirAll(abs, 0755); err == nil {
|
||||
return abs
|
||||
}
|
||||
}
|
||||
abs, _ := filepath.Abs(candidates[0])
|
||||
return abs
|
||||
}
|
||||
|
||||
// deleteOldUploads 删除目录下超过保留时长的临时文件
|
||||
func (l *UploadImageLogic) deleteOldUploads(dir string) {
|
||||
maxAgeH := l.svcCtx.Config.Upload.TempFileMaxAgeH
|
||||
if maxAgeH <= 0 {
|
||||
maxAgeH = defaultTempFileMaxAgeH
|
||||
}
|
||||
cutoff := time.Now().Add(-time.Duration(maxAgeH) * time.Hour)
|
||||
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
l.Errorf("deleteOldUploads ReadDir: %v", err)
|
||||
return
|
||||
}
|
||||
for _, e := range entries {
|
||||
if e.IsDir() {
|
||||
continue
|
||||
}
|
||||
path := filepath.Join(dir, e.Name())
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if info.ModTime().Before(cutoff) {
|
||||
if err := os.Remove(path); err != nil {
|
||||
l.Errorf("deleteOldUploads Remove %s: %v", path, err)
|
||||
} else {
|
||||
l.Infof("deleteOldUploads removed %s", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user