This commit is contained in:
2024-09-14 10:48:09 +08:00
commit a5fa833937
192 changed files with 87641 additions and 0 deletions

716
api/car.go Normal file
View File

@@ -0,0 +1,716 @@
package api
import (
"errors"
"fmt"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"io"
"log"
"qnc-server/config"
"qnc-server/db"
"qnc-server/model/model"
"qnc-server/model/request"
"qnc-server/model/response"
"qnc-server/service"
"qnc-server/utils"
"strconv"
"sync"
)
type Car struct {
}
var carService service.CarService
// 注册路由
func InitCar(group *gin.RouterGroup) {
var c Car
{
carPrivateGroup := group.Group("car")
carPrivateGroup.Use(JWTAuth())
carPrivateGroup.POST("insurance", c.Insurance)
carPrivateGroup.POST("maintenance", c.Maintenance)
carPrivateGroup.GET("get_query", c.GetQueryRecord)
carPrivateGroup.POST("person_car_verify", c.PersonCarVerify)
carPrivateGroup.POST("under_name", c.UnderName)
carPrivateGroup.POST("insurance_info", c.InsuranceInfo)
//carPrivateGroup.POST("new_energy_power", c.NewEnergyPower)
carPrivateGroup.POST("vin_check_info", c.VinCheckInfo)
carPrivateGroup.POST("vehicle_transfer", c.VehicleTransfer)
carPrivateGroup.POST("vehicle_valuation", c.VehicleValuation)
carPrivateGroup.GET("get_maintenance_history", c.QueryHistoryList)
}
{
carPublicGroup := group.Group("car")
carPublicGroup.POST("callback", c.CarCallback)
}
}
// 出险综合查询
func (car *Car) Insurance(c *gin.Context) {
var reqBody request.CarInsuranceReq
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
userid := utils.GetUserID(c)
// 查找是否有 未消费 已付费 的订单
order, err := orderService.QueryConsumedOrder(userid, "chexian")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 为空就没有消费资格
response.OkNeedPay(c)
return
} else {
log.Printf("QueryConsumedOrder err:%s", err.Error())
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
}
var carInsurance model.CarInsurance
reqData := map[string]interface{}{
"vin": reqBody.Vin,
}
plainText, err := carService.CarReqYuShan("CAR074", &reqData)
if err != nil {
log.Printf("YuShan request at %s error %s", "CAR074", err.Error())
notifyService.SendNotification("羽山接口请求错误,请及时处理", "出险综合查询", userid, order.ID)
response.FailRefund(c)
return
}
_, err = carService.CarInsuranceDataAnalysis(string(plainText))
if err != nil {
if errors.Is(err, utils.ErrNoRecord) {
notifyService.SendNotification("用户查询数据为空,请及时处理", "出险综合查询", userid, order.ID)
} else {
notifyService.SendNotification("接口系统响应错误,请及时处理", "出险综合查询", userid, order.ID)
}
response.FailRefund(c)
return
}
carInsurance.Resp = string(plainText)
carInsurance.Vin = reqBody.Vin
carInsurance.UserID = userid
carInsurance.OrderID = order.ID
err = db.DB.Create(&carInsurance).Error
if err != nil {
log.Printf("汽车出险综合记录失败:%v", err)
notifyService.SendNotification("汽车查询数据保存错误,请及时处理", "出险综合查询", userid, order.ID)
response.FailRefund(c)
return
}
orderService.OrderConsumed(order)
response.OkWithData(gin.H{
"record_id": carInsurance.OrderID,
}, c)
}
// 维保记录
func (car *Car) Maintenance(c *gin.Context) {
var reqBody request.CarMaintenanceReq
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
userid := utils.GetUserID(c)
order, err := orderService.QueryConsumedOrder(userid, "weibao")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 为空就没有消费资格
response.OkNeedPay(c)
return
} else {
log.Printf("QueryConsumedOrder err:%s", err.Error())
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
}
notifySN, _ := utils.GenerateRandomString()
var carMaintenance model.CarMaintenance
reqData := map[string]interface{}{
"vin": reqBody.Vin,
"image": reqBody.ImageBase64,
"notifyUrl": fmt.Sprintf("%s?notify_sn=%s", config.ConfigData.Car.Callback, notifySN),
}
plainText, err := carService.CarReqYuShan("CAR058", &reqData)
if err != nil {
log.Printf("YuShan request at %s error %s", "CAR058", err.Error())
notifyService.SendNotification("羽山接口请求错误,请及时处理", "维保记录CAR058", userid, order.ID)
response.FailRefund(c)
return
}
data, err := carService.CarDataAnalysis(string(plainText))
if err != nil {
if errors.Is(err, utils.ErrNoRecord) {
notifyService.SendNotification("用户查询数据为空,请及时处理", "维保记录CAR058", userid, order.ID)
} else {
notifyService.SendNotification("接口系统响应错误,请及时处理", "维保记录CAR058", userid, order.ID)
}
response.FailRefund(c)
return
}
// 检查 report 中是否存在 "orderId"
orderId, exists := data.Retdata["orderId"]
if !exists {
log.Printf("orderId不存在")
response.FailWithMessage("信息输入不匹配,无相关数据", c)
return
}
carMaintenance.Vin = reqBody.Vin
carMaintenance.UserID = userid
carMaintenance.YuShanOrderID = orderId.(string)
carMaintenance.OrderID = order.ID
carMaintenance.Status = model.StatusPending
carMaintenance.NotifySN = notifySN
err = db.DB.Create(&carMaintenance).Error
if err != nil {
log.Printf("汽车出险综合订单记录失败:%v", err)
notifyService.SendNotification("汽车查询订单保存错误,请及时处理", "维保记录", userid, order.ID)
response.FailRefund(c)
return
}
orderService.OrderConsumed(order)
response.OkWait(c)
}
func (car *Car) GetQueryRecord(c *gin.Context) {
var req request.GetQueryRecordReq
if err := c.ShouldBindQuery(&req); err != nil {
response.FailWithMessage("Failed to read request", c)
return
}
userid := utils.GetUserID(c)
var record interface{}
var err error
if req.IsCase {
record, err = carService.GetRecordCaseByFeature(req.Feature)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
log.Printf("无相关示例记录:%v", err)
response.FailWithMessage("无相关示例记录", c)
return
} else {
log.Printf("查询示例记录错误:%v", err)
response.FailWithMessage(err.Error(), c)
return
}
}
} else {
record, err = carService.GetRecordByFeature(req.Feature, req.ID, userid)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
log.Printf("无相关查车记录:%v", err)
response.FailWithMessage("无相关查车记录", c)
return
} else {
log.Printf("查询查车记录错误:%v", err)
response.FailWithMessage(err.Error(), c)
return
}
}
}
response.OkWithData(record, c)
}
// 人车核验 CAR012
// 行驶证信息核验
func (car *Car) PersonCarVerify(c *gin.Context) {
var reqBody request.CarNameTypeNumberReq
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
userid := utils.GetUserID(c)
// 查找是否有 未消费 已付费 的订单
order, err := orderService.QueryConsumedOrder(userid, "rencheheyan")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 为空就没有消费资格
response.OkNeedPay(c)
return
} else {
log.Printf("QueryConsumedOrder err:%s", err.Error())
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
}
var carModel model.CarPersonCarVerify
reqData := map[string]interface{}{
"name": reqBody.Name,
"carType": reqBody.CarType,
"carNumber": reqBody.CarNumber,
}
plainText, err := carService.CarReqYuShan("P_C_B332", &reqData)
if err != nil {
log.Printf("YuShan request at %s error %s", "P_C_B332", err.Error())
notifyService.SendNotification("羽山接口请求错误,请及时处理", "人车核验P_C_B332", userid, order.ID)
response.FailRefund(c)
return
}
_, err = carService.CarPersonCarVerifyDataAnalysis(string(plainText))
if err != nil {
if errors.Is(err, utils.ErrNoRecord) {
notifyService.SendNotification("用户查询数据为空,请及时处理", "人车核验P_C_B332", userid, order.ID)
} else {
notifyService.SendNotification("接口系统响应错误,请及时处理", "人车核验P_C_B332", userid, order.ID)
}
response.FailRefund(c)
return
}
carModel.Resp = string(plainText)
carModel.Name = reqBody.Name
carModel.CarNumber = reqBody.CarNumber
carModel.CarType = reqBody.CarType
carModel.UserID = userid
err = db.DB.Create(&carModel).Error
if err != nil {
log.Printf("人车核验保存错误:%v", err)
notifyService.SendNotification("人车核验保存错误,请及时处理", "人车核验P_C_B332", userid, order.ID)
response.FailRefund(c)
return
}
orderService.OrderConsumed(order)
response.OkWithData(gin.H{
"record_id": carModel.OrderID,
}, c)
}
// CAR061名下车辆
func (car *Car) UnderName(c *gin.Context) {
var reqBody request.CarCardNoReq
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
userid := utils.GetUserID(c)
order, err := orderService.QueryConsumedOrder(userid, "mingxiacheliang")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 为空就没有消费资格
response.OkNeedPay(c)
return
} else {
log.Printf("QueryConsumedOrder err:%s", err.Error())
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
}
var carModel model.CarUnderName
reqData := map[string]interface{}{
"cardNo": reqBody.CardNo,
}
plainText, err := carService.CarReqYuShan("CAR061", &reqData)
if err != nil {
log.Printf("YuShan request at %s error %s", "CAR061", err.Error())
notifyService.SendNotification("羽山接口请求错误,请及时处理", "CAR061名下车辆", userid, order.ID)
response.FailRefund(c)
return
}
_, err = carService.CarInsuranceDataAnalysis(string(plainText))
if err != nil {
if errors.Is(err, utils.ErrNoRecord) {
notifyService.SendNotification("用户查询数据为空,请及时处理", "CAR061名下车辆", userid, order.ID)
} else {
notifyService.SendNotification("接口系统响应错误,请及时处理", "CAR061名下车辆", userid, order.ID)
}
response.FailRefund(c)
return
}
carModel.Resp = string(plainText)
carModel.UserID = userid
carModel.OrderID = order.ID
err = db.DB.Create(&carModel).Error
if err != nil {
log.Printf("名下车辆查询保存错误:%v", err)
notifyService.SendNotification("汽车查询数据保存错误,请及时处理", "CAR061名下车辆", userid, order.ID)
response.FailRefund(c)
return
}
orderService.OrderConsumed(order)
response.OkWithData(gin.H{
"record_id": carModel.OrderID,
}, c)
}
// CAR070车辆上险信息
func (car *Car) InsuranceInfo(c *gin.Context) {
var reqBody request.CarVinReq
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
userid := utils.GetUserID(c)
order, err := orderService.QueryConsumedOrder(userid, "cheliangshangxian")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 为空就没有消费资格
response.OkNeedPay(c)
return
} else {
log.Printf("QueryConsumedOrder err:%s", err.Error())
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
}
var carModel model.CarInsuranceInfo
reqData := map[string]interface{}{
"vin": reqBody.Vin,
}
var wg sync.WaitGroup
var plainTextSx, plainTextWx []byte
var sxErr, WxErr error
wg.Add(2)
go func() {
defer wg.Done()
plainTextSx, sxErr = carService.CarReqYuShan("CAR070", &reqData)
}()
go func() {
defer wg.Done()
plainTextWx, WxErr = carService.CarReqYuShan("CAR095", &reqData)
}()
wg.Wait()
if sxErr != nil {
log.Printf("YuShan request at %s error %s", "CAR070", sxErr.Error())
notifyService.SendNotification("羽山接口请求错误,请及时处理", "CAR070", userid, order.ID)
response.FailRefund(c)
return
}
if WxErr != nil {
log.Printf("YuShan request at %s error %s", "CAR095", WxErr.Error())
notifyService.SendNotification("羽山接口请求错误,请及时处理", "CAR095", userid, order.ID)
response.FailRefund(c)
return
}
_, err = carService.CarInsuranceDataAnalysis(string(plainTextSx))
if err != nil {
if errors.Is(err, utils.ErrNoRecord) {
notifyService.SendNotification("用户查询数据为空,请及时处理", "CAR070", userid, order.ID)
} else {
notifyService.SendNotification("接口系统响应错误,请及时处理", "CAR070", userid, order.ID)
}
response.FailRefund(c)
return
}
_, err = carService.CarInsuranceDataAnalysis(string(plainTextWx))
if err != nil {
if errors.Is(err, utils.ErrNoRecord) {
notifyService.SendNotification("用户查询数据为空,请及时处理", "CAR095", userid, order.ID)
} else {
notifyService.SendNotification("接口系统响应错误,请及时处理", "CAR095", userid, order.ID)
}
response.FailRefund(c)
return
}
carModel.Resp = string(plainTextSx)
carModel.FiveResp = string(plainTextWx)
carModel.UserID = userid
carModel.Vin = reqBody.Vin
carModel.OrderID = order.ID
err = db.DB.Create(&carModel).Error
if err != nil {
log.Printf("车辆上险信息保存错误:%v", err)
notifyService.SendNotification("汽车查询数据保存错误,请及时处理", "车辆上险信息", userid, order.ID)
response.FailRefund(c)
return
}
orderService.OrderConsumed(order)
response.OkWithData(gin.H{
"record_id": carModel.OrderID,
}, c)
}
// CAR079 车辆VIN码查车辆信息
func (car *Car) VinCheckInfo(c *gin.Context) {
var reqBody request.CarVinReq
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
userid := utils.GetUserID(c)
order, err := orderService.QueryConsumedOrder(userid, "vinchache")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 为空就没有消费资格
response.OkNeedPay(c)
return
} else {
log.Printf("QueryConsumedOrder err:%s", err.Error())
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
}
var carModel model.CarVinCheckInfo
reqData := map[string]interface{}{
"vin": reqBody.Vin,
}
plainText, err := carService.CarReqYuShan("CAR079", &reqData)
if err != nil {
log.Printf("YuShan request at %s error %s", "CAR079", err.Error())
notifyService.SendNotification("羽山接口请求错误,请及时处理", "车辆VIN码查车辆信息", userid, order.ID)
response.FailRefund(c)
return
}
_, err = carService.CarInsuranceDataAnalysis(string(plainText))
if err != nil {
if errors.Is(err, utils.ErrNoRecord) {
notifyService.SendNotification("用户查询数据为空,请及时处理", "车辆VIN码查车辆信息", userid, order.ID)
} else {
notifyService.SendNotification("接口系统响应错误,请及时处理", "车辆VIN码查车辆信息", userid, order.ID)
}
response.FailRefund(c)
return
}
carModel.Resp = string(plainText)
carModel.UserID = userid
carModel.Vin = reqBody.Vin
carModel.OrderID = order.ID
err = db.DB.Create(&carModel).Error
if err != nil {
log.Printf("VIN查车辆信息保存错误%v", err)
notifyService.SendNotification("汽车查询数据保存错误,请及时处理", "VIN查车辆信息", userid, order.ID)
response.FailRefund(c)
return
}
orderService.OrderConsumed(order)
response.OkWithData(gin.H{
"record_id": carModel.OrderID,
}, c)
}
// 车辆维保记录CAR058回调
func (car *Car) CarCallback(c *gin.Context) {
// 读取请求体
encryptedData, err := io.ReadAll(c.Request.Body)
if err != nil {
log.Printf("CarCallback ReadAll err:%s", err.Error())
return
}
NotifySN := c.Query("notify_sn")
apiKey := config.ConfigData.Car.ApiKey
respStr, err := utils.Decrypt(string(encryptedData), []byte(apiKey))
if err != nil {
log.Printf("CarCallback DecodeYushan err:%s", err.Error())
}
log.Printf("%sCarCallback%s", NotifySN, respStr)
_, err = carService.CarDataAnalysis(respStr)
if err != nil {
if errors.Is(err, utils.ErrUnmashal) {
log.Printf("CarCallback Unmashal err:%s", err.Error())
return
}
}
maintenance, GetMaintenanceErr := carService.GetMaintenanceBySn(NotifySN)
if GetMaintenanceErr != nil {
log.Printf("GetMaintenanceBySn err:%s", GetMaintenanceErr.Error())
return
}
maintenance.Resp = respStr
if err != nil {
if errors.Is(err, utils.ErrNoRecord) {
notifyService.SendNotification("用户查询数据为空,请及时处理", "维保记录CAR059", maintenance.UserID, maintenance.OrderID)
} else {
notifyService.SendNotification("接口系统响应错误,请及时处理", "维保记录CAR059", maintenance.UserID, maintenance.OrderID)
}
maintenance.Status = model.StatusFailed
} else {
maintenance.Status = model.StatusSuccess
}
err = db.DB.Save(&maintenance).Error
if err != nil {
log.Printf("callback maintenance save err:%s", err.Error())
}
response.Ok(c)
}
// 查询维保历史
func (car *Car) QueryHistoryList(c *gin.Context) {
// 从查询参数中获取分页参数
pageSizeStr := c.DefaultQuery("page_size", "10")
pageNumStr := c.DefaultQuery("page_num", "1")
userId := utils.GetUserID(c)
pageSize, err := strconv.Atoi(pageSizeStr)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
pageNum, err := strconv.Atoi(pageNumStr)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, err := carService.GetMaintenanceList(pageSize, pageNum, userId)
if err != nil {
log.Println("GetMaintenanceList:", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
response.OkWithData(list, c)
}
// CAR066 车辆过户次数
func (car *Car) VehicleTransfer(c *gin.Context) {
var reqBody request.CarVinReq
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
userid := utils.GetUserID(c)
order, err := orderService.QueryConsumedOrder(userid, "cheliangguohu")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 为空就没有消费资格
response.OkNeedPay(c)
return
} else {
log.Printf("QueryConsumedOrder err:%s", err.Error())
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
}
var carModel model.CarVehicleTransfer
reqData := map[string]interface{}{
"vin": reqBody.Vin,
}
plainText, err := carService.CarReqYuShan("CAR066", &reqData)
if err != nil {
log.Printf("YuShan request at %s error %s", "CAR066", err.Error())
notifyService.SendNotification("羽山接口请求错误,请及时处理", "车辆过户次数", userid, order.ID)
response.FailRefund(c)
return
}
_, err = carService.CarInsuranceDataAnalysis(string(plainText))
if err != nil {
if errors.Is(err, utils.ErrNoRecord) {
notifyService.SendNotification("用户查询数据为空,请及时处理", "车辆过户次数", userid, order.ID)
} else {
notifyService.SendNotification("接口系统响应错误,请及时处理", "车辆过户次数", userid, order.ID)
}
response.FailRefund(c)
return
}
carModel.Resp = string(plainText)
carModel.UserID = userid
carModel.Vin = reqBody.Vin
carModel.OrderID = order.ID
err = db.DB.Create(&carModel).Error
if err != nil {
log.Printf("车辆过户次数信息保存错误:%v", err)
notifyService.SendNotification("汽车查询数据保存错误,请及时处理", "车辆过户次数", userid, order.ID)
response.FailRefund(c)
return
}
orderService.OrderConsumed(order)
response.OkWithData(gin.H{
"record_id": carModel.OrderID,
}, c)
}
// CAR100 车辆估值
func (car *Car) VehicleValuation(c *gin.Context) {
var reqBody request.VehicleValuationReq
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
userid := utils.GetUserID(c)
order, err := orderService.QueryConsumedOrder(userid, "cheliangguzhi")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 为空就没有消费资格
response.OkNeedPay(c)
return
} else {
log.Printf("QueryConsumedOrder err:%s", err.Error())
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
}
var carModel model.CarVehicleValuation
reqData := map[string]interface{}{
"vin": reqBody.Vin,
"carNumber": reqBody.CarNumber,
"cardNo": "",
}
plainText, err := carService.CarReqYuShan("CAR100", &reqData)
if err != nil {
log.Printf("YuShan request at %s error %s", "CAR066", err.Error())
notifyService.SendNotification("羽山接口请求错误,请及时处理", "车辆估值", userid, order.ID)
response.FailRefund(c)
return
}
_, err = carService.CarInsuranceDataAnalysis(string(plainText))
if err != nil {
if errors.Is(err, utils.ErrNoRecord) {
notifyService.SendNotification("用户查询数据为空,请及时处理", "车辆估值", userid, order.ID)
} else {
notifyService.SendNotification("接口系统响应错误,请及时处理", "车辆估值", userid, order.ID)
}
response.FailRefund(c)
return
}
carModel.Resp = string(plainText)
carModel.UserID = userid
carModel.Vin = reqBody.Vin
carModel.CarNumber = reqBody.CarNumber
carModel.OrderID = order.ID
err = db.DB.Create(&carModel).Error
if err != nil {
log.Printf("车辆估值信息保存错误:%v", err)
notifyService.SendNotification("汽车查询数据保存错误,请及时处理", "车辆估值", userid, order.ID)
response.FailRefund(c)
return
}
orderService.OrderConsumed(order)
response.OkWithData(gin.H{
"record_id": carModel.OrderID,
}, c)
}

225
api/ent.go Normal file
View File

@@ -0,0 +1,225 @@
package api
import (
"encoding/json"
"errors"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"log"
"net/http"
"qnc-server/model/model"
"qnc-server/model/request"
"qnc-server/model/response"
"qnc-server/service"
"qnc-server/utils"
"sync"
"time"
)
type Ent struct {
}
var entService service.EntSerivce
// 注册路由
func InitEnt(group *gin.RouterGroup) {
var e Ent
entPrivateGroup := group.Group("ent")
entPrivateGroup.Use(JWTAuth())
entPrivateGroup.GET("search", e.search)
entPrivateGroup.GET("query_details", e.queryDetails)
entPrivateGroup.GET("query_feature/:feature", e.queryFeature)
entPrivateGroup.GET("query_list_feature/:feature", e.queryListFeature)
}
func (e *Ent) search(c *gin.Context) {
var reqBody request.EntSearchReq
if err := c.ShouldBindQuery(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
resp, err := entService.RequestTycOpen(reqBody, model.Links["search"])
time.Sleep(2 * time.Second)
if err != nil {
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
if resp == nil {
response.OkWithEmpty(c)
return
}
response.OkWithData(resp, c)
}
func (e *Ent) queryDetails(c *gin.Context) {
var reqBody request.EntReq
if err := c.ShouldBindQuery(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
var wg sync.WaitGroup
var baseinfoResp, holderResp interface{}
var baseinfoErr, holderErr error
wg.Add(2)
go func() {
defer wg.Done()
baseinfoResp, baseinfoErr = entService.RequestTycOpen(reqBody, model.Links["baseinfo"])
}()
go func() {
defer wg.Done()
holderResp, holderErr = entService.RequestTycOpen(reqBody, model.Links["holder"])
}()
wg.Wait()
if baseinfoErr != nil || holderErr != nil {
return
}
response.OkWithData(gin.H{
"baseinfo": baseinfoResp,
"holder": holderResp,
}, c)
}
func (e *Ent) queryListFeature(c *gin.Context) {
feature := c.Param("feature")
if _, exists := model.Links[feature]; !exists {
c.AbortWithStatus(http.StatusNotFound)
return
}
userid := utils.GetUserID(c)
// 查找是否有 未消费 已付费 的订单
order, err := orderService.QueryConsumedOrder(userid, feature)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 为空就没有消费资格
response.OkNeedPay(c)
return
} else {
log.Printf("QueryConsumedOrder err:%s", err.Error())
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
}
entFeatureOrder, err := entService.QueryEntFeatureOrder(order.ID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
} else {
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
}
if len(entFeatureOrder) > 1 {
response.OkNeedPay(c)
return
}
var reqBody request.EntListReq
if err := c.ShouldBindQuery(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
resp, err := entService.RequestTycOpen(reqBody, model.Links[feature])
if err != nil {
log.Printf("天眼查接口错误:%s", err.Error())
notifyService.SendNotification("天眼查接口错误,请及时处理", model.Links[feature], userid, order.ID)
response.FailRefund(c)
return
}
RespJSON, err := json.Marshal(resp)
if err != nil {
log.Printf("failed to marshal feature data: ", err)
}
var entFeatureRecord = model.EntFeature{
UserID: userid,
OrderID: order.ID,
FeatureName: feature,
Resp: string(RespJSON),
}
err = entService.SaveEntFeatureRecords(entFeatureRecord)
if err != nil {
log.Printf("SaveEntFeatureRecords err:%s", err.Error())
}
if resp != nil || len(entFeatureOrder) > 0 {
orderService.OrderConsumed(order)
}
if resp == nil {
response.OkWithEmpty(c)
return
}
response.OkWithData(resp, c)
}
func (e *Ent) queryFeature(c *gin.Context) {
feature := c.Param("feature")
if _, exists := model.Links[feature]; !exists {
c.AbortWithStatus(http.StatusNotFound)
return
}
userid := utils.GetUserID(c)
// 查找是否有 未消费 已付费 的订单
order, err := orderService.QueryConsumedOrder(userid, feature)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 为空就没有消费资格
response.OkNeedPay(c)
return
} else {
log.Printf("QueryConsumedOrder err:%s", err.Error())
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
}
entFeatureOrder, err := entService.QueryEntFeatureOrder(order.ID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
} else {
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
}
if len(entFeatureOrder) > 1 {
response.OkNeedPay(c)
return
}
var reqBody request.EntReq
if err := c.ShouldBindQuery(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
resp, err := entService.RequestTycOpen(reqBody, model.Links[feature])
if err != nil {
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
RespJSON, err := json.Marshal(resp)
if err != nil {
log.Printf("failed to marshal feature data: ", err)
}
var entFeatureRecord = model.EntFeature{
UserID: userid,
OrderID: order.ID,
FeatureName: feature,
Resp: string(RespJSON),
}
err = entService.SaveEntFeatureRecords(entFeatureRecord)
if err != nil {
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
if resp != nil || len(entFeatureOrder) > 0 {
orderService.OrderConsumed(order)
}
if resp == nil {
response.OkWithEmpty(c)
return
}
response.OkWithData(resp, c)
}

160
api/feature.go Normal file
View File

@@ -0,0 +1,160 @@
package api
import (
"encoding/json"
"github.com/gin-gonic/gin"
"log"
"net/http"
request2 "qnc-server/model/request"
"qnc-server/model/response"
"qnc-server/service"
"strings"
)
type Feature struct {
}
var featureService service.Feature
// 注册路由
func InitFeature(group *gin.RouterGroup) {
var f Feature
featruePrivate := group.Group("feature")
featruePrivate.Use(JWTAuth())
featruePrivate.POST("ocr_name", f.OcrName)
featruePrivate.POST("verify_elements", f.VerifyElements)
}
func (f *Feature) OcrName(c *gin.Context) {
var request request2.FeatureOcrReq
// 绑定 JSON 请求体到 OcrRequest 结构体
if err := c.ShouldBindJSON(&request); err != nil {
response.Fail(c)
return
}
// 调用 OCRService
ocrResult, err := featureService.OCRService(request.Base64)
if err != nil {
log.Printf("【OCR校验】请求错误%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
// 尝试解析为错误响应
var ocrError response.OcrErrorResponse
json.Unmarshal([]byte(ocrResult), &ocrError)
if ocrError.ErrorCode != 0 {
// 如果解析成功,且存在 error_code则返回错误信息
response.FailWithMessage("检测签名错误,请重新签名", c)
return
}
// 尝试解析为成功响应
var ocrSuccess response.OcrSuccessResponse
err = json.Unmarshal([]byte(ocrResult), &ocrSuccess)
if err != nil {
// 如果解析失败,则返回解析错误
response.FailWithMessage("系统错误错误,请稍后再试", c)
return
}
// 判断 words_result_num 是否等于 1
if ocrSuccess.WordsResultNum != 1 {
response.FailWithMessage("检测签名错误,请重新签名", c)
return
}
// 判断 OCR 结果的第一个 words 是否与 name 匹配
if len(ocrSuccess.WordsResult) > 0 && strings.Contains(ocrSuccess.WordsResult[0].Words, request.Name) {
response.Ok(c)
} else {
response.FailWithMessage("签名不匹配,请输入签入正确的姓名", c)
return
}
}
func (f *Feature) VerifyElements(c *gin.Context) {
var reqBody request2.FeatureVerifyElementsReq
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
// 二要素核验
twoElementsParams := map[string]any{
"name": reqBody.Name,
"idcard": reqBody.CardNo,
}
twoElementsRespStr, err := requestService.AliYunRequest(http.MethodPost, "https://kzidcardv1.market.alicloudapi.com/api-mall/api/id_card/check", nil, twoElementsParams)
var twoElementsResp response.TwoElementsResp
err = json.Unmarshal([]byte(twoElementsRespStr), &twoElementsResp)
if err != nil {
log.Printf("二要素解析错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
if twoElementsResp.Success == false {
response.FailWithMessage("请输入有效的身份证号码", c)
return
}
if twoElementsResp.Code != 200 {
response.FailWithMessage(twoElementsResp.Msg, c)
return
}
if twoElementsResp.Data.Result == 1 {
response.FailWithMessage("姓名与身份证不一致", c)
return
}
response.Ok(c)
// 三要素
//westName, err := utils.WestDexEncrypt(reqBody.Name, "121a1e41fc1690dd6b90afbcacd80cf4")
//if err != nil {
// log.Printf("西部加密错误:%v", err)
// response.FailWithMessage("系统错误,请稍后再试", c)
// return
//}
//westIDCard, err := utils.WestDexEncrypt(reqBody.CardNo, "121a1e41fc1690dd6b90afbcacd80cf4")
//if err != nil {
// log.Printf("西部加密错误:%v", err)
// response.FailWithMessage("系统错误,请稍后再试", c)
// return
//}
//westPhone, err := utils.WestDexEncrypt(reqBody.Mobile, "121a1e41fc1690dd6b90afbcacd80cf4")
//if err != nil {
// log.Printf("西部加密错误:%v", err)
// response.FailWithMessage("系统错误,请稍后再试", c)
// return
//}
//threeElementsReq := map[string]interface{}{
// "name": westName,
// "idNo": westIDCard,
// "phone": westPhone,
//}
//
//threeElements, err := requestService.WestDexRequest("G15BJ02", threeElementsReq)
//var data response.Wrapper[response.ThreeElementsResponse]
//err = json.Unmarshal([]byte(threeElements), &data)
//if err != nil {
// log.Printf("响应解析失败:%v", err)
// response.FailWithMessage("系统错误,请稍后再试", c)
// return
//}
//
//switch data.Data.Code {
//case "1000":
//case "1003":
// response.FailWithMessage("姓名、证件号均与手机号实名信息不一致", c)
// return
//case "1004":
// response.FailWithMessage("姓名不正确", c)
// return
//case "1005":
// response.FailWithMessage("证件号码不正确", c)
// return
//default:
// log.Printf("三要素检测错误%v", data.Data.Msg)
// response.FailWithMessage("服务器错误请稍后再试", c)
// return
//}
}

18
api/init.go Normal file
View File

@@ -0,0 +1,18 @@
package api
import (
"github.com/gin-gonic/gin"
)
func InitApi(group *gin.RouterGroup) {
InitUser(group)
InitSingleQuery(group)
InitLawsuitQuery(group)
InitEnt(group)
InitPay(group)
InitProduct(group)
InitRender(group)
InitCar(group)
InitVerif(group)
InitFeature(group)
}

299
api/lawsuitQuery.go Normal file
View File

@@ -0,0 +1,299 @@
package api
import (
"encoding/json"
"errors"
"fmt"
"github.com/gin-gonic/gin"
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
"log"
"net/http"
"qnc-server/config"
"qnc-server/db"
"qnc-server/model/model"
"qnc-server/model/request"
"qnc-server/model/response"
"qnc-server/model/types"
"qnc-server/service"
"qnc-server/utils"
)
type LawsuitQuery struct {
}
// 注册路由
func InitLawsuitQuery(group *gin.RouterGroup) {
var l LawsuitQuery
lawsuitQueryPrivateGroup := group.Group("lawsuitQuery")
lawsuitQueryPrivateGroup.Use(JWTAuth())
lawsuitQueryPrivateGroup.POST("query_v2", l.queryListV2)
lawsuitQueryPrivateGroup.GET("get_record", l.GetRecord)
}
var lawsuitQueryService service.LawsuitQueryService
var orderService service.OrderService
func (l *LawsuitQuery) queryListV2(c *gin.Context) {
ctx := c.Request.Context()
var reqBody request.LawsuitQueryV2Req
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
if *reqBody.Type == types.Individual {
if config.ConfigData.Verify.Three {
// 三要素校验
westName, err := utils.WestDexEncrypt(reqBody.Name, "121a1e41fc1690dd6b90afbcacd80cf4")
if err != nil {
log.Printf("西部加密错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
westIDCard, err := utils.WestDexEncrypt(reqBody.CardNo, "121a1e41fc1690dd6b90afbcacd80cf4")
if err != nil {
log.Printf("西部加密错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
westPhone, err := utils.WestDexEncrypt(reqBody.Mobile, "121a1e41fc1690dd6b90afbcacd80cf4")
if err != nil {
log.Printf("西部加密错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
threeElementsReq := map[string]interface{}{
"name": westName,
"idNo": westIDCard,
"phone": westPhone,
}
threeElements, err := requestService.WestDexRequest("G15BJ02", threeElementsReq)
var data response.Wrapper[response.ThreeElementsResponse]
err = json.Unmarshal([]byte(threeElements), &data)
if err != nil {
log.Printf("响应解析失败:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
switch data.Data.Code {
case 1000:
case 1001:
response.FailWithMessage("姓名、证件号、手机号信息不一致", c)
return
case 1003:
response.FailWithMessage("姓名、证件号、手机号信息不一致", c)
return
case 1004:
response.FailWithMessage("姓名不正确", c)
return
case 1005:
response.FailWithMessage("证件号码不正确", c)
return
default:
log.Printf("三要素检测错误%v", data.Data.Msg)
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
} else {
// 二要素核验
twoElementsParams := map[string]any{
"name": reqBody.Name,
"idcard": reqBody.CardNo,
}
twoElementsRespStr, err := requestService.AliYunRequest(http.MethodPost, "https://kzidcardv1.market.alicloudapi.com/api-mall/api/id_card/check", nil, twoElementsParams)
var twoElementsResp response.TwoElementsResp
err = json.Unmarshal([]byte(twoElementsRespStr), &twoElementsResp)
if err != nil {
log.Printf("【二要素】解析错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
if twoElementsResp.Success == false {
fmt.Printf("【二要素】身份证无效")
response.FailWithMessage("请输入有效的身份证号码", c)
return
}
if twoElementsResp.Code != 200 {
response.FailWithMessage(twoElementsResp.Msg, c)
return
}
if twoElementsResp.Data.Result == 1 {
fmt.Printf("【二要素】姓名与身份证不一致")
response.FailWithMessage("姓名与身份证不一致", c)
return
}
}
} else {
// 校验企业社会统一信用代码
isCreditCode := utils.ValidateUnifiedSocialCreditCode(reqBody.CardNo)
if isCreditCode == false {
response.FailWithMessage("请输入企业社会统一信用代码", c)
return
}
}
// 校验验证码
isRight, err := utils.VerifyCode(reqBody.Mobile, reqBody.VerifyCode)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
} else if !isRight {
response.FailWithMessage("验证码错误", c)
return
}
userid := utils.GetUserID(c)
// 查找是否有 未消费 已付费 的订单
order, err := orderService.QueryConsumedOrder(userid, "justiceLawsuit")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 为空就没有消费资格
response.OkNeedPay(c)
return
} else {
log.Printf("QueryConsumedOrder err:%s", err.Error())
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
}
var lawsuitReq map[string]interface{}
var reqCode string
if *reqBody.Type == types.Individual {
encodeName, err := utils.WestDexEncrypt(reqBody.Name, "121a1e41fc1690dd6b90afbcacd80cf4")
if err != nil {
log.Printf("【司法涉诉】加密错误:%v", err)
response.FailWithMessage(err.Error(), c)
return
}
encodeID, err := utils.WestDexEncrypt(reqBody.CardNo, "121a1e41fc1690dd6b90afbcacd80cf4")
if err != nil {
log.Printf("【司法涉诉】加密错误:%v", err)
response.FailWithMessage(err.Error(), c)
return
}
lawsuitReq = map[string]interface{}{
"name": encodeName,
"id_card": encodeID,
}
reqCode = "G22BJ03"
} else {
encodeID, err := utils.WestDexEncrypt(reqBody.CardNo, "121a1e41fc1690dd6b90afbcacd80cf4")
if err != nil {
log.Printf("【司法涉诉】加密错误:%v", err)
response.FailWithMessage(err.Error(), c)
return
}
lawsuitReq = map[string]interface{}{
"ent_name": encodeID,
}
reqCode = "Q03BJ03"
}
// 发起请求并处理响应
plainText, err := requestService.WestDexRequest(reqCode, lawsuitReq)
if err != nil {
handleErrorAndRefund(order, userid, "接口系统错误,将自动退款", c)
return
}
var resp model.LawsuitResponse
err = json.Unmarshal([]byte(plainText), &resp)
if err != nil {
handleErrorAndRefund(order, userid, "司法诉讼记录解析错误", c)
return
}
if resp.Data.ErrCode != "200" {
handleErrorAndRefund(order, userid, fmt.Sprintf("司法诉讼响应错误:%s", resp.Data.ErrMsg), c)
return
}
// 根据类型判断是个人信息还是公司信息
var respData json.RawMessage
if *reqBody.Type == types.Individual {
if resp.Data.Data.PersonInfo == nil || resp.Data.Data.PersonInfo.Code == "-1000" {
handleErrorAndRefund(order, userid, fmt.Sprintf("司法诉讼查询失败:%s", resp.Data.Data.PersonInfo.Msg), c)
return
}
respData = resp.Data.Data.PersonInfo.Data
} else {
if resp.Data.Data.CompanyInfo == nil || resp.Data.Data.CompanyInfo.Code == "-1000" {
handleErrorAndRefund(order, userid, fmt.Sprintf("司法诉讼查询失败:%s", resp.Data.Data.CompanyInfo.Msg), c)
return
}
respData = resp.Data.Data.CompanyInfo.Data
}
var query model.Query
query.OrderID = order.ID
query.Code, _ = utils.EncryptCode(reqBody.Mobile, "776d538f1265a814437d2e12ac10b45b85045bc208a1eb129ef301f4157aa82f")
query.Userid = userid
query.ProductID = order.ProductID
db.DB.Create(&query)
uniqueID := utils.SetCacheData(ctx, respData)
// 更新订单状态并返回成功响应
orderService.OrderConsumed(order)
response.OkWithData(gin.H{
"record_id": uniqueID,
}, c)
}
// 错误处理和退款函数
func handleErrorAndRefund(order model.PayOrder, userid uint, msg string, c *gin.Context) {
log.Printf("【司法诉讼】%s", msg)
notifyService.SendNotification(msg, "司法诉讼", userid, order.ID)
if order.PaymentMethod == model.PaymentMethod_ALIPAY {
err := orderService.AliRefund(ctx, order)
if err != nil {
notifyService.SendNotification("【支付宝】"+msg+",自动退款失败", "司法诉讼", userid, order.ID)
} else {
notifyService.SendNotification("【支付宝】自动退款成功", order.Product.ProductName, order.Userid, order.ID)
}
} else {
err := orderService.WeChatRefund(order)
if err != nil {
notifyService.SendNotification(msg+",自动退款失败", "司法诉讼", userid, order.ID)
}
}
response.FailRefund(c)
}
func (l *LawsuitQuery) GetRecord(c *gin.Context) {
var req request.GetLawsuitRecordReq
if err := c.ShouldBindQuery(&req); err != nil {
response.FailWithMessage("Failed to read request", c)
return
}
//userid := utils.GetUserID(c)
//record, err := lawsuitQueryService.QueryLawsuitByOrder(req.ID, userid)
//if err != nil {
// log.Printf("【司法涉诉】获取司法涉诉记录错误:%v", err)
// notifyService.SendNotification("系统错误,获取司法涉诉记录错误,请及时处理", "司法涉诉", userid, record.OrderID)
// response.FailRefund(c)
// return
//}
//var resp any
//err = json.Unmarshal([]byte(record.Resp), &resp)
//if err != nil {
// log.Printf("【司法涉诉】获取司法涉诉解析错误:%v", err)
// notifyService.SendNotification("系统错误,获取司法涉诉解析错误,请及时处理", "司法涉诉", userid, record.OrderID)
// return
//}
data, err := utils.GetCacheData(ctx, req.ID)
if err != nil {
if errors.Is(err, redis.Nil) {
response.Ok(c)
return
} else {
response.FailWithMessage("系统错误", c)
return
}
}
response.OkWithData(data, c)
}

92
api/middleware.go Normal file
View File

@@ -0,0 +1,92 @@
package api
import (
"errors"
"github.com/gin-gonic/gin"
"github.com/golang-jwt/jwt/v5"
"log"
"qnc-server/config"
"qnc-server/model/response"
"qnc-server/utils"
"strconv"
"time"
)
func JWTAuth() gin.HandlerFunc {
return func(c *gin.Context) {
token := utils.GetToken(c)
if token == "" {
response.NoAuth("未登录", c)
c.Abort()
return
}
j := utils.NewJWT()
// parseToken 解析token包含的信息
claims, err := j.ParseToken(token)
if err != nil {
if errors.Is(err, utils.TokenExpired) {
response.NoAuth("授权已过期", c)
utils.ClearToken(c)
c.Abort()
return
}
response.NoAuth(err.Error(), c)
utils.ClearToken(c)
c.Abort()
return
}
if claims.Disable {
response.Fail(c)
c.Abort()
return
}
c.Set("claims", claims)
if claims.ExpiresAt.Unix()-time.Now().Unix() < claims.BufferTime {
dr, _ := utils.ParseDuration(config.ConfigData.JWT.ExpiresTime)
claims.ExpiresAt = jwt.NewNumericDate(time.Now().Add(dr))
newToken, _ := j.CreateTokenByOldToken(token, *claims)
newClaims, _ := j.ParseToken(newToken)
c.Header("new-token", newToken)
c.Header("new-expires-at", strconv.FormatInt(newClaims.ExpiresAt.Unix(), 10))
utils.SetToken(c, newToken, int(dr.Seconds()))
}
c.Next()
if newToken, exists := c.Get("new-token"); exists {
c.Header("new-token", newToken.(string))
}
if newExpiresAt, exists := c.Get("new-expires-at"); exists {
c.Header("new-expires-at", newExpiresAt.(string))
}
}
}
// NotifyMiddleware 中间件
func NotifyMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
if config.ConfigData.Notify.Switch {
startTime, err := utils.ParseTime(config.ConfigData.Notify.StartTime)
if err != nil {
log.Printf("【通知中间件】起始时间解析错误:%v", err)
c.Next()
return
}
endTime, err := utils.ParseTime(config.ConfigData.Notify.EndTime)
if err != nil {
log.Printf("【通知中间件】结束时间解析错误:%v", err)
c.Next()
return
}
if utils.IsInTimeRange(startTime, endTime) {
response.FailNotify(c)
c.Abort()
return
}
}
c.Next()
}
}

696
api/pay.go Normal file
View File

@@ -0,0 +1,696 @@
package api
import (
"context"
"errors"
"fmt"
"github.com/gin-gonic/gin"
"github.com/redis/go-redis/v9"
"github.com/smartwalle/alipay/v3"
wxcore "github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/core/auth/verifiers"
"github.com/wechatpay-apiv3/wechatpay-go/core/downloader"
"github.com/wechatpay-apiv3/wechatpay-go/core/notify"
"github.com/wechatpay-apiv3/wechatpay-go/services/payments"
"github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic"
wxutils "github.com/wechatpay-apiv3/wechatpay-go/utils"
"gorm.io/gorm"
"log"
"net/http"
"qnc-server/config"
"qnc-server/db"
"qnc-server/global"
"qnc-server/model/model"
"qnc-server/model/request"
"qnc-server/model/response"
"qnc-server/service"
"qnc-server/utils"
"strconv"
"time"
)
type Pay struct {
}
var ctx = context.Background()
var productService service.ProductService
// 注册路由
func InitPay(group *gin.RouterGroup) {
var p Pay
{
payPublicGroup := group.Group("pay")
payPublicGroup.POST("callback/:platform", p.Callback) // 微信支付支付回调
payPublicGroup.GET("refund_details/:id", p.RefundDetailsHTML) // 内部退款订单详情页面
payPublicGroup.POST("refund/:id", p.Refund) // 内部退款按钮
payPublicGroup.POST("refund_callback/:platform", p.RefundCallback) // 微信退款回调
payPublicGroup.POST("ali_callback", p.AlipayCallback) // 阿里退款回调
payPublicGroup.POST("complaint_callback/:platform", p.WxPayComplaintCallback)
}
{
payPrivateGroup := group.Group("pay")
payPrivateGroup.Use(JWTAuth())
payPrivateGroup.POST("prepay", p.Prepay) // 创建微信支付订单
payPrivateGroup.GET("order_list", p.GetOrderList) // 获取订单列表
payPrivateGroup.POST("ali_prepay", p.AliPrepay) // 创建支付宝支付订单
payPrivateGroup.GET("get_query_cache", p.GetQueryCache) // 获取网页的查询缓存
}
}
func (p *Pay) GetOrderList(c *gin.Context) {
// 从查询参数中获取分页参数
pageSizeStr := c.DefaultQuery("page_size", "10")
pageNumStr := c.DefaultQuery("page_num", "1")
userId := utils.GetUserID(c)
pageSize, err := strconv.Atoi(pageSizeStr)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
pageNum, err := strconv.Atoi(pageNumStr)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
}
list, err := orderService.GetList(pageSize, pageNum, userId)
if err != nil {
log.Println("get order list error:", err)
response.FailWithMessage(err.Error(), c)
return
}
response.OkWithData(list, c)
}
func (p *Pay) Prepay(c *gin.Context) {
Claims, err := utils.GetClaims(c)
if err != nil {
log.Println("get claims error:", err)
response.FailWithMessage(err.Error(), c)
return
}
var reqBody request.PrepayReq
err = c.ShouldBindJSON(&reqBody)
if err != nil {
response.FailWithMessage("Failed to read request body, need product", c)
return
}
var (
userid = Claims.Userid
openid string
appid string
mchID string
)
// 获取用户ip
clientIP := c.ClientIP()
switch reqBody.Platform {
case model.PlatformMPWEIXIN:
openid = Claims.AuthIdentifiers.OpenID
appid = config.ConfigData.System.WxAppId
mchID = config.ConfigData.WxPay.MchID
case model.PlatformMPH5:
openid = Claims.AuthIdentifiers.OpenID
appid = config.ConfigData.System.WxH5AppId
mchID = config.ConfigData.WxPay.MchH5ID
case model.PlatformH5:
appid = config.ConfigData.System.WxH5AppId
mchID = config.ConfigData.WxPay.MchH5ID
default:
response.FailWithMessage("ProductName Must be wx or mp-h5 or h5", c)
return
}
product, err := productService.GetProduct(reqBody.ProductName)
if err != nil {
log.Println(err)
response.FailWithMessage(err.Error(), c)
return
}
// 查看用户是否内部号
user, err := userService.GetUserByUserid(Claims.Userid)
if err != nil {
log.Println(err)
response.FailWithMessage(err.Error(), c)
return
}
var amount int64
if user.Inside {
amount = int64(1)
} else {
amount = int64(product.SellPrice)
}
var outTradeNo = fmt.Sprintf("wx_%s", utils.GenerateOrderNumber())
var resp interface{}
if reqBody.Platform == model.PlatformMPWEIXIN {
resp, err = orderService.WechatJSAPIPrepay(appid, mchID, product, outTradeNo, amount, openid, model.PlatformMPWEIXIN, global.GlobalData.PayClient)
if err != nil {
log.Printf("【创建微信支付订单】创建支付失败,系统错误: %v", err)
response.FailWithMessage("创建支付失败,系统错误", c)
return
}
} else if reqBody.Platform == model.PlatformMPH5 {
resp, err = orderService.WechatJSAPIPrepay(appid, mchID, product, outTradeNo, amount, model.PlatformMPH5, openid, global.GlobalData.PayH5Client)
if err != nil {
log.Printf("【创建微信支付订单】创建支付失败,系统错误: %v", err)
response.FailWithMessage("创建支付失败,系统错误", c)
return
}
} else {
resp, err = orderService.WechatH5Prepay(appid, mchID, product, outTradeNo, amount, clientIP, global.GlobalData.PayH5Client)
if err != nil {
log.Printf("【创建微信支付订单】创建支付失败,系统错误: %v", err)
response.FailWithMessage("创建支付失败,系统错误", c)
return
}
}
var payOrder = model.PayOrder{
OutTradeNo: outTradeNo,
Amount: amount,
Userid: userid,
PayStatus: model.PayStatusNotPay,
ProductID: product.ID,
Product: &product,
Platform: reqBody.Platform,
PaymentMethod: model.PaymentMethod_WECHAT,
}
err = db.DB.Create(&payOrder).Error
if err != nil {
log.Printf("create payOrder error%v", err)
}
response.OkWithData(resp, c)
}
func (p *Pay) Callback(c *gin.Context) {
ctx := c //这个参数是context.Background()
cRequest := c.Request //这个值是*http.Request
platform := c.Param("platform")
var mchID string
var mchCertificateSerialNumber string
var mchAPIv3Key string
var privateKeyPath string
switch platform {
case model.PlatformMPWEIXIN:
mchID = config.ConfigData.WxPay.MchID
mchCertificateSerialNumber = config.ConfigData.WxPay.MchCertificateSerialNumber
mchAPIv3Key = config.ConfigData.WxPay.MchAPIv3Key
privateKeyPath = "merchant/mp/apiclient_key.pem"
case model.PlatformH5, model.PlatformMPH5:
mchID = config.ConfigData.WxPay.MchH5ID
mchCertificateSerialNumber = config.ConfigData.WxPay.MchH5CertificateSerialNumber
mchAPIv3Key = config.ConfigData.WxPay.MchH5APIv3Key
privateKeyPath = "merchant/mph5/apiclient_key.pem"
}
mchPrivateKey, err := wxutils.LoadPrivateKeyWithPath(privateKeyPath)
if err != nil {
log.Printf("load merchant private key error")
return
}
// 1. 使用 `RegisterDownloaderWithPrivateKey` 注册下载器
err = downloader.MgrInstance().RegisterDownloaderWithPrivateKey(ctx, mchPrivateKey, mchCertificateSerialNumber, mchID, mchAPIv3Key)
if err != nil {
log.Printf("register downloader with private key error")
return
}
// 2. 获取商户号对应的微信支付平台证书访问器
certificateVisitor := downloader.MgrInstance().GetCertificateVisitor(mchID)
// 3. 使用证书访问器初始化 `notify.Handler`
handler := notify.NewNotifyHandler(mchAPIv3Key, verifiers.NewSHA256WithRSAVerifier(certificateVisitor))
transaction := new(payments.Transaction)
notifyReq, err := handler.ParseNotifyRequest(context.Background(), cRequest, transaction)
// 如果验签未通过,或者解密失败
if err != nil {
log.Printf("parse notify request error")
return
}
log.Printf("微信支付回调响应: %+v", notifyReq)
var status string
if notifyReq.Summary == "支付成功" {
status = model.PayStatusSuccess
} else {
status = model.PayStatusPayError
}
err = db.DB.Model(&model.PayOrder{}).Where("out_trade_no = ?", transaction.OutTradeNo).Updates(model.PayOrder{PayStatus: status, TransactionId: *transaction.TransactionId}).Error
if err != nil {
log.Printf("订单回调处理错误%v", err)
}
c.String(http.StatusOK, "success")
}
func (p *Pay) RefundDetailsHTML(c *gin.Context) {
encryptedOrderID := c.Param("id")
// 解密订单ID
decryptedOrderID, err := utils.IDDecrypt(encryptedOrderID, "njbh287yfbuyh18suygbhd98")
if err != nil {
log.Printf("解密订单ID(%s)失败:%v", decryptedOrderID, err)
c.String(http.StatusNotFound, "")
return
}
// 将字符串转换为 uint64
u64, err := strconv.ParseUint(decryptedOrderID, 10, 64)
if err != nil {
log.Printf("uint64转换失败: %v", err)
c.String(http.StatusNotFound, "")
return
}
// 将 uint64 转换为 uint
orderID := uint(u64)
order, err := orderService.GetOrderByid(orderID)
if err != nil {
log.Printf("订单(%d)获取失败:%v", orderID, err)
c.String(http.StatusNotFound, "")
return
}
type RenderOrder struct {
ID string
OutTradeNo string
TransactionId string
Userid uint
CreatedAt string
Product string
Amount float64
PayStatus string
}
renderOrder := RenderOrder{
ID: encryptedOrderID,
OutTradeNo: order.OutTradeNo,
TransactionId: order.TransactionId,
Userid: order.Userid,
CreatedAt: order.CreatedAt.Format("2006-01-02 15:04:05"),
Product: order.Product.ProductName,
Amount: float64(order.Amount) / 100,
PayStatus: order.PayStatus,
}
c.HTML(http.StatusOK, "refund.html", renderOrder)
}
func (p *Pay) Refund(c *gin.Context) {
encryptedOrderID := c.Param("id")
// 解密订单ID
decryptedOrderID, err := utils.IDDecrypt(encryptedOrderID, "njbh287yfbuyh18suygbhd98")
if err != nil {
log.Printf("解密订单ID(%s)失败:%v", decryptedOrderID, err)
c.String(http.StatusNotFound, "")
return
}
// 将字符串转换为 uint64
u64, err := strconv.ParseUint(decryptedOrderID, 10, 64)
if err != nil {
log.Printf("uint64转换失败: %v", err)
c.String(http.StatusNotFound, "")
return
}
// 将 uint64 转换为 uint
orderID := uint(u64)
order, err := orderService.GetOrderByid(orderID)
if err != nil {
log.Printf("订单(%d)获取失败:%v", orderID, err)
c.String(http.StatusNotFound, "")
return
}
var payClient *wxcore.Client
switch order.Platform {
case model.PlatformMPWEIXIN:
payClient = global.GlobalData.PayClient
case model.PlatformH5, model.PlatformMPH5:
payClient = global.GlobalData.PayH5Client
}
outRefundNo := utils.GenerateOrderRefundNumber()
svc := refunddomestic.RefundsApiService{Client: payClient}
resp, result, err := svc.Create(ctx,
refunddomestic.CreateRequest{
OutTradeNo: wxcore.String(order.OutTradeNo),
OutRefundNo: wxcore.String(outRefundNo), //退款单号
NotifyUrl: wxcore.String(fmt.Sprintf("%s/%s", config.ConfigData.WxPay.RefundNotifyURL, order.Platform)),
Amount: &refunddomestic.AmountReq{
Currency: wxcore.String("CNY"),
Refund: wxcore.Int64(order.Amount),
Total: wxcore.Int64(order.Amount),
},
},
)
if err != nil {
// 处理错误
log.Printf("微信订单申请退款错误:%s", err)
response.Fail(c)
} else {
// 处理返回结果
log.Printf("微信订单申请退款 response status=%d resp=%s", result.Response.StatusCode, resp)
order.PayStatus = model.PayStatusUnderRefund
err = db.DB.Save(order).Error
if err != nil {
log.Printf("微信订单退款状态修改失败 underRefund Error:%v", err)
}
response.Ok(c)
}
}
func (p *Pay) RefundCallback(c *gin.Context) {
ctx := c //这个参数是context.Background()
cRequest := c.Request //这个值是*http.Request
platform := c.Param("platform")
var mchID string
var mchCertificateSerialNumber string
var mchAPIv3Key string
var privateKeyPath string
switch platform {
case model.PlatformMPWEIXIN:
mchID = config.ConfigData.WxPay.MchID
mchCertificateSerialNumber = config.ConfigData.WxPay.MchCertificateSerialNumber
mchAPIv3Key = config.ConfigData.WxPay.MchAPIv3Key
privateKeyPath = "merchant/mp/apiclient_key.pem"
case model.PlatformH5, model.PlatformMPH5:
mchID = config.ConfigData.WxPay.MchH5ID
mchCertificateSerialNumber = config.ConfigData.WxPay.MchH5CertificateSerialNumber
mchAPIv3Key = config.ConfigData.WxPay.MchH5APIv3Key
privateKeyPath = "merchant/mph5/apiclient_key.pem"
}
mchPrivateKey, err := wxutils.LoadPrivateKeyWithPath(privateKeyPath)
if err != nil {
log.Printf("load merchant private key error")
return
}
// 1. 使用 `RegisterDownloaderWithPrivateKey` 注册下载器
err = downloader.MgrInstance().RegisterDownloaderWithPrivateKey(ctx, mchPrivateKey, mchCertificateSerialNumber, mchID, mchAPIv3Key)
if err != nil {
log.Printf("register downloader with private key error")
return
}
// 2. 获取商户号对应的微信支付平台证书访问器
certificateVisitor := downloader.MgrInstance().GetCertificateVisitor(mchID)
// 3. 使用证书访问器初始化 `notify.Handler`
handler := notify.NewNotifyHandler(mchAPIv3Key, verifiers.NewSHA256WithRSAVerifier(certificateVisitor))
transaction := new(payments.Transaction)
notifyReq, err := handler.ParseNotifyRequest(context.Background(), cRequest, transaction)
// 如果验签未通过,或者解密失败
if err != nil {
log.Printf("parse notify request error")
return
}
log.Printf("微信退款回调响应 notifyReq: %+v", notifyReq)
log.Printf("微信退款回调响应 transaction: %+v", transaction)
order := model.PayOrder{}
err = db.DB.Preload("Product").Where("out_trade_no = ?", transaction.OutTradeNo).First(&order).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
log.Printf("【微信退款回调响应】找不到回调相关的订单。OutTradeNo%s", transaction.OutTradeNo)
return
} else {
log.Printf("退款回调处理错误%v", err)
}
}
if notifyReq.EventType == "REFUND.SUCCESS" {
order.PayStatus = model.PayStatusRefund
err = db.DB.Save(&order).Error
if err != nil {
log.Printf("退款回调处理错误%v", err)
} else {
notifyService.SendNotification("退款成功", order.Product.ProductName, order.Userid, order.ID)
}
} else if notifyReq.EventType == "REFUND.ABNORMAL" {
order.PayStatus = model.PayStatusRefundError
err = db.DB.Save(&order).Error
if err != nil {
log.Printf("退款回调处理错误%v", err)
} else {
notifyService.SendNotification("退款异常,请及时处理", order.Product.ProductName, order.Userid, order.ID)
}
}
//log.Printf("微信退款回调响应: %+v", transaction)
c.String(http.StatusOK, "success")
}
// AliPrepay 阿里支付
func (p *Pay) AliPrepay(c *gin.Context) {
var reqBody request.H5PrepayReq
err := c.ShouldBindJSON(&reqBody)
if err != nil {
response.FailWithMessage("Failed to read request body, need product", c)
return
}
Claims, err := utils.GetClaims(c)
if err != nil {
log.Println("get claims error:", err)
response.FailWithMessage(err.Error(), c)
return
}
// 获取商品信息
product, err := productService.GetProduct(reqBody.ProductName)
if err != nil {
log.Println(err)
response.FailWithMessage(err.Error(), c)
return
}
// 生成单号
outTradeNo := fmt.Sprintf("ali_%s", utils.GenerateOrderNumber())
// 查看用户是否内部号
user, err := userService.GetUserByUserid(Claims.Userid)
if err != nil {
log.Println(err)
response.FailWithMessage(err.Error(), c)
return
}
var amount string
var orderAmount int64
log.Printf("is inside%+v,is %t", user, user.Inside)
if user.Inside {
amount = "0.01"
orderAmount = 1
} else {
amount = utils.ConvertCentsToYuan(product.SellPrice)
orderAmount = int64(product.SellPrice)
}
pay := alipay.TradeWapPay{}
pay.NotifyURL = config.ConfigData.AliPay.NotifyURL // 替换为您的回调地址
pay.ReturnURL = fmt.Sprintf("%s%s?callback=true", config.ConfigData.Server.Domain, reqBody.Href) // 支付成功后跳转的地址
pay.Subject = product.ProductName // 订单标题
pay.OutTradeNo = outTradeNo // 生成的唯一订单号
pay.TotalAmount = amount // 支付金额,单位元
pay.ProductCode = "QUICK_WAP_PAY" // WAP支付的产品代码
payURL, err := global.GlobalData.AliPayClient.TradeWapPay(pay)
if err != nil {
log.Printf("【阿里支付创建】创建订单错误:%v", err)
response.Fail(c)
return
}
ctx := context.Background()
expiration := 5 * time.Minute // 5分钟超时
err = db.RedisClient.Set(ctx, fmt.Sprintf("alipay_%s", outTradeNo), reqBody.QueryData, expiration).Err()
if err != nil {
log.Printf("【阿里支付创建】前端请求数据缓存错误:%v", err)
response.Fail(c)
return
}
var payOrder = model.PayOrder{
OutTradeNo: outTradeNo,
Amount: orderAmount,
Userid: Claims.Userid,
PayStatus: model.PayStatusNotPay,
ProductID: product.ID,
Product: &product,
Platform: reqBody.Platform,
PaymentMethod: model.PaymentMethod_ALIPAY,
}
err = db.DB.Create(&payOrder).Error
if err != nil {
log.Printf("【阿里支付创建】订单保存错误:%v", err)
response.Fail(c)
return
}
response.OkWithData(gin.H{
"PayUrl": payURL.String(),
}, c)
}
// AlipayCallback 阿里支付回调
func (p *Pay) AlipayCallback(c *gin.Context) {
// 解析表单
err := c.Request.ParseForm()
if err != nil {
log.Printf("ali pay callback解析请求表单失败%v", err)
return
}
// DecodeNotification 内部已调用 VerifySign 方法验证签名
noti, err := global.GlobalData.AliPayClient.DecodeNotification(c.Request.Form)
if err != nil {
log.Printf("【阿里支付回调】通知解码失败失败:%v", err)
return
}
log.Printf("【阿里支付回调】接收到支付宝回调,商户订单号:%s", noti.OutTradeNo)
log.Printf("【阿里支付回调】接收到支付宝回调,支付宝订单号:%s", noti.OutTradeNo)
log.Printf("【阿里支付回调】交易状态:%s", noti.TradeStatus)
if noti.TradeStatus == alipay.TradeStatusSuccess {
log.Printf("【阿里支付回调】交易成功")
err = db.DB.Model(&model.PayOrder{}).Where("out_trade_no = ?", noti.OutTradeNo).Updates(model.PayOrder{PayStatus: model.PayStatusSuccess, AliTradeNo: noti.TradeNo}).Error
if err != nil {
log.Printf("【阿里支付回调】订单回调处理错误%v", err)
}
// 确认收到通知消息,不然支付宝后续会继续推送相同的消息
alipay.ACKNotification(c.Writer)
}
}
func (p *Pay) GetQueryCache(c *gin.Context) {
var reqBody request.QueryDataReq
err := c.ShouldBindQuery(&reqBody)
if err != nil {
response.FailWithMessage("Failed to read request body, need out_trade_no", c)
return
}
ctx := context.Background()
key := fmt.Sprintf("alipay_%s", reqBody.OutTradeNo)
result, err := db.RedisClient.Get(ctx, key).Result()
if errors.Is(err, redis.Nil) {
response.FailWithMessage("超时,请重新手动输入数据查询", c)
return
} else if err != nil {
log.Printf("【阿里支付】获取缓存表单数据错误%v", err)
response.FailWithMessage("请重新手动输入数据", c)
return
}
response.OkWithData(result, c)
}
func (p *Pay) WxPayComplaintCallback(c *gin.Context) {
ctx := context.Background()
cRequest := c.Request
platform := c.Param("platform")
var mchID string
var mchCertificateSerialNumber string
var mchAPIv3Key string
var privateKeyPath string
var platformString string
switch platform {
case model.PlatformMPWEIXIN:
platformString = "微信小程序"
mchID = config.ConfigData.WxPay.MchID
mchCertificateSerialNumber = config.ConfigData.WxPay.MchCertificateSerialNumber
mchAPIv3Key = config.ConfigData.WxPay.MchAPIv3Key
privateKeyPath = "merchant/mp/apiclient_key.pem"
case model.PlatformH5, model.PlatformMPH5:
platformString = "公众号"
mchID = config.ConfigData.WxPay.MchH5ID
mchCertificateSerialNumber = config.ConfigData.WxPay.MchH5CertificateSerialNumber
mchAPIv3Key = config.ConfigData.WxPay.MchH5APIv3Key
privateKeyPath = "merchant/mph5/apiclient_key.pem"
}
notifyService.SendComplaintNotification(fmt.Sprintf("%s投诉", platformString), "", platformString, "")
mchPrivateKey, err := wxutils.LoadPrivateKeyWithPath(privateKeyPath)
if err != nil {
log.Printf("【%s投诉通知回调】加载商户私钥失败: %v", platformString, err)
return
}
// 注册下载器
err = downloader.MgrInstance().RegisterDownloaderWithPrivateKey(ctx, mchPrivateKey, mchCertificateSerialNumber, mchID, mchAPIv3Key)
if err != nil {
log.Printf("【%s投诉通知回调】注册下载器失败: %v", platformString, err)
return
}
// 获取微信支付平台证书访问器
certificateVisitor := downloader.MgrInstance().GetCertificateVisitor(mchID)
// 初始化 notify.Handler
handler := notify.NewNotifyHandler(mchAPIv3Key, verifiers.NewSHA256WithRSAVerifier(certificateVisitor))
// 解析并验证通知请求
content := make(map[string]interface{})
notifyReq, err := handler.ParseNotifyRequest(ctx, cRequest, &content)
if err != nil {
log.Printf("【%s投诉通知回调】解析通知请求失败: %v", platformString, err)
return
}
// 打印通知摘要和解析后的内容
log.Printf("【%s投诉通知回调】通知信息: %s", platformString, notifyReq.Summary)
log.Printf("【%s投诉通知回调】通知数据: %s", platformString, content)
// 根据解析后的数据处理通知
actionType, ok := content["action_type"].(string)
if !ok {
log.Printf("【%s投诉通知回调】通知内容中缺少action_type字段", platformString)
return
}
complaintID, ok := content["complaint_id"].(string)
if !ok {
log.Printf("【%s投诉通知回调】通知内容中缺少complaint_id字段", platformString)
return
}
switch actionType {
case "CREATE_COMPLAINT":
message := "用户提交了新的投诉"
notifyService.SendComplaintNotification(message, "新投诉", platformString, complaintID)
case "CONTINUE_COMPLAINT":
message := "用户继续投诉"
notifyService.SendComplaintNotification(message, "继续投诉", platformString, complaintID)
case "USER_RESPONSE":
message := "用户在投诉单中添加了新留言"
notifyService.SendComplaintNotification(message, "用户留言", platformString, complaintID)
case "RESPONSE_BY_PLATFORM":
message := "平台在投诉单中添加了新留言"
notifyService.SendComplaintNotification(message, "平台留言", platformString, complaintID)
case "SELLER_REFUND":
message := "商户发起了全额退款"
notifyService.SendComplaintNotification(message, "全额退款", platformString, complaintID)
case "MERCHANT_RESPONSE":
message := "商户对投诉进行了回复"
notifyService.SendComplaintNotification(message, "商户回复", platformString, complaintID)
case "MERCHANT_CONFIRM_COMPLETE":
message := "商户标记投诉处理完成"
notifyService.SendComplaintNotification(message, "投诉处理完成", platformString, complaintID)
case "MERCHANT_APPROVE_REFUND":
message := "商户同意了退款"
notifyService.SendComplaintNotification(message, "同意退款", platformString, complaintID)
case "MERCHANT_REJECT_REFUND":
message := "商户拒绝了退款"
notifyService.SendComplaintNotification(message, "拒绝退款", platformString, complaintID)
case "REFUND_SUCCESS":
message := "退款已成功到账"
notifyService.SendComplaintNotification(message, "退款到账", platformString, complaintID)
default:
message := "未知投诉类型"
notifyService.SendComplaintNotification(message, "未知投诉类型", platformString, complaintID)
log.Printf("【%s投诉通知回调】收到未知类型的投诉通知: %s", platformString, actionType)
}
// 返回成功响应
c.String(http.StatusOK, "success")
}

54
api/product.go Normal file
View File

@@ -0,0 +1,54 @@
package api
import (
"github.com/gin-gonic/gin"
"qnc-server/model/request"
"qnc-server/model/response"
"qnc-server/service"
)
type Product struct {
}
var product service.ProductService
// 注册路由
func InitProduct(group *gin.RouterGroup) {
var p Product
productPrivateGroup := group.Group("product")
productPublicGroup := group.Group("product")
productPrivateGroup.Use(JWTAuth())
productPrivateGroup.GET("/product_notes", p.GetProductNotes)
productPublicGroup.GET("/product_info", p.GetProductInfo)
}
func (p *Product) GetProductNotes(c *gin.Context) {
var req request.GetProductNotesReq
if err := c.ShouldBindQuery(&req); err != nil {
response.FailWithMessage("Failed to read request query", c)
return
}
product, err := product.GetProduct(req.ProductName)
if err != nil {
response.FailWithMessage("get product err", c)
return
}
response.OkWithData(product.Notes, c)
}
func (p *Product) GetProductInfo(c *gin.Context) {
var req request.GetProductNotesReq
if err := c.ShouldBindQuery(&req); err != nil {
response.FailWithMessage("Failed to read request query", c)
return
}
product, err := product.GetProduct(req.ProductName)
if err != nil {
response.FailWithMessage("get product err", c)
return
}
response.OkWithData(gin.H{
"id": product.ProductEn,
"name": product.ProductName,
"price": product.SellPrice,
}, c)
}

69
api/render.go Normal file
View File

@@ -0,0 +1,69 @@
package api
import (
"github.com/gin-gonic/gin"
"qnc-server/config"
"qnc-server/model/response"
"qnc-server/service"
)
type Render struct {
}
var renderService service.RenderService
// 注册路由
func InitRender(group *gin.RouterGroup) {
var r Render
RenderPublicGroup := group.Group("render")
RenderPublicGroup.GET("get_index", r.GetIndex)
RenderPublicGroup.GET("get_car_index", r.GetCarIndex)
RenderPublicGroup.GET("get_verify_index", r.GetVerifyIndex)
RenderPublicGroup.GET("get_notification", r.GetNotification)
RenderPublicGroup.GET("get_shadow", r.GetShade)
RenderPublicGroup.GET("setting", r.GetSetting)
}
func (r *Render) GetSetting(c *gin.Context) {
sign := config.ConfigData.Setting.Sign
response.OkWithData(gin.H{"sign": sign}, c)
}
func (r *Render) GetIndex(c *gin.Context) {
renderlist, err := renderService.GetIndex()
if err != nil {
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
response.OkWithData(renderlist, c)
}
func (r *Render) GetCarIndex(c *gin.Context) {
renderlist, err := renderService.GetCarIndex()
if err != nil {
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
response.OkWithData(renderlist, c)
}
func (r *Render) GetVerifyIndex(c *gin.Context) {
renderlist, err := renderService.GetVerifyIndex()
if err != nil {
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
response.OkWithData(renderlist, c)
}
func (r *Render) GetNotification(c *gin.Context) {
notification, err := renderService.GetNotificationList()
if err != nil {
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
response.OkWithData(notification, c)
}
func (r *Render) GetShade(c *gin.Context) {
shadow, err := renderService.GetShadow()
if err != nil {
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
response.OkWithData(shadow, c)
}

293
api/singleQuery.go Normal file
View File

@@ -0,0 +1,293 @@
package api
import (
"encoding/json"
"errors"
"github.com/gin-gonic/gin"
"net/http"
"qnc-server/config"
"github.com/redis/go-redis/v9"
"gorm.io/gorm"
"log"
"qnc-server/db"
"qnc-server/model/model"
"qnc-server/model/request"
"qnc-server/model/response"
"qnc-server/service"
"qnc-server/utils"
)
type SingleQuery struct {
}
var singleQueryService service.SingleQueryService
var notifyService service.Notify
var requestService service.RequestService
// 注册路由
func InitSingleQuery(group *gin.RouterGroup) {
var s SingleQuery
singQueryPrivate := group.Group("single")
singQueryPrivate.Use(JWTAuth())
singQueryPrivate.POST("bc_evaluate_marriage", NotifyMiddleware(), s.EvaluateMarriageQuery)
singQueryPrivate.GET("get_merriage_record", s.GetMerriageRecord)
}
func (s *SingleQuery) EvaluateMarriageQuery(c *gin.Context) {
ctx := c.Request.Context()
var reqBody request.EvaluateMarriageReq
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
if config.ConfigData.Setting.Sign {
if reqBody.SignImage == "" {
response.FailWithMessage("Failed to read request body", c)
return
}
}
if len(reqBody.Mobile) != 11 {
response.FailWithMessage("请输入正确的手机号码", c)
return
}
// 校验验证码
isRight, err := utils.VerifyCode(reqBody.Mobile, reqBody.VerifyCode)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
} else if !isRight {
response.FailWithMessage("验证码错误", c)
return
}
if config.ConfigData.Verify.Three {
// 三要素
westName, err := utils.WestDexEncrypt(reqBody.Name, "121a1e41fc1690dd6b90afbcacd80cf4")
if err != nil {
log.Printf("西部加密错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
westIDCard, err := utils.WestDexEncrypt(reqBody.CardNo, "121a1e41fc1690dd6b90afbcacd80cf4")
if err != nil {
log.Printf("西部加密错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
westPhone, err := utils.WestDexEncrypt(reqBody.Mobile, "121a1e41fc1690dd6b90afbcacd80cf4")
if err != nil {
log.Printf("西部加密错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
threeElementsReq := map[string]interface{}{
"name": westName,
"idNo": westIDCard,
"phone": westPhone,
}
threeElements, err := requestService.WestDexRequest("G15BJ02", threeElementsReq)
var data response.Wrapper[response.ThreeElementsResponse]
err = json.Unmarshal([]byte(threeElements), &data)
if err != nil {
log.Printf("响应解析失败:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
switch data.Data.Code {
case 1000:
case 1001:
response.FailWithMessage("姓名、证件号、手机号信息不一致", c)
return
case 1003:
response.FailWithMessage("姓名、证件号、手机号信息不一致", c)
return
case 1004:
response.FailWithMessage("姓名不正确", c)
return
case 1005:
response.FailWithMessage("证件号码不正确", c)
return
default:
log.Printf("三要素检测错误%v", data.Data.Msg)
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
} else {
// 二要素核验
twoElementsParams := map[string]any{
"name": reqBody.Name,
"idcard": reqBody.CardNo,
}
twoElementsRespStr, err := requestService.AliYunRequest(http.MethodPost, "https://kzidcardv1.market.alicloudapi.com/api-mall/api/id_card/check", nil, twoElementsParams)
var twoElementsResp response.TwoElementsResp
err = json.Unmarshal([]byte(twoElementsRespStr), &twoElementsResp)
if err != nil {
log.Printf("二要素解析错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
if twoElementsResp.Success == false {
response.FailWithMessage("请输入有效的身份证号码", c)
return
}
if twoElementsResp.Code != 200 {
response.FailWithMessage(twoElementsResp.Msg, c)
return
}
if twoElementsResp.Data.Result == 1 {
response.FailWithMessage("姓名与身份证不一致", c)
return
}
}
userid := utils.GetUserID(c)
//查找是否有 未消费并且已付费 的订单
order, err := orderService.QueryConsumedOrder(userid, "evaluateMarriage")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 为空就没有消费资格
response.OkNeedPay(c)
return
} else {
log.Printf("QueryConsumedOrder err:%s", err.Error())
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
}
encodeName, err := utils.WestDexEncrypt(reqBody.Name, "121a1e41fc1690dd6b90afbcacd80cf4")
if err != nil {
log.Printf("【婚姻查询】加密错误:%v", err)
response.FailWithMessage(err.Error(), c)
return
}
encodeID, err := utils.WestDexEncrypt(reqBody.CardNo, "121a1e41fc1690dd6b90afbcacd80cf4")
if err != nil {
log.Printf("【婚姻查询】加密错误:%v", err)
response.FailWithMessage(err.Error(), c)
return
}
evaluateMarriageReq := map[string]interface{}{
"name": encodeName,
"id": encodeID,
}
plainText, err := requestService.WestDexRequest("G09GX01", evaluateMarriageReq)
if err != nil {
notifyService.SendNotification("接口系统错误,将自动退款", "婚姻查询", userid, order.ID)
if order.PaymentMethod == model.PaymentMethod_ALIPAY {
err := orderService.AliRefund(ctx, order)
if err != nil {
notifyService.SendNotification("【支付宝】接口系统错误,自动退款失败", "婚姻查询", userid, order.ID)
} else {
notifyService.SendNotification("【支付宝】自动退款成功", order.Product.ProductName, order.Userid, order.ID)
}
} else {
err := orderService.WeChatRefund(order)
if err != nil {
notifyService.SendNotification("接口系统响应错误,自动退款失败", "婚姻查询", userid, order.ID)
}
}
response.FailRefund(c)
return
}
result, err := singleQueryService.MarryAnalysis(plainText)
if err != nil {
if errors.Is(err, utils.ErrNoRecord) {
notifyService.SendNotification("用户查询数据为空,将自动退款", "婚姻查询", userid, order.ID)
if order.PaymentMethod == model.PaymentMethod_ALIPAY {
err := orderService.AliRefund(ctx, order)
if err != nil {
notifyService.SendNotification("【支付宝】用户查询数据为空,自动退款失败", "婚姻查询", userid, order.ID)
} else {
notifyService.SendNotification("【支付宝】自动退款成功", order.Product.ProductName, order.Userid, order.ID)
}
} else {
err := orderService.WeChatRefund(order)
if err != nil {
notifyService.SendNotification("用户查询数据为空,自动退款失败", "婚姻查询", userid, order.ID)
}
}
} else {
notifyService.SendNotification("接口系统错误,将自动退款", "婚姻查询", userid, order.ID)
if order.PaymentMethod == model.PaymentMethod_ALIPAY {
err := orderService.AliRefund(ctx, order)
if err != nil {
notifyService.SendNotification("【支付宝】接口系统错误,自动退款失败", "婚姻查询", userid, order.ID)
} else {
notifyService.SendNotification("【支付宝】自动退款成功", order.Product.ProductName, order.Userid, order.ID)
}
} else {
err := orderService.WeChatRefund(order)
if err != nil {
notifyService.SendNotification("接口系统响应错误,自动退款失败", "婚姻查询", userid, order.ID)
}
}
}
log.Printf("【婚姻查询】%s", err.Error())
response.FailRefund(c)
return
}
var code string
if result.Code == 201 {
code = "1"
} else if result.Code == 202 {
code = "2"
} else if result.Code == 203 {
code = "0"
} else {
response.FailRefund(c)
return
}
orderService.OrderConsumed(order)
var query model.Query
query.OrderID = order.ID
query.Code, _ = utils.EncryptCode(reqBody.Mobile, "776d538f1265a814437d2e12ac10b45b85045bc208a1eb129ef301f4157aa82f")
query.Userid = userid
query.ProductID = order.ProductID
db.DB.Create(&query)
fileName, err := featureService.GenerateSignPDF(reqBody.Name, query.ID, reqBody.SignImage)
query.SignFile = fileName
db.DB.Save(&query)
var respData = gin.H{
"card_no": utils.EncryptIDCard(reqBody.CardNo),
"name": utils.EncryptName(reqBody.Name),
"result": []map[string]string{
{
"maritalStatus": code,
},
},
}
uniqueID := utils.SetCacheData(ctx, respData)
response.OkWithData(gin.H{
"record_id": uniqueID,
}, c)
}
func (s *SingleQuery) GetMerriageRecord(c *gin.Context) {
ctx := c.Request.Context()
var req request.GetMerriageRecordReq
if err := c.ShouldBindQuery(&req); err != nil {
response.FailWithMessage("Failed to read request", c)
return
}
data, err := utils.GetCacheData(ctx, req.ID)
if err != nil {
if errors.Is(err, redis.Nil) {
response.Ok(c)
return
} else {
response.FailWithMessage("系统错误", c)
return
}
}
response.OkWithData(data, c)
}

367
api/user.go Normal file
View File

@@ -0,0 +1,367 @@
package api
import (
"fmt"
"github.com/gin-gonic/gin"
"log"
"math/rand"
"qnc-server/config"
common "qnc-server/model/common"
model "qnc-server/model/model"
"qnc-server/model/request"
"qnc-server/model/response"
"qnc-server/service"
"qnc-server/utils"
"strconv"
"time"
)
type User struct {
}
var userService service.UserService
var verifyService service.VerifyCode
// 注册路由
func InitUser(group *gin.RouterGroup) {
var u User
userPublicGroup := group.Group("user")
userPrivateGroup := group.Group("user")
{
userPublicGroup.GET("test", u.Test)
userPublicGroup.POST("log", u.Log)
userPublicGroup.POST("login", u.Login)
userPublicGroup.POST("h5_login", u.H5Login)
userPublicGroup.POST("phone_login", u.PhoneLogin)
userPublicGroup.POST("verify", u.GetVerify)
userPublicGroup.GET("get_config", u.GetSDKConfig)
}
{
userPrivateGroup.Use(JWTAuth())
}
}
func (u *User) Test(c *gin.Context) {
authIdentifier := model.AuthIdentifier{
OpenID: "oR_hJ6kv2bmH7YWPzuZdwQvN4B5g",
UnionID: "ogzHA6W4T7vHGl-99nlHkSJDczBc",
}
authentications, err := userService.MatchingAuthentications(authIdentifier)
if err != nil {
return
}
response.OkWithData(authentications, c)
}
func (u *User) Log(c *gin.Context) {
postForm := c.Request.PostForm
log.Printf("前端请求打印:%s", postForm)
response.Ok(c)
}
func (u *User) Login(c *gin.Context) {
var reqBody request.UserLoginReq
// 尝试将请求的 JSON body 绑定到 reqBody 结构体
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
// 检查请求体中是否包含 code 字段
if reqBody.Code == "" {
response.FailWithMessage("code is required", c)
return
}
// Code获取用户信息
wxResponse, err := userService.RequestWx(reqBody.Code)
if err != nil {
response.FailWithMessage(fmt.Errorf("request weixin err%w", err).Error(), c)
return
}
authIdentifier := model.AuthIdentifier{
OpenID: wxResponse.OpenId,
UnionID: wxResponse.Unionid,
}
// 是否有匹配的校验信息
authentications, err := userService.MatchingAuthentications(authIdentifier)
if err != nil {
log.Printf("MatchingAuthentications Error%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
var authentication model.Authentication
if len(authentications) == 0 {
authentication, err = userService.Register(model.User{}, model.AUTHTYPE_WECHAT_MP, authIdentifier)
if err != nil {
log.Printf("注册用户错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
//成功创建用户
} else {
// 校验信息是否有该平台
hasWechatMP := false
for _, auth := range authentications {
if auth.AuthType == model.AUTHTYPE_WECHAT_MP {
hasWechatMP = true
authentication = auth
}
}
// 没有则创建该平台校验并关联用户
if !hasWechatMP {
userid := authentication.UserID
authentication, err = userService.CreateAuthentications(userid, authIdentifier, model.AUTHTYPE_WECHAT_MP)
if err != nil {
log.Printf("创建平台校验错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
//成功校验该平台成功
}
}
user, err := userService.GetUser(authentication.UserID)
if err != nil {
log.Printf("authentication获取关联用户失败%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
u.TokenNext(c, user, authIdentifier, model.AUTHTYPE_WECHAT_MP)
}
// 公众号h5登录
func (u *User) H5Login(c *gin.Context) {
var reqBody request.H5UserLoginReq
// 尝试将请求的 JSON body 绑定到 reqBody 结构体
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
if reqBody.UserinfoAuth == nil {
response.FailWithDetailed(reqBody, "根本没有Auth", c)
return
}
// 检查请求体中是否包含 code 字段
if reqBody.Code == "" {
response.FailWithMessage("code is required", c)
return
}
// 获取到h5的openid token
wxH5Response, err := userService.RequestWxH5(reqBody.Code)
if err != nil {
log.Printf("request weixin err%v", err)
response.FailWithMessage(fmt.Sprintf("登录失败,请稍后再试:%v", err), c)
return
}
authIdentifier := model.AuthIdentifier{
OpenID: wxH5Response.Openid,
}
// 是否有匹配的校验信息
authentications, err := userService.MatchingAuthentications(authIdentifier)
if err != nil {
log.Printf("GetUserByH5 err%v", err)
response.FailWithMessage(fmt.Sprintf("系统错误登录失败,请稍后再试:%v", err), c)
return
}
var authentication model.Authentication
if len(authentications) == 0 {
// 是否手动授权登录
if *reqBody.UserinfoAuth {
userinfo, err := userService.GetSnsUserInfo(wxH5Response.AccessToken, wxH5Response.Openid)
if err != nil {
log.Printf("request weixin getuserinfo err%v", err)
response.FailWithMessage(fmt.Sprintf("登录失败,请稍后再试:%v", err), c)
return
}
log.Printf("UserInfo%v", userinfo)
authIdentifier.UnionID = userinfo.UnionID
log.Printf("authIdentifier%v", authIdentifier)
//查找匹配UnionID
authentications, err = userService.MatchingAuthentications(authIdentifier)
log.Printf("authentications%v", authentications)
if err != nil {
log.Printf("get user err%v", err)
response.FailWithMessage(fmt.Sprintf("登录失败,请稍后再试:%v", err), c)
return
}
if len(authentications) == 0 {
//没有则创建用户
authentication, err = userService.Register(model.User{}, model.AUTHTYPE_WECHAT_H5, authIdentifier)
if err != nil {
log.Printf("注册用户错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
} else {
// 有则创建平台校验
authentication, err = userService.CreateAuthentications(authentications[0].UserID, authIdentifier, model.AUTHTYPE_WECHAT_H5)
if err != nil {
log.Printf("创建平台校验错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
}
} else {
// 静默登录并且库中无openid让前端重新进行手动授权
response.ManualAuth(c)
return
}
} else {
for _, auth := range authentications {
if auth.AuthType == model.AUTHTYPE_WECHAT_H5 {
authentication = auth
}
}
}
user, err := userService.GetUser(authentication.UserID)
if err != nil {
log.Printf("authentication获取关联用户失败%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
//有则直接登录
u.TokenNext(c, user, authIdentifier, model.AUTHTYPE_WECHAT_H5)
}
// 手机号登录
func (u *User) PhoneLogin(c *gin.Context) {
var reqBody request.PhoneLoginReq
// 尝试将请求的 JSON body 绑定到 reqBody 结构体
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage(err.Error(), c)
return
}
// 校验验证码
isRight, err := utils.VerifyCode(reqBody.PhoneNumber, reqBody.VerifyCode)
if err != nil {
response.FailWithMessage(err.Error(), c)
return
} else if !isRight {
response.FailWithMessage("验证码错误", c)
return
}
authIdentifier := model.AuthIdentifier{
Phone: reqBody.PhoneNumber,
}
authentications, err := userService.MatchingAuthentications(authIdentifier)
var authentication model.Authentication
if len(authentications) == 0 {
authentication, err = userService.Register(model.User{Phone: reqBody.PhoneNumber}, model.AUTHTYPE_PHONE, authIdentifier)
if err != nil {
log.Printf("注册用户错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
//成功创建用户
} else {
authentication = authentications[0]
}
user, err := userService.GetUser(authentication.UserID)
if err != nil {
log.Printf("authentication获取关联用户失败%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
u.TokenNext(c, user, authIdentifier, model.AUTHTYPE_PHONE)
}
// TokenNext 登录以后签发jwt
func (u *User) TokenNext(c *gin.Context, user *model.User, authIdentifiers model.AuthIdentifier, platform model.AuthType) {
j := &utils.JWT{SigningKey: []byte(config.ConfigData.JWT.SigningKey)} // 唯一签名
claims := j.CreateClaims(common.BaseClaims{
Userid: user.Userid,
Disable: user.Disable != nil && *user.Disable,
AuthIdentifiers: authIdentifiers,
Platform: platform,
})
token, err := j.CreateToken(claims)
if err != nil {
response.FailWithMessage("获取token失败", c)
return
}
utils.SetToken(c, token, int(claims.RegisteredClaims.ExpiresAt.Unix()-time.Now().Unix()))
response.OkWithDetailed(response.LoginResponse{
User: *user,
//AuthIdentifiers: authIdentifiers,
Token: token,
ExpiresAt: claims.RegisteredClaims.ExpiresAt.Unix() * 1000,
}, "登录成功", c)
return
}
// 获取验证码
func (u *User) GetVerify(c *gin.Context) {
var req request.VerifyCodeReq
// 绑定JSON数据到结构体
if err := c.ShouldBindJSON(&req); err != nil {
response.FailWithMessage("PhoneNumber is required", c)
return
}
// 验证手机号码格式
if !utils.IsValidPhoneNumber(req.PhoneNumber) {
response.FailWithMessage("Invalid phone number format", c)
return
}
existsCode, err := utils.CanRequestCode(req.PhoneNumber)
if err != nil {
return
}
if existsCode {
response.FailWithMessage("请求过于频繁,请稍后再试", c)
return
}
var code = utils.GenerateVerificationCode()
defaultTemplateCode := config.ConfigData.VerifyCode.TemplateCode
if req.Template == "marriage" {
defaultTemplateCode = config.ConfigData.VerifyCode.MarriageTemplateCode
}
err = verifyService.Send(code, req.PhoneNumber, defaultTemplateCode)
if err != nil {
log.Printf("【获取验证码】请求验证码发送失败:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
err = utils.StoreVerificationCode(req.PhoneNumber, code)
if err != nil {
log.Printf("【获取验证码】验证码缓存失败:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
response.OkWithMessage("获取成功", c)
}
// h5 sdk config
func (u *User) GetSDKConfig(c *gin.Context) {
pageURL := c.Query("url")
if pageURL == "" {
log.Printf("GetSDKConfig url 没传")
response.FailWithMessage("系统错误", c)
return
}
ticket, err := userService.GetJsapiTicket()
if err != nil || ticket == "" {
log.Printf("get jsapi ticket err:%v", err)
response.FailWithMessage("获取配置失败", c)
return
}
nonceStr := strconv.Itoa(rand.Int())
timestamp := strconv.FormatInt(time.Now().Unix(), 10)
signature := userService.CreateSignature(ticket, nonceStr, timestamp, pageURL)
response.OkWithData(gin.H{
"ticket": ticket,
"appId": config.ConfigData.System.WxH5AppId,
"timestamp": timestamp,
"nonceStr": nonceStr,
"signature": signature,
}, c)
}

359
api/verify.go Normal file
View File

@@ -0,0 +1,359 @@
package api
import (
"encoding/json"
"errors"
"fmt"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"log"
"net/http"
"qnc-server/db"
"qnc-server/model/model"
"qnc-server/model/request"
"qnc-server/model/response"
"qnc-server/service"
"qnc-server/utils"
)
type Verify struct {
}
var verifisService service.VerifyService
// 注册路由
func InitVerif(group *gin.RouterGroup) {
var c Verify
{
verifPrivateGroup := group.Group("verify")
verifPrivateGroup.Use(JWTAuth())
verifPrivateGroup.POST("phone", c.VerifyNamePhone)
verifPrivateGroup.GET("get_query", c.GetQueryRecord)
verifPrivateGroup.POST("card_no", c.VerifyNameCard)
verifPrivateGroup.POST("bank_card", c.VerifyBankCard)
verifPrivateGroup.POST("skill_cert", c.VerifySkillCert)
}
}
// 姓名手机二要素核验
func (verify *Verify) VerifyNamePhone(c *gin.Context) {
var reqBody request.VerifyNamePhoneReq
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
userid := utils.GetUserID(c)
// 查找是否有 未消费 已付费 的订单
order, err := orderService.QueryConsumedOrder(userid, "shoujiheyan")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 为空就没有消费资格
response.OkNeedPay(c)
return
} else {
log.Printf("【手机号核验】查询订单错误:%s", err.Error())
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
}
// 二要素核验
body := map[string]any{
"mobile": reqBody.Phone,
"name": reqBody.Name,
}
respStr, err := requestService.AliYunRequest(http.MethodPost, "http://yyseys.qianshutong.com/web/interface/yyseysyz", nil, body)
if err != nil {
log.Printf("【手机号核验】阿里响应错误: %s", err.Error())
notifyService.SendNotification("阿里响应错误请及时处理", "手机号核验", userid, order.ID)
response.FailRefund(c)
return
}
var verifyPhoneName = model.VerifyPhoneName{}
verifyPhoneName.Resp = respStr
verifyPhoneName.Code, _ = utils.EncryptCode(reqBody.Phone, "776d538f1265a814437d2e12ac10b45b85045bc208a1eb129ef301f4157aa82f")
verifyPhoneName.OrderID = order.ID
verifyPhoneName.UserID = userid
err = db.DB.Create(&verifyPhoneName).Error
if err != nil {
log.Printf("【手机号核验】记录失败:%v", err)
notifyService.SendNotification("【手机号核验】数据保存错误,请及时处理", "手机号核验", userid, order.ID)
response.FailRefund(c)
return
}
orderService.OrderConsumed(order)
response.OkWithData(gin.H{
"record_id": verifyPhoneName.OrderID,
}, c)
}
// 姓名身份证二要素核验
func (verify *Verify) VerifyNameCard(c *gin.Context) {
var reqBody request.VerifyNameCardReq
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
userid := utils.GetUserID(c)
// 查找是否有 未消费 已付费 的订单
order, err := orderService.QueryConsumedOrder(userid, "shenfenzhengheyan")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 为空就没有消费资格
response.OkNeedPay(c)
return
} else {
log.Printf("【身份证核验】查询订单错误:%s", err.Error())
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
}
// 二要素核验
body := map[string]any{
"idcard": reqBody.Card,
"name": reqBody.Name,
}
respStr, err := requestService.AliYunRequest(http.MethodPost, "https://kzidcardv1.market.alicloudapi.com/api-mall/api/id_card/check", nil, body)
if err != nil {
log.Printf("【身份证核验】阿里响应错误: %s", err.Error())
notifyService.SendNotification("阿里响应错误请及时处理", "身份证核验", userid, order.ID)
response.FailRefund(c)
return
}
var verifyCardName = model.VerifyCardName{}
verifyCardName.Resp = respStr
verifyCardName.OrderID = order.ID
verifyCardName.UserID = userid
err = db.DB.Create(&verifyCardName).Error
if err != nil {
log.Printf("【身份证核验】记录失败:%v", err)
notifyService.SendNotification("【身份证核验】数据保存错误,请及时处理", "身份证核验", userid, order.ID)
response.FailRefund(c)
return
}
orderService.OrderConsumed(order)
response.OkWithData(gin.H{
"record_id": verifyCardName.OrderID,
}, c)
}
// 银行卡黑名单
func (verify *Verify) VerifyBankCard(c *gin.Context) {
var reqBody request.VerifyBankCardReq
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
userid := utils.GetUserID(c)
// 查找是否有 未消费 已付费 的订单
order, err := orderService.QueryConsumedOrder(userid, "yinhangkaheimingdan")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 为空就没有消费资格
response.OkNeedPay(c)
return
} else {
log.Printf("QueryConsumedOrder err:%s", err.Error())
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
}
var verifModel model.VerifyBankCard
reqData := map[string]interface{}{
"cardNo": reqBody.CardNo,
"cardld": reqBody.CardID,
"mobile": reqBody.Phone,
"name": reqBody.Name,
}
plainText, err := verifisService.VerifReqYuShan("FIN019", &reqData)
if err != nil {
log.Printf("【羽山接口请求错误】 %s error %s", "FIN019", err.Error())
notifyService.SendNotification("羽山接口请求错误,请及时处理", "银行卡黑名单查询V1", userid, order.ID)
response.FailRefund(c)
return
}
_, err = verifisService.VerifyDataAnalysis(string(plainText))
if err != nil {
if errors.Is(err, utils.ErrNoRecord) {
notifyService.SendNotification("用户查询数据为空,请及时处理", "银行卡黑名单查询V1", userid, order.ID)
} else {
notifyService.SendNotification("接口系统响应错误,请及时处理", "银行卡黑名单查询V1", userid, order.ID)
}
response.FailRefund(c)
return
}
verifModel.Resp = string(plainText)
verifModel.Code, _ = utils.EncryptCode(reqBody.Phone, "776d538f1265a814437d2e12ac10b45b85045bc208a1eb129ef301f4157aa82f")
verifModel.UserID = userid
err = db.DB.Create(&verifModel).Error
if err != nil {
log.Printf("银行卡黑名单查询V1保存错误%v", err)
notifyService.SendNotification("银行卡黑名单查询V1保存错误请及时处理", "银行卡黑名单查询V1", userid, order.ID)
response.FailRefund(c)
return
}
orderService.OrderConsumed(order)
response.OkWithData(gin.H{
"record_id": verifModel.OrderID,
}, c)
}
// 技能资格证书查询 HRD004
func (verify *Verify) VerifySkillCert(c *gin.Context) {
var reqBody request.VerifyNameCardReq
if err := c.ShouldBindJSON(&reqBody); err != nil {
response.FailWithMessage("Failed to read request body", c)
return
}
// 二要素核验
twoElementsParams := map[string]any{
"name": reqBody.Name,
"idcard": reqBody.Card,
}
twoElementsRespStr, err := requestService.AliYunRequest(http.MethodPost, "https://kzidcardv1.market.alicloudapi.com/api-mall/api/id_card/check", nil, twoElementsParams)
var twoElementsResp response.TwoElementsResp
err = json.Unmarshal([]byte(twoElementsRespStr), &twoElementsResp)
if err != nil {
log.Printf("二要素解析错误:%v", err)
response.FailWithMessage("系统错误,请稍后再试", c)
return
}
if twoElementsResp.Success == false {
response.FailWithMessage("请输入有效的身份证号码", c)
return
}
if twoElementsResp.Data.Result == 1 {
response.FailWithMessage("姓名与身份证不一致", c)
return
}
userid := utils.GetUserID(c)
// 查找是否有 未消费 已付费 的订单
order, err := orderService.QueryConsumedOrder(userid, "jinengzigezhengshu")
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
// 为空就没有消费资格
response.OkNeedPay(c)
return
} else {
log.Printf("QueryConsumedOrder err:%s", err.Error())
response.FailWithMessage("服务器错误请稍后再试", c)
return
}
}
var verifModel model.VerifySkillCert
reqData := map[string]interface{}{
"cardNo": reqBody.Card,
"name": reqBody.Name,
}
plainText, err := verifisService.VerifReqYuShan("HRD004", &reqData)
if err != nil {
log.Printf("【羽山接口请求错误】 %s error %s", "HRD004", err.Error())
var isNoRecord = "羽山接口请求错误"
if order.PaymentMethod == model.PaymentMethod_ALIPAY {
err := orderService.AliRefund(ctx, order)
if err != nil {
notifyService.SendNotification(fmt.Sprintf("【支付宝】%s自动退款失败", isNoRecord), "技能资格证书查询", userid, order.ID)
} else {
notifyService.SendNotification("【支付宝】自动退款成功", order.Product.ProductName, order.Userid, order.ID)
}
} else {
err := orderService.WeChatRefund(order)
if err != nil {
notifyService.SendNotification(fmt.Sprintf("%s自动退款失败", isNoRecord), "技能资格证书查询", userid, order.ID)
}
}
response.FailRefund(c)
return
}
_, err = verifisService.VerifyCertDataAnalysis(string(plainText))
if err != nil {
var isNoRecord string
if errors.Is(err, utils.ErrNoRecord) {
isNoRecord = "用户查询为空"
} else {
isNoRecord = "接口系统错误"
}
if order.PaymentMethod == model.PaymentMethod_ALIPAY {
err := orderService.AliRefund(ctx, order)
if err != nil {
notifyService.SendNotification(fmt.Sprintf("【支付宝】%s自动退款失败", isNoRecord), "技能资格证书查询", userid, order.ID)
} else {
notifyService.SendNotification("【支付宝】自动退款成功", order.Product.ProductName, order.Userid, order.ID)
}
} else {
err := orderService.WeChatRefund(order)
if err != nil {
notifyService.SendNotification(fmt.Sprintf("%s自动退款失败", isNoRecord), "技能资格证书查询", userid, order.ID)
}
}
response.FailRefund(c)
return
}
verifModel.Resp = string(plainText)
verifModel.UserID = userid
err = db.DB.Create(&verifModel).Error
if err != nil {
log.Printf("技能资格证书查询保存错误:%v", err)
notifyService.SendNotification("技能资格证书查询保存错误,请及时处理", "技能资格证书查询", userid, order.ID)
response.FailRefund(c)
return
}
orderService.OrderConsumed(order)
response.OkWithData(gin.H{
"record_id": verifModel.OrderID,
}, c)
}
func (verify *Verify) GetQueryRecord(c *gin.Context) {
var req request.GetQueryRecordReq
if err := c.ShouldBindQuery(&req); err != nil {
response.FailWithMessage("Failed to read request", c)
return
}
userid := utils.GetUserID(c)
var record interface{}
var err error
if req.IsCase {
record, err = verifisService.GetRecordCaseByFeature(req.Feature)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
log.Printf("无相关示例记录:%v", err)
response.FailWithMessage("无相关示例记录", c)
return
} else {
log.Printf("查询示例记录错误:%v", err)
response.FailWithMessage(err.Error(), c)
return
}
}
} else {
record, err = verifisService.GetRecordByFeature(req.Feature, req.ID, userid)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
log.Printf("无相关记录:%v", err)
response.FailWithMessage("无相关记录", c)
return
} else {
log.Printf("查询记录错误:%v", err)
response.FailWithMessage(err.Error(), c)
return
}
}
}
response.OkWithData(record, c)
}