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

205
service/antifraud.go Normal file
View File

@@ -0,0 +1,205 @@
package service
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"io"
"log"
"net/http"
"qnc-server/config"
"qnc-server/db"
"qnc-server/model/model"
"qnc-server/model/request"
"qnc-server/utils"
"strings"
"time"
)
type AntiFraudService struct {
}
const RetCodeSuccess = "000000"
const RetCodeEmpty = "100000"
func (a *AntiFraudService) ReqYuShan(data *model.AntiFraudReqPayload) (plainText []byte, err error) {
currentTime := time.Now()
unixMilliseconds := currentTime.UnixNano() / int64(time.Millisecond)
requestSN, _ := utils.GenerateRandomString()
apiKey := config.ConfigData.Antifraud.ApiKey
acctId := config.ConfigData.Antifraud.AcctId
httpUrl := config.ConfigData.Antifraud.HttpUrl
// 根据 ProdID 动态构建请求数据
reqData := map[string]interface{}{
"prod_id": data.ProdID,
"req_time": unixMilliseconds,
"request_sn": requestSN,
"req_data": map[string]interface{}{},
}
for _, param := range model.AntiFraudProdIDParams[data.ProdID] {
switch param {
case "cardNo":
reqData["req_data"].(map[string]interface{})["cardNo"] = data.CardNo
case "name":
reqData["req_data"].(map[string]interface{})["name"] = data.Name
case "mobile":
reqData["req_data"].(map[string]interface{})["mobile"] = data.Mobile
case "type":
reqData["req_data"].(map[string]interface{})["type"] = 3
case "keyWord":
reqData["req_data"].(map[string]interface{})["keyWord"] = data.CardNo
}
}
messageBytes, err := json.Marshal(reqData)
if err != nil {
return
}
key, err := hex.DecodeString(apiKey)
if err != nil {
return
}
//加密
cipherText := utils.AES_CBC_Encrypt(messageBytes, key)
content := base64.StdEncoding.EncodeToString(cipherText)
respStr, err := httpDo(httpUrl, content, acctId)
if err != nil {
return
}
//解密
sDec, err := base64.StdEncoding.DecodeString(respStr)
if err != nil {
if utils.IsJSON(respStr) {
return []byte(respStr), nil
}
return
}
plainText = utils.AES_CBC_Decrypt(sDec, key)
return
}
// post请求
func httpDo(url string, content string, acctId string) (stringBody string, err error) {
client := &http.Client{}
req, err := http.NewRequest("POST", url, strings.NewReader(content))
if err != nil {
return
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("ACCT_ID", acctId)
resp, err := client.Do(req)
if err != nil {
return
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
return
}
return string(body), nil
}
// 对返回信息进行处理,返回一份完整的报告
func (a *AntiFraudService) DataAnalysis(antiFraud model.AntiFraud) (report model.AntiFraudReport) {
// 反欺诈评分(标准简版),分值越低风险越高
var fraudScoreResp model.YuShanResponse[model.FraudScoreRetData]
err := json.Unmarshal([]byte(antiFraud.FraudScoreResp), &fraudScoreResp)
if err != nil {
log.Printf("fraudScoreResp Unmarshal error%v", err)
}
if fraudScoreResp.Retcode == RetCodeSuccess {
report.FraudScore.Score = utils.ConvertScore(fraudScoreResp.Retdata.BjScore, 300, 850)
} else {
log.Printf("fraudScoreResp error%s", fraudScoreResp.Retmsg)
}
// 综合风险评估
var riskAssessmentResp model.YuShanResponse[model.RiskAssessmentRetData]
err = json.Unmarshal([]byte(antiFraud.RiskAssessmentResp), &riskAssessmentResp)
if err != nil {
log.Printf("riskAssessmentResp Unmarshal error%v", err)
}
if riskAssessmentResp.Retcode == RetCodeSuccess {
report.RiskAssessment.Score = utils.ConvertScore(riskAssessmentResp.Retdata.BjScore, 300, 850)
} else {
log.Printf("riskAssessmentResp error%s", riskAssessmentResp.Retmsg)
}
// 风险手机号列表核验
var mobileRiskCheckResp model.YuShanResponse[map[string]interface{}]
err = json.Unmarshal([]byte(antiFraud.MobileRiskCheckResp), &mobileRiskCheckResp)
if err != nil {
log.Printf("mobileRiskCheckResp Unmarshal error%v", err)
}
if mobileRiskCheckResp.Retcode == RetCodeSuccess {
report.MobileRiskCheck = mobileRiskCheckResp.Retdata
} else {
log.Printf("mobileRiskCheckResp error%s", mobileRiskCheckResp.Retmsg)
}
// 风险人员V2 RiskPersonV2Resp
var riskPersonV2Resp model.YuShanResponse[map[string]interface{}]
err = json.Unmarshal([]byte(antiFraud.RiskPersonV2Resp), &riskPersonV2Resp)
if err != nil {
log.Printf("riskPersonV2Resp Unmarshal error%v", err)
}
if riskPersonV2Resp.Retcode == RetCodeSuccess {
report.RiskPersonV2 = riskPersonV2Resp.Retdata
} else {
log.Printf("riskPersonV2Resp error%s", riskPersonV2Resp.Retmsg)
}
// 反诈反赌核验 AntiFraudGamblingResp
var antiFraudGamblingResp model.YuShanResponse[map[string]interface{}]
err = json.Unmarshal([]byte(antiFraud.AntiFraudGamblingResp), &antiFraudGamblingResp)
if err != nil {
log.Printf("antiFraudGamblingResp Unmarshal error%v", err)
}
if antiFraudGamblingResp.Retcode == RetCodeSuccess {
report.AntiFraudGambling = antiFraudGamblingResp.Retdata
} else {
log.Printf("antiFraudGamblingResp error%s", antiFraudGamblingResp.Retmsg)
}
// 电信诈骗名单 TelecomFraudListResp
var telecomFraudListResp model.YuShanResponse[map[string]interface{}]
err = json.Unmarshal([]byte(antiFraud.TelecomFraudListResp), &telecomFraudListResp)
if err != nil {
log.Printf("telecomFraudListResp Unmarshal error%v", err)
}
if telecomFraudListResp.Retcode == RetCodeSuccess {
report.TelecomFraudList = telecomFraudListResp.Retdata
} else {
log.Printf("telecomFraudListResp error%s", telecomFraudListResp.Retmsg)
}
// 个人可信度 PersonalCredibilityResp
var personalCredibilityResp model.YuShanResponse[map[string]interface{}]
err = json.Unmarshal([]byte(antiFraud.PersonalCredibilityResp), &personalCredibilityResp)
if err != nil {
log.Printf("personalCredibilityResp Unmarshal error%v", err)
}
if personalCredibilityResp.Retcode == RetCodeSuccess {
report.PersonalCredibility = personalCredibilityResp.Retdata
} else {
log.Printf("personalCredibilityResp error%s", personalCredibilityResp.Retmsg)
}
return report
}
func (a *AntiFraudService) GetAntifraud(reqBody request.AntiFraudQueryReq) (af model.AntiFraud) {
db.DB.Where("card_no = ? AND name = ? AND mobile = ?", reqBody.CardNo, reqBody.Name, reqBody.Mobile).First(&af)
return
}
func (a *AntiFraudService) QueryAntifraudOrder(orderID uint) (antifraud []model.AntiFraud, err error) {
err = db.DB.Where("order_id = ?", orderID).Find(&antifraud).Error
if err != nil {
log.Printf("query antifraud order failed: %v", err)
}
return
}

354
service/car.go Normal file
View File

@@ -0,0 +1,354 @@
package service
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"github.com/gin-gonic/gin"
"log"
"qnc-server/config"
"qnc-server/db"
"qnc-server/model/model"
"qnc-server/utils"
"time"
)
type CarService struct {
}
func (c *CarService) CarReqYuShan(prodID string, data *map[string]interface{}) (plainText []byte, err error) {
currentTime := time.Now()
unixMilliseconds := currentTime.UnixNano() / int64(time.Millisecond)
requestSN, _ := utils.GenerateRandomString()
apiKey := config.ConfigData.Car.ApiKey
acctId := config.ConfigData.Car.AcctId
httpUrl := config.ConfigData.Car.HttpUrl
// 根据 ProdID 动态构建请求数据
reqData := map[string]interface{}{
"prod_id": prodID,
"req_time": unixMilliseconds,
"request_sn": requestSN,
"req_data": data,
}
messageBytes, err := json.Marshal(reqData)
if err != nil {
return
}
key, err := hex.DecodeString(apiKey)
if err != nil {
return
}
//加密
cipherText := utils.AES_CBC_Encrypt(messageBytes, key)
content := base64.StdEncoding.EncodeToString(cipherText)
respStr, err := httpDo(httpUrl, content, acctId)
if err != nil {
return
}
//解密
sDec, err := base64.StdEncoding.DecodeString(respStr)
if err != nil {
if utils.IsJSON(respStr) {
return []byte(respStr), nil
}
return
}
plainText = utils.AES_CBC_Decrypt(sDec, key)
return
}
// 对返回信息进行处理,返回一份完整的报告
func (c *CarService) CarInsuranceDataAnalysis(respString string) (data map[string]interface{}, err error) {
var resp model.YuShanResponse[map[string]interface{}]
err = json.Unmarshal([]byte(respString), &resp)
if err != nil {
log.Printf("CarInsurance Unmarshal error%v", err)
return nil, utils.ErrSystemError
}
if resp.Retcode == RetCodeSuccess {
data = resp.Retdata
return data, nil
} else if resp.Retcode == RetCodeEmpty {
log.Printf("Car resp empty%s", resp.Retmsg)
return data, utils.ErrNoRecord
} else {
log.Printf("Car resp error%s", resp.Retmsg)
return data, utils.ErrSystemError
}
}
// 对返回信息进行处理,返回一份完整的报告
func (c *CarService) CarDataAnalysis(respString string) (data model.YuShanResponse[map[string]interface{}], err error) {
err = json.Unmarshal([]byte(respString), &data)
if err != nil {
log.Printf("CarInsurance Unmarshal error%v", err)
return data, utils.ErrUnmashal
}
if data.Retcode == RetCodeSuccess {
return data, nil
} else if data.Retcode == RetCodeEmpty {
log.Printf("Car resp empty%s", data.Retmsg)
return data, utils.ErrNoRecord
} else {
log.Printf("Car resp error%s", data.Retmsg)
return data, utils.ErrSystemError
}
}
// 对返回信息进行处理,返回一份完整的报告 人车核验特殊版
func (c *CarService) CarPersonCarVerifyDataAnalysis(respString string) (data map[string]interface{}, err error) {
var resp model.YuShanResponse[map[string]interface{}]
err = json.Unmarshal([]byte(respString), &resp)
if err != nil {
log.Printf("CarPersonCarVerifyDataAnalysis Unmarshal error%v", err)
return nil, utils.ErrSystemError
}
if resp.Retcode == RetCodeSuccess {
if resp.Retdata == nil {
log.Printf("Car resp empty%s", resp.Retmsg)
return data, utils.ErrNoRecord
} else {
data = resp.Retdata
return data, nil
}
} else if resp.Retcode == RetCodeEmpty {
log.Printf("Car resp empty%s", resp.Retmsg)
return data, utils.ErrNoRecord
} else {
log.Printf("Car resp error%s", resp.Retmsg)
return data, utils.ErrSystemError
}
}
func (c *CarService) GetRecordByFeature(feature string, id uint, userID uint) (interface{}, error) {
var result interface{}
switch feature {
case "chexian":
var record model.CarInsurance
if err := db.DB.Where("order_id = ? AND userid = ?", id, userID).First(&record).Error; err != nil {
return nil, err
}
analysis, err := c.CarInsuranceDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
result = analysis
case "weibao":
var record model.CarMaintenance
if err := db.DB.Where("order_id = ? AND userid = ?", id, userID).First(&record).Error; err != nil {
return nil, err
}
analysis, err := c.CarInsuranceDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
result = analysis
case "rencheheyan":
var record model.CarPersonCarVerify
if err := db.DB.Where("order_id = ? AND userid = ?", id, userID).First(&record).Error; err != nil {
return nil, err
}
analysis, err := c.CarPersonCarVerifyDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
result = analysis
case "mingxiacheliang":
var record model.CarUnderName
if err := db.DB.Where("order_id = ? AND userid = ?", id, userID).First(&record).Error; err != nil {
return nil, err
}
analysis, err := c.CarInsuranceDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
result = analysis
case "cheliangshangxian":
var record model.CarInsuranceInfo
if err := db.DB.Where("order_id = ? AND userid = ?", id, userID).First(&record).Error; err != nil {
return nil, err
}
analysisSx, err := c.CarInsuranceDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
analysisWx, err := c.CarInsuranceDataAnalysis(record.FiveResp)
if err != nil {
return nil, err
}
result = gin.H{
"shangxian": analysisSx,
"chewuxiang": analysisWx,
}
case "cheliangguohu":
var record model.CarVehicleTransfer
if err := db.DB.Where("order_id = ?", id).First(&record).Error; err != nil {
return nil, err
}
analysis, err := c.CarInsuranceDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
result = analysis
case "vinchache":
var record model.CarVinCheckInfo
if err := db.DB.Where("order_id = ? AND userid = ?", id, userID).First(&record).Error; err != nil {
return nil, err
}
analysis, err := c.CarInsuranceDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
result = analysis
case "cheliangguzhi":
var record model.CarVehicleValuation
if err := db.DB.Where("order_id = ? AND userid = ?", id, userID).First(&record).Error; err != nil {
return nil, err
}
analysis, err := c.CarInsuranceDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
result = analysis
default:
return nil, errors.New("Invalid feature")
}
return result, nil
}
func (c *CarService) GetRecordCaseByFeature(feature string) (interface{}, error) {
var result interface{}
switch feature {
case "chexian":
var record model.CarInsurance
if err := db.DB.Where("id = ?", 1).First(&record).Error; err != nil {
return nil, err
}
analysis, err := c.CarInsuranceDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
result = analysis
case "weibao":
var record model.CarMaintenance
if err := db.DB.Where("id = ?", 1).First(&record).Error; err != nil {
return nil, err
}
analysis, err := c.CarInsuranceDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
result = analysis
case "mingxiacheliang":
var record model.CarUnderName
if err := db.DB.Where("id = ? ", 1).First(&record).Error; err != nil {
return nil, err
}
analysis, err := c.CarInsuranceDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
result = analysis
case "rencheheyan":
var record model.CarPersonCarVerify
if err := db.DB.Where("id = ? ", 1).First(&record).Error; err != nil {
return nil, err
}
analysis, err := c.CarPersonCarVerifyDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
result = analysis
case "cheliangshangxian":
var record model.CarInsuranceInfo
if err := db.DB.Where("id = ?", 1).First(&record).Error; err != nil {
return nil, err
}
analysisSx, err := c.CarInsuranceDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
analysisWx, err := c.CarInsuranceDataAnalysis(record.FiveResp)
if err != nil {
return nil, err
}
result = gin.H{
"shangxian": analysisSx,
"chewuxiang": analysisWx,
}
case "vinchache":
var record model.CarVinCheckInfo
if err := db.DB.Where("id = ?", 1).First(&record).Error; err != nil {
return nil, err
}
analysis, err := c.CarInsuranceDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
result = analysis
case "cheliangguohu":
var record model.CarVehicleTransfer
if err := db.DB.Where("id = ?", 1).First(&record).Error; err != nil {
return nil, err
}
analysis, err := c.CarInsuranceDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
result = analysis
case "cheliangguzhi":
var record model.CarVehicleValuation
if err := db.DB.Where("id = ?", 1).First(&record).Error; err != nil {
return nil, err
}
analysis, err := c.CarInsuranceDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
result = analysis
default:
return nil, errors.New("Invalid feature")
}
return result, nil
}
func (c *CarService) DecodeYushan(resp []byte) (plainText []byte, err error) {
apiKey := config.ConfigData.Car.ApiKey
key, err := hex.DecodeString(apiKey)
if err != nil {
return
}
respStr := string(resp)
sDec, err := base64.StdEncoding.DecodeString(respStr)
if err != nil {
if utils.IsJSON(respStr) {
log.Printf("车辆维保记录CAR058回调 DecodeJSON err:%s", respStr)
return []byte(respStr), nil
}
}
plainText = utils.AES_CBC_Decrypt(sDec, key)
return plainText, nil
}
func (c *CarService) GetMaintenanceBySn(sn string) (maintenance *model.CarMaintenance, err error) {
err = db.DB.Where("notify_sn = ?", sn).First(&maintenance).Error
if err != nil {
return nil, err
}
return maintenance, nil
}
func (c *CarService) GetMaintenanceList(pageSize int, pageNum int, userId uint) (list []model.CarMaintenance, err error) {
offset := (pageNum - 1) * pageSize
result := db.DB.Model(&model.CarMaintenance{}).
Select("id, created_at, updated_at, order_id, status, userid, vin").
Where("userid = ?", userId).Order("id desc").
Limit(pageSize).
Offset(offset).
Find(&list)
if result.Error != nil {
return list, result.Error
}
return list, nil
}

117
service/ent.go Normal file
View File

@@ -0,0 +1,117 @@
package service
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"qnc-server/config"
"qnc-server/db"
"qnc-server/model/model"
"qnc-server/model/request"
"qnc-server/model/response"
"strconv"
)
type EntSerivce struct {
}
// 请求天眼查
func (e *EntSerivce) RequestTycOpen(data interface{}, link string) (respData interface{}, err error) {
baseURL := config.ConfigData.TycOpen.Url
completeURL := fmt.Sprintf("%s/%s", baseURL, link)
token := config.ConfigData.TycOpen.Token
// 创建查询参数
params := url.Values{}
// 根据数据类型设置查询参数
switch v := data.(type) {
case request.EntReq:
params.Add("keyword", v.Keyword)
case request.EntListReq:
if link == "open/v4/open/partners" || link == "open/human/companyholding/2.0" {
params.Add("cid", v.Keyword)
} else {
params.Add("keyword", v.Keyword)
}
if v.PageNum != 0 {
params.Add("page_num", strconv.Itoa(v.PageNum))
}
if v.PageSize != 0 {
params.Add("page_size", strconv.Itoa(v.PageSize))
}
case request.EntSearchReq:
params.Add("word", v.Work)
if v.PageNum != 0 {
params.Add("page_num", strconv.Itoa(v.PageNum))
}
if v.PageSize != 0 {
params.Add("page_size", strconv.Itoa(v.PageSize))
}
default:
err = fmt.Errorf("未知的数据类型")
return
}
// 构建完整的请求 URL
requestURL := fmt.Sprintf("%s?%s", completeURL, params.Encode())
// 创建请求
reqBody := bytes.NewBuffer([]byte{})
req, err := http.NewRequest("GET", requestURL, reqBody)
if err != nil {
log.Printf("failed 创建请求: %v", err)
return
}
// 设置请求头
req.Header.Set("Authorization", token)
// 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Printf("failed 发送请求: %v", err)
return
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Printf("failed 读取响应体: %v", err)
return
}
log.Printf("【企业响应】: %v", string(body))
var queryResp response.EntResponse
err = json.Unmarshal(body, &queryResp)
if err != nil {
log.Printf("failed to unmarshal response: %v", err)
return
}
if queryResp.ErrorCode == 0 || queryResp.ErrorCode == 300000 {
respData = queryResp.Result
return
}
if message, exists := response.EntRepsStatusCodeMap[queryResp.ErrorCode]; exists {
log.Println(message, queryResp.Reason, requestURL)
return nil, fmt.Errorf("response status error: %s", queryResp.Reason)
} else {
log.Println("未知的状态码:", queryResp.Reason)
return nil, fmt.Errorf("response undefind status error: %s", queryResp.Reason)
}
}
func (e *EntSerivce) QueryEntFeatureOrder(orderID uint) (entFeature []model.EntFeature, err error) {
err = db.DB.Where("order_id = ?", orderID).Find(&entFeature).Error
if err != nil {
log.Printf("query entFeature order failed: %v", err)
}
return
}
func (e *EntSerivce) SaveEntFeatureRecords(record model.EntFeature) (err error) {
err = db.DB.Save(&record).Error
if err != nil {
log.Printf("entFeature save record failed: %v", err)
}
return
}

174
service/feature.go Normal file
View File

@@ -0,0 +1,174 @@
package service
import (
"encoding/json"
"fmt"
"github.com/jung-kurt/gofpdf"
"io"
"log"
"net/http"
"os"
"qnc-server/utils"
"strings"
"time"
)
const API_KEY = "aMsrBNGUJxgcgqdm3SEdcumm"
const SECRET_KEY = "sWlv2h2AWA3aAt5bjXCkE6WeA5AzpAAD"
type Feature struct {
}
// Service 函数:接受 base64 编码的图像,返回 OCR 识别结果
func (f *Feature) OCRService(imageBase64 string) (string, error) {
// 获取 Access Token
accessToken := f.GetAccessToken()
if accessToken == "" {
return "", fmt.Errorf("获取 Access Token 失败")
}
// 设置请求 URL
url := "https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic?access_token=" + accessToken
// 设置请求体,包含图片的 base64 编码
payload := strings.NewReader(fmt.Sprintf("image=%s&detect_direction=false&paragraph=false&probability=false&multidirectional_recognize=false", imageBase64))
// 创建 HTTP 客户端和请求
client := &http.Client{}
req, err := http.NewRequest("POST", url, payload)
if err != nil {
return "", err
}
// 设置请求头
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
req.Header.Add("Accept", "application/json")
// 发送请求并获取响应
res, err := client.Do(req)
if err != nil {
return "", err
}
defer res.Body.Close()
// 读取响应体
body, err := io.ReadAll(res.Body)
if err != nil {
return "", err
}
return string(body), nil
}
/**
* 使用 AKSK 生成鉴权签名Access Token
* @return string 鉴权签名信息Access Token
*/
func (f *Feature) GetAccessToken() string {
url := "https://aip.baidubce.com/oauth/2.0/token"
postData := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s", API_KEY, SECRET_KEY)
resp, err := http.Post(url, "application/x-www-form-urlencoded", strings.NewReader(postData))
if err != nil {
fmt.Println(err)
return ""
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
return ""
}
accessTokenObj := map[string]interface{}{}
_ = json.Unmarshal(body, &accessTokenObj)
return accessTokenObj["access_token"].(string)
}
func (f *Feature) GenerateSignPDF(name string, id uint, signatureBase64 string) (fileName string, err error) {
// 创建 PDF 实例A4 纸张大小
pdf := gofpdf.New("P", "mm", "A4", "")
fontPath := "simhei.ttf"
// 添加中文字体支持
pdf.AddUTF8Font("SimHei", "", fontPath) // 使用 simhei.ttf 字体
pdf.SetFont("SimHei", "", 12)
// 添加一页
pdf.AddPage()
// 插入标题,居中对齐
pdf.SetFont("SimHei", "", 20)
pdf.CellFormat(0, 10, "声明", "", 1, "C", false, 0, "") // C 表示居中对齐
pdf.Ln(12)
// 插入副标题
pdf.SetFont("SimHei", "", 14)
pdf.Cell(40, 10, "全能查声明:")
pdf.Ln(10)
// 插入正文内容,添加两字符的缩进(全角空格)
pdf.SetFont("SimHei", "", 12)
indent := "  " // 两字符的缩进,使用全角空格
pdf.MultiCell(0, 10, indent+"1、本产品所有数据均来自第三方。可能部分数据未公开数据更新延迟或信息受到限制不一定全部返回因此我们不做准确性、真实性、完整性承诺。不同数据格式及记录详细程度会存在差异此为行业正常现象。本报告仅供参考不能作为诉讼证据、决策性依据等材料请结合实际情况做出更准确的决策。", "", "", false)
pdf.Ln(3)
pdf.MultiCell(0, 10, indent+"2、本产品仅供用户本人查询或被授权查询。除非用户取得合法授权用户不得利用本产品查询他人信息。用户因未获得合法授权而擅自查询他人信息所产生的任何后果由用户自行承担责任。", "", "", false)
pdf.Ln(3)
// 插入承诺声明
pdf.SetFont("SimHei", "", 14)
pdf.Cell(40, 10, "本人承诺:")
pdf.Ln(10)
text := fmt.Sprintf("本人%s在此声明我即将通过此小程序查询的婚姻状态信息仅涉及本人。本人确认不将所得信息用作任何诉讼证据不会将所获取的信息用于任何非法用途包括但不限于欺诈、胁迫、侵犯隐私或其他违法行为不将所得信息转卖、传播给他人。本人承诺对查询结果所引发的任何后果承担全部责任。", name)
// 插入承诺内容,添加两字符的缩进
pdf.SetFont("SimHei", "", 12)
pdf.MultiCell(0, 10, indent+text, "", "", false)
pdf.Ln(10)
// 定位签名和日期到右下角
pageWidth, pageHeight := pdf.GetPageSize()
rightMargin := 20.0
bottomMargin := 20.0
// 设置签名部分的 XY 坐标,靠右下角
pdf.SetXY(pageWidth-rightMargin-40, pageHeight-bottomMargin-40) // 签名部分坐标
pdf.SetFont("SimHei", "", 12)
pdf.Cell(40, 10, "签名:")
// 解码 Base64 签名图片并保存为临时文件
var tempFileName = fmt.Sprintf("signature-%d.png", id)
signaturePath, err := utils.DecodeBase64ToPNG(signatureBase64, tempFileName)
if err != nil {
log.Printf("【签名图片生成】解码 Base64 签名图片并保存为临时文件失败:%v", err)
return "", err
}
// 在 PDF 中嵌入签名图片
pdf.ImageOptions(signaturePath, pageWidth-rightMargin-28, pageHeight-bottomMargin-40, 40, 10, false, gofpdf.ImageOptions{ImageType: "PNG"}, 0, "")
// 插入当前日期,显示在签名下方
currentDate := time.Now().Format("2006-01-02")
currentDateCN := time.Now().Format("2006年01月02日")
pdf.SetXY(pageWidth-rightMargin-40, pageHeight-bottomMargin-20)
pdf.Cell(40, 10, "日期:"+currentDateCN)
// 根据日期和 ID 动态生成文件名
outputDir := "signature"
os.MkdirAll(outputDir, os.ModePerm) // 如果不存在则创建目录
fileName = fmt.Sprintf("%s/%s_%d.pdf", outputDir, currentDate, id)
// 保存 PDF 文件
err = pdf.OutputFileAndClose(fileName)
if err != nil {
log.Printf("【签名图片生成】保存 PDF 文件失败:%v", err)
return "", err
}
// 删除临时签名图片文件
err = os.Remove(signaturePath)
if err != nil {
log.Printf("【签名图片生成】删除临时文件失败:%v", err)
}
return fileName, nil
}

280
service/lawsuitQuery.go Normal file
View File

@@ -0,0 +1,280 @@
package service
import (
"bytes"
"encoding/json"
"fmt"
"io"
"log"
"net/http"
"net/url"
"qnc-server/config"
"qnc-server/db"
"qnc-server/model/model"
"qnc-server/model/request"
"qnc-server/model/response"
"qnc-server/utils"
)
type LawsuitQueryService struct {
}
func (l *LawsuitQueryService) QueryList(reqData request.LawsuitQueryReq) (data []response.CaseDetail, err error) {
baseURL := config.ConfigData.LawsuitQuery.ListURL
appCode := config.ConfigData.LawsuitQuery.AppCode
// 创建查询参数
params := url.Values{}
params.Add("sign", utils.GenerateMD5())
params.Add("entityName", reqData.Name)
params.Add("entityId", reqData.CardNo)
params.Add("needBranch", "1")
params.Add("fl", "1")
// 构建完整的请求 URL
requestURL := fmt.Sprintf("%s?%s", baseURL, params.Encode())
// 创建请求
reqBody := bytes.NewBuffer([]byte{})
req, err := http.NewRequest("POST", requestURL, reqBody)
if err != nil {
log.Printf("failed 创建请求: %v", err)
return
}
// 设置请求头
req.Header.Set("Authorization", "APPCODE "+appCode)
// 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Printf("failed 发送请求: %v", err)
return
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Printf("failed 读取响应体: %v", err)
return
}
var queryResp response.LawsuitQueryListResponse
err = json.Unmarshal(body, &queryResp)
if err != nil {
log.Printf("query list failed to unmarshal response: %v", err)
return
}
switch queryResp.Rc {
case "0000":
var respData response.LawsuitQueryListData
queryRespBytes, err := json.Marshal(queryResp.Data)
if err != nil {
log.Printf("queryResp Marshal Error: %v", err)
return nil, fmt.Errorf("响应错误,请稍后再试")
}
err = json.Unmarshal(queryRespBytes, &respData)
if err != nil {
log.Printf("sxr queryResp Unmarshal Error: %v", err)
return nil, fmt.Errorf("响应错误,请稍后再试")
}
return respData.List, nil
case "0001":
log.Printf("涉诉专业版查询成功无数据: %v", queryResp.Msg)
return data, nil
case "1000":
log.Printf("apikey校验失败: %v", err)
return data, fmt.Errorf("响应错误,请稍后再试")
default:
log.Printf("未知响应代码: %s", queryResp.Rc)
return data, fmt.Errorf("响应错误,请稍后再试")
}
}
func (l *LawsuitQueryService) QuerySxrList(reqData request.LawsuitQueryReq) (data []response.SxrDetail, err error) {
baseURL := config.ConfigData.LawsuitQuery.SxrURL
appCode := config.ConfigData.LawsuitQuery.AppCode
// 创建查询参数
params := url.Values{}
params.Add("sign", utils.GenerateMD5())
params.Add("entityName", reqData.Name)
params.Add("entityId", reqData.CardNo)
params.Add("btype", "1")
params.Add("related", "0")
// 构建完整的请求 URL
requestURL := fmt.Sprintf("%s?%s", baseURL, params.Encode())
// 创建请求
reqBody := bytes.NewBuffer([]byte{})
req, err := http.NewRequest("POST", requestURL, reqBody)
if err != nil {
log.Printf("failed 创建请求: %v", err)
return
}
// 设置请求头
req.Header.Set("Authorization", "APPCODE "+appCode)
// 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Printf("failed 发送请求: %v", err)
return
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Printf("failed 读取响应体: %v", err)
return
}
var queryResp response.LawsuitQueryListResponse
err = json.Unmarshal(body, &queryResp)
if err != nil {
log.Printf("sxr failed to unmarshal response: %v", err)
return
}
switch queryResp.Rc {
case "0000":
var respData []response.SxrDetail
queryRespBytes, err := json.Marshal(queryResp.Data)
if err != nil {
log.Printf("queryResp Marshal Error: %v", err)
return nil, fmt.Errorf("响应错误,请稍后再试")
}
err = json.Unmarshal(queryRespBytes, &respData)
if err != nil {
log.Printf("sxr queryResp Unmarshal Error: %v", err)
return nil, fmt.Errorf("响应错误,请稍后再试")
}
return respData, nil
case "0001":
return data, nil
case "1000":
log.Printf("apikey校验失败: %v", err)
return data, fmt.Errorf("响应错误,请稍后再试")
default:
log.Printf("未知响应代码: %s", queryResp.Rc)
return data, fmt.Errorf("响应错误,请稍后再试")
}
}
func (l *LawsuitQueryService) QueryLimitHighList(reqData request.LawsuitQueryReq) (data []response.LimitHightDetail, err error) {
baseURL := config.ConfigData.LawsuitQuery.LimitHightURL
appCode := config.ConfigData.LawsuitQuery.AppCode
// 创建查询参数
params := url.Values{}
params.Add("name", reqData.Name)
params.Add("idcard_number", reqData.CardNo)
params.Add("mobile_number", reqData.Mobile)
// 创建请求
reqBody := bytes.NewBufferString(params.Encode())
req, err := http.NewRequest("POST", baseURL, reqBody)
if err != nil {
log.Printf("failed 创建请求: %v", err)
return
}
// 设置请求头
req.Header.Set("Authorization", "APPCODE "+appCode)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
// 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Printf("failed 发送请求: %v", err)
return
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
log.Printf("failed 读取响应体: %v", err)
return
}
var queryResp response.LawsuitLimitHightQueryListResponse
err = json.Unmarshal(body, &queryResp)
if err != nil {
log.Printf("limit hight failed to unmarshal response: %v", err)
return
}
switch queryResp.Code {
case 200:
var respData response.LimitHightData
queryRespBytes, err := json.Marshal(queryResp.Data)
if err != nil {
log.Printf("limit hight Marshal Error: %v", err)
return nil, fmt.Errorf("响应错误,请稍后再试")
}
err = json.Unmarshal(queryRespBytes, &respData)
if err != nil {
log.Printf("limit hight failed to unmarshal data: %v", err)
return nil, fmt.Errorf("响应错误,请稍后再试")
}
return respData.CaseList, nil
case 400:
return data, nil
default:
log.Printf("%s响应错误: %s", params.Encode(), queryResp.Msg)
return data, fmt.Errorf("请求错误:%s", queryResp.Msg)
}
}
func (l *LawsuitQueryService) SaveLawsuit(userid uint, reqBody request.LawsuitQueryReq, orderID uint, combinedResponse response.CombinedResponse) (err error) {
ListRespMarshal, err := json.Marshal(combinedResponse.ListResp)
if err != nil {
return err
}
SxrRespMarshal, err := json.Marshal(combinedResponse.SxrResp)
if err != nil {
return err
}
LimitHightRespMarshal, err := json.Marshal(combinedResponse.LimitHightResp)
if err != nil {
return err
}
err = db.DB.Create(&model.Lawsuit{
UserID: userid,
CardNo: reqBody.CardNo,
Name: reqBody.Name,
Mobile: reqBody.Mobile,
ListResp: string(ListRespMarshal),
SxrResp: string(SxrRespMarshal),
LimitHightResp: string(LimitHightRespMarshal),
OrderID: orderID,
}).Error
if err != nil {
return err
}
return
}
func (l *LawsuitQueryService) CheckPayment(userid uint) (payment bool, err error) {
var lawsuits []model.Lawsuit
err = db.DB.Where("userid = ?", userid).Order("id desc").Limit(2).Find(&lawsuits).Error
if err != nil {
return false, err
}
if len(lawsuits) == 0 {
return true, nil
}
checkFields := func(lawsuit model.Lawsuit) bool {
return (lawsuit.ListResp == "" || lawsuit.ListResp == "null") &&
(lawsuit.SxrResp == "" || lawsuit.SxrResp == "null") &&
(lawsuit.LimitHightResp == "" || lawsuit.LimitHightResp == "null")
}
if len(lawsuits) == 1 {
if checkFields(lawsuits[0]) {
return false, nil
}
}
if len(lawsuits) == 2 {
if checkFields(lawsuits[0]) && !checkFields(lawsuits[1]) {
return false, nil
}
}
return true, nil
}

175
service/loanEvaluation.go Normal file
View File

@@ -0,0 +1,175 @@
package service
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"log"
"qnc-server/config"
"qnc-server/db"
"qnc-server/model/model"
"qnc-server/utils"
"time"
)
const RetCodeYiDong = "000001"
const RetCodeLianTong = "000002"
const RetCodeDianXin = "000003"
type LoanEvaluationService struct {
}
func (a *LoanEvaluationService) QueryLoanEvaluationOrder(orderID uint) (oanEvaluation []model.LoanEvaluation, err error) {
err = db.DB.Where("order_id = ?", orderID).Find(&oanEvaluation).Error
if err != nil {
log.Printf("query oanEvaluation order failed: %v", err)
}
return
}
func (a *LoanEvaluationService) ReqYuShan(data *model.LoanEvaluationReqPayload) (plainText []byte, err error) {
currentTime := time.Now()
unixMilliseconds := currentTime.UnixNano() / int64(time.Millisecond)
requestSN, _ := utils.GenerateRandomString()
apiKey := config.ConfigData.Antifraud.ApiKey
acctId := config.ConfigData.Antifraud.AcctId
httpUrl := config.ConfigData.Antifraud.HttpUrl
// 根据 ProdID 动态构建请求数据
reqData := map[string]interface{}{
"prod_id": data.ProdID,
"req_time": unixMilliseconds,
"request_sn": requestSN,
"req_data": map[string]interface{}{},
}
for _, param := range model.LoanEvaluationProdIDParams[data.ProdID] {
switch param {
case "cardNo":
reqData["req_data"].(map[string]interface{})["cardNo"] = data.CardNo
case "name":
reqData["req_data"].(map[string]interface{})["name"] = data.Name
case "mobile":
reqData["req_data"].(map[string]interface{})["mobile"] = data.Mobile
case "encryptionType":
reqData["req_data"].(map[string]interface{})["encryptionType"] = 1
case "idCard":
reqData["req_data"].(map[string]interface{})["idCard"] = data.CardNo
case "phone":
reqData["req_data"].(map[string]interface{})["phone"] = data.Mobile
}
}
messageBytes, err := json.Marshal(reqData)
if err != nil {
return
}
key, err := hex.DecodeString(apiKey)
if err != nil {
return
}
//加密
cipherText := utils.AES_CBC_Encrypt(messageBytes, key)
content := base64.StdEncoding.EncodeToString(cipherText)
respStr, err := httpDo(httpUrl, content, acctId)
if err != nil {
return
}
//解密
sDec, err := base64.StdEncoding.DecodeString(respStr)
if err != nil {
if utils.IsJSON(respStr) {
return []byte(respStr), nil
}
return
}
plainText = utils.AES_CBC_Decrypt(sDec, key)
return
}
// 对返回信息进行处理,返回一份完整的报告
func (a *LoanEvaluationService) DataAnalysis(loanEvaluation model.LoanEvaluation) (report model.LoanEvaluationReport) {
// 手机月消费档次
var mobileMonthlyConsumptionLevelResp model.YuShanResponse[map[string]interface{}]
err := json.Unmarshal([]byte(loanEvaluation.MobileMonthlyConsumptionLevelResp), &mobileMonthlyConsumptionLevelResp)
if err != nil {
log.Printf("mobileMonthlyConsumptionLevelResp Unmarshal error%v", err)
}
if mobileMonthlyConsumptionLevelResp.Retcode == RetCodeSuccess || mobileMonthlyConsumptionLevelResp.Retcode == RetCodeYiDong || mobileMonthlyConsumptionLevelResp.Retcode == RetCodeLianTong || mobileMonthlyConsumptionLevelResp.Retcode == RetCodeDianXin {
report.MobileMonthlyConsumptionLevel = mobileMonthlyConsumptionLevelResp.Retdata
} else {
log.Printf("mobileMonthlyConsumptionLevelResp error%s", mobileMonthlyConsumptionLevelResp.Retmsg)
}
// 近六个月欠费停机次数查询
var recentSixMonthsSuspendedTimesResp model.YuShanResponse[map[string]interface{}]
err = json.Unmarshal([]byte(loanEvaluation.RecentSixMonthsSuspendedTimesResp), &recentSixMonthsSuspendedTimesResp)
if err != nil {
log.Printf("recentSixMonthsSuspendedTimesResp Unmarshal error%v", err)
}
if recentSixMonthsSuspendedTimesResp.Retcode == RetCodeSuccess || recentSixMonthsSuspendedTimesResp.Retcode == RetCodeYiDong || recentSixMonthsSuspendedTimesResp.Retcode == RetCodeLianTong || recentSixMonthsSuspendedTimesResp.Retcode == RetCodeDianXin {
report.RecentSixMonthsSuspendedTimes = recentSixMonthsSuspendedTimesResp.Retdata
} else {
log.Printf("recentSixMonthsSuspendedTimesResp error%s", recentSixMonthsSuspendedTimesResp.Retmsg)
}
// 逾选指数 OverdueSelectionIndexResp
var overdueSelectionIndexResp model.YuShanResponse[map[string]interface{}]
err = json.Unmarshal([]byte(loanEvaluation.OverdueSelectionIndexResp), &overdueSelectionIndexResp)
if err != nil {
log.Printf("overdueSelectionIndexResp Unmarshal error%v", err)
}
if overdueSelectionIndexResp.Retcode == RetCodeSuccess {
report.OverdueSelectionIndex = overdueSelectionIndexResp.Retdata
} else {
log.Printf("overdueSelectionIndexResp error%s", overdueSelectionIndexResp.Retmsg)
}
// 付选指数 PaymentSelectionIndexResp
var paymentSelectionIndexResp model.YuShanResponse[map[string]interface{}]
err = json.Unmarshal([]byte(loanEvaluation.PaymentSelectionIndexResp), &paymentSelectionIndexResp)
if err != nil {
log.Printf("paymentSelectionIndexResp Unmarshal error%v", err)
}
if paymentSelectionIndexResp.Retcode == RetCodeSuccess {
report.PaymentSelectionIndex = paymentSelectionIndexResp.Retdata
} else {
log.Printf("paymentSelectionIndexResp error%s", paymentSelectionIndexResp.Retmsg)
}
// 贷选指数 LoanSelectionIndexResp
var loanSelectionIndexResp model.YuShanResponse[map[string]interface{}]
err = json.Unmarshal([]byte(loanEvaluation.LoanSelectionIndexResp), &loanSelectionIndexResp)
if err != nil {
log.Printf("loanSelectionIndexResp Unmarshal error%v", err)
}
if loanSelectionIndexResp.Retcode == RetCodeSuccess {
report.LoanSelectionIndex = loanSelectionIndexResp.Retdata
} else {
log.Printf("loanSelectionIndexResp error%s", loanSelectionIndexResp.Retmsg)
}
// 借选指数 BorrowSelectionIndexResp
var borrowSelectionIndexResp model.YuShanResponse[map[string]interface{}]
err = json.Unmarshal([]byte(loanEvaluation.BorrowSelectionIndexResp), &borrowSelectionIndexResp)
if err != nil {
log.Printf("borrowSelectionIndexResp Unmarshal error%v", err)
}
if borrowSelectionIndexResp.Retcode == RetCodeSuccess {
report.BorrowSelectionIndex = borrowSelectionIndexResp.Retdata
} else {
log.Printf("borrowSelectionIndexResp error%s", borrowSelectionIndexResp.Retmsg)
}
// 金融黑名单 FinancialBlacklistResp
var financialBlacklistResp model.YuShanResponse[map[string]interface{}]
err = json.Unmarshal([]byte(loanEvaluation.FinancialBlacklistResp), &financialBlacklistResp)
if err != nil {
log.Printf("financialBlacklistResp Unmarshal error%v", err)
}
if financialBlacklistResp.Retcode == RetCodeSuccess {
report.FinancialBlacklist = financialBlacklistResp.Retdata
} else {
log.Printf("financialBlacklistResp error%s", financialBlacklistResp.Retmsg)
}
return report
}

127
service/notify.go Normal file
View File

@@ -0,0 +1,127 @@
package service
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"qnc-server/config"
"qnc-server/utils"
"strconv"
"time"
)
// const webhookURL = "https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=3b71e94f-8ca6-459b-b678-1db890e8170f"
type Notify struct {
}
type MarkdownMessage struct {
MsgType string `json:"msgtype"`
Markdown Markdown `json:"markdown"`
}
type Markdown struct {
Content string `json:"content"`
}
func (n *Notify) SendNotification(message string, interfa string, userID uint, orderID uint) {
enID, err := utils.IDEncrypt(strconv.Itoa(int(orderID)), "njbh287yfbuyh18suygbhd98")
if err != nil {
log.Println("IDEncrypt error", err)
return
}
url := fmt.Sprintf("%s/api/pay/refund_details/%s", config.ConfigData.Server.Domain, enID)
currentTime := time.Now().Format("2006年1月2日 15:04:05")
content := fmt.Sprintf(
`<font color="warning">【全能查】</font>%s。
>接口:<font color="comment">%s</font>
>用户ID:<font color="comment">%d</font>
>订单ID:<font color="comment">%d</font>
>时间:<font color="comment">%s</font>
[点击跳转到退款页面](%s)`,
message, interfa, userID, orderID, currentTime, url,
)
var webhookURL = fmt.Sprintf("%s?key=%s", config.ConfigData.Robot.Addr, config.ConfigData.Robot.Webhook)
markdownMessage := MarkdownMessage{
MsgType: "markdown",
Markdown: Markdown{
Content: content,
},
}
jsonData, err := json.Marshal(markdownMessage)
if err != nil {
log.Printf("机器人通知 Marshal 错误:%s", err.Error())
return
}
req, err := http.NewRequest("POST", webhookURL, bytes.NewBuffer(jsonData))
if err != nil {
log.Printf("机器人通知 NewRequest 错误:%s", err.Error())
return
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Printf("机器人通知 请求 错误:%s", err.Error())
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Printf("机器人通知 响应 错误:%s", resp.Status)
return
}
}
func (n *Notify) SendComplaintNotification(message string, complaintType string, platform string, complaintID string) {
currentTime := time.Now().Format("2006年1月2日 15:04:05")
content := fmt.Sprintf(
`<font color="warning">【全能查】</font>用户投诉
>投诉单号:<font color="comment">%s</font>
>投诉状态:<font color="comment">%s</font>
>投诉内容:<font color="comment">%s</font>
>投诉商户号平台:<font color="comment">%s</font>
>时间:<font color="comment">%s</font>`,
complaintID, complaintType, message, platform, currentTime,
)
var webhookURL = fmt.Sprintf("%s?key=%s", config.ConfigData.Robot.Addr, config.ConfigData.Robot.Webhook)
markdownMessage := MarkdownMessage{
MsgType: "markdown",
Markdown: Markdown{
Content: content,
},
}
jsonData, err := json.Marshal(markdownMessage)
if err != nil {
log.Printf("机器人通知 Marshal 错误:%s", err.Error())
return
}
req, err := http.NewRequest("POST", webhookURL, bytes.NewBuffer(jsonData))
if err != nil {
log.Printf("机器人通知 NewRequest 错误:%s", err.Error())
return
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Printf("机器人通知 请求 错误:%s", err.Error())
return
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
log.Printf("机器人通知 响应 错误:%s", resp.Status)
return
}
}

236
service/order.go Normal file
View File

@@ -0,0 +1,236 @@
package service
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/smartwalle/alipay/v3"
wxcore "github.com/wechatpay-apiv3/wechatpay-go/core"
"github.com/wechatpay-apiv3/wechatpay-go/services/payments/h5"
"github.com/wechatpay-apiv3/wechatpay-go/services/payments/jsapi"
"github.com/wechatpay-apiv3/wechatpay-go/services/refunddomestic"
"log"
"net/http"
"qnc-server/config"
"qnc-server/db"
"qnc-server/global"
"qnc-server/model/model"
"qnc-server/utils"
)
type OrderService struct {
}
func (a *OrderService) QueryConsumedOrder(userid uint, productEn string) (payOrder model.PayOrder, err error) {
err = db.DB.Joins("JOIN product ON product.id = pay_order.product_id").Where("pay_order.userid = ? AND pay_order.pay_status = ? AND pay_order.has_consumed = ? AND product.product_en = ?", userid, model.PayStatusSuccess, model.NotConsumed, productEn).First(&payOrder).Error
return
}
// 付款消费
func (a *OrderService) OrderConsumed(payOrder model.PayOrder) {
payOrder.HasConsumed = 1
err := db.DB.Save(&payOrder).Error
if err != nil {
log.Printf("消费订单失败:%v", err)
}
}
// 失效并退款
func (a *OrderService) OrderFailure(payOrder model.PayOrder) {
payOrder.HasConsumed = 2
err := db.DB.Save(&payOrder).Error
if err != nil {
log.Printf("失效订单失败:%v", err)
}
}
func (a *OrderService) GetList(pageSize int, pageNum int, userId uint) (list []model.PayOrder, err error) {
offset := (pageNum - 1) * pageSize
result := db.DB.Preload("Product").Where("userid = ? AND pay_status IN ?", userId, []string{model.PayStatusSuccess, model.PayStatusRefund, model.PayStatusUnderRefund}).Order("id desc").Limit(pageSize).Offset(offset).Find(&list)
if result.Error != nil {
return list, result.Error
}
return list, nil
}
func (a *OrderService) GetOrderByid(id uint) (order *model.PayOrder, err error) {
order = &model.PayOrder{}
err = db.DB.Preload("Product").Where("id = ?", id).First(order).Error
if err != nil {
return
}
return
}
// 微信小程序或公众号
func (a *OrderService) WechatJSAPIPrepay(appid string, mchID string, product model.Product, outTradeNo string, amount int64, openid string, platform string, client *wxcore.Client) (resp *jsapi.PrepayWithRequestPaymentResponse, err error) {
prepayReq := jsapi.PrepayRequest{
Appid: &appid,
Mchid: &mchID,
Description: wxcore.String(product.ProductName),
OutTradeNo: wxcore.String(outTradeNo), // 唯一的订单号
Attach: wxcore.String(product.Description),
NotifyUrl: wxcore.String(fmt.Sprintf("%s/%s", config.ConfigData.WxPay.NotifyURL, platform)), // 回调地址,需替换为实际地址
Amount: &jsapi.Amount{
Total: wxcore.Int64(amount), // 订单金额,单位为分
},
Payer: &jsapi.Payer{
Openid: wxcore.String(openid), // 用户的 OpenID通过前端传入
}}
// 发起预支付请求
svc := jsapi.JsapiApiService{Client: client}
resp, _, err = svc.PrepayWithRequestPayment(ctx, prepayReq)
if err != nil {
return nil, err
}
return resp, nil
}
// 浏览器H5
func (a *OrderService) WechatH5Prepay(appid string, mchID string, product model.Product, outTradeNo string, amount int64, userIP string, client *wxcore.Client) (resp *h5.PrepayResponse, err error) {
prepayReq := h5.PrepayRequest{
Appid: wxcore.String(appid),
Mchid: wxcore.String(mchID),
Description: wxcore.String(product.ProductName),
OutTradeNo: wxcore.String(outTradeNo), // 唯一的订单号
Attach: wxcore.String(product.Description),
NotifyUrl: wxcore.String(config.ConfigData.WxPay.NotifyURL), // 回调地址,需替换为实际地址
Amount: &h5.Amount{
Total: wxcore.Int64(amount), // 订单金额,单位为分
},
SceneInfo: &h5.SceneInfo{
PayerClientIp: wxcore.String(userIP), // 可通过前端传入
H5Info: &h5.H5Info{
Type: wxcore.String("Wap"),
//AppName: wxcore.String("全能查"),
//AppUrl: wxcore.String("https://你的域名"),
},
},
}
// 发起预支付请求
svc := h5.H5ApiService{Client: client}
resp, _, err = svc.Prepay(context.Background(), prepayReq)
if err != nil {
return nil, err
}
return resp, nil
}
func (a *OrderService) WeChatRefund(order model.PayOrder) (err error) {
var client *wxcore.Client
switch order.Platform {
case model.PlatformMPWEIXIN:
client = global.GlobalData.PayClient
case model.PlatformMPH5:
client = global.GlobalData.PayH5Client
}
outRefundNo := utils.GenerateOrderRefundNumber()
svc := refunddomestic.RefundsApiService{Client: client}
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)
return errors.New(fmt.Sprintf("微信订单申请退款错误:%s", err))
} 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)
return errors.New(fmt.Sprintf("微信订单退状态修改失败 underRefund Error:%v", err))
}
return nil
}
}
// 创建投诉回调URL
func (a *OrderService) RegisterComplaintNotificationURL(client *wxcore.Client, platform string) {
url := "https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications"
// 请求体数据
requestBody := map[string]string{
"url": fmt.Sprintf("%s/%s/pay/complaint_callback/%s", config.ConfigData.Server.Domain, config.ConfigData.Server.Prefix, platform), // 你的回调URL
}
// 将请求体序列化为 JSON
requestBodyJson, err := json.Marshal(requestBody)
if err != nil {
log.Fatalf("【%s创建投诉回调URL】请求体JSON序列化失败: %v", platform, err)
}
// 发送POST请求
result, err := client.Post(context.Background(), url, requestBodyJson)
if err != nil {
// 尝试解析响应内容,获取详细错误信息
var response map[string]interface{}
if err := json.NewDecoder(result.Response.Body).Decode(&response); err != nil {
log.Fatalf("【%s创建投诉回调URL】解析错误响应失败: %v", platform, err)
}
// 检查错误代码和信息
if responseCode, ok := response["code"].(string); ok && responseCode == "PARAM_ERROR" {
if responseMessage, ok := response["message"].(string); ok && responseMessage == "数据已存在" {
log.Printf("【%s创建投诉回调URL】数据已存在不需要重新创建: %+v\n", platform, response)
return
}
}
log.Fatalf("【%s创建投诉回调URL】请求失败: %v", platform, err)
}
// 处理响应
defer result.Response.Body.Close()
// 检查请求状态
if result.Response.StatusCode != http.StatusOK {
log.Fatalf("【%s创建投诉回调URL】请求失败状态码: %v", platform, result.Response.StatusCode)
}
// 解析响应
var response map[string]interface{}
if err := json.NewDecoder(result.Response.Body).Decode(&response); err != nil {
log.Fatalf("【%s创建投诉回调URL】解析响应失败: %v", platform, err)
}
log.Printf("【%s创建投诉回调URL】投诉通知回调URL创建成功: %+v\n", platform, response)
return
}
func (a *OrderService) AliRefund(ctx context.Context, payOrder model.PayOrder) error {
// 创建退款请求参数
refund := alipay.TradeRefund{}
refund.OutTradeNo = payOrder.OutTradeNo
refund.RefundAmount = utils.ConvertCentsToYuan(int(payOrder.Amount)) // 退款金额,单位元
refund.OutRequestNo = fmt.Sprintf("ali_refund_%s", utils.GenerateOrderNumber()) // 生成的唯一退款请求号
// 发起退款请求
refundResp, err := global.GlobalData.AliPayClient.TradeRefund(ctx, refund)
if err != nil {
log.Printf("【阿里支付退款】退款请求错误:%v", err)
return fmt.Errorf("refund request failed")
}
if refundResp.IsSuccess() {
// 更新订单状态
err = db.DB.Model(&model.PayOrder{}).Where("out_trade_no = ?", payOrder.OutTradeNo).Updates(model.PayOrder{
PayStatus: model.PayStatusRefund,
}).Error
if err != nil {
log.Printf("【阿里支付退款】订单更新错误:%v", err)
return fmt.Errorf("order update failed")
}
return nil
} else {
log.Printf("【阿里支付退款】退款失败:%v", refundResp.SubMsg)
return fmt.Errorf("refund failed: %s", refundResp.SubMsg)
}
}

17
service/product.go Normal file
View File

@@ -0,0 +1,17 @@
package service
import (
"log"
"qnc-server/db"
"qnc-server/model/model"
)
type ProductService struct{}
func (p *ProductService) GetProduct(productName string) (product model.Product, err error) {
err = db.DB.Where("BINARY product_en = ?", productName).First(&product).Error
if err != nil {
log.Println(err.Error())
}
return
}

35
service/render.go Normal file
View File

@@ -0,0 +1,35 @@
package service
import (
"qnc-server/db"
"qnc-server/model/model"
)
type RenderService struct {
}
func (r *RenderService) GetIndex() (renderList []model.Render, err error) {
err = db.DB.Find(&renderList).Error
return
}
func (r *RenderService) GetCarIndex() (renderList []model.CarRender, err error) {
err = db.DB.Find(&renderList).Error
return
}
func (r *RenderService) GetVerifyIndex() (renderList []model.VerifyRender, err error) {
err = db.DB.Find(&renderList).Error
return
}
func (r *RenderService) GetNotificationList() (Notification []model.GlobalNotification, err error) {
if err := db.DB.Find(&Notification).Error; err != nil {
return nil, err
}
return
}
func (r *RenderService) GetShadow() (shadow model.RenderShadow, err error) {
err = db.DB.First(&shadow).Error
if err != nil {
return
}
return
}

227
service/request.go Normal file
View File

@@ -0,0 +1,227 @@
package service
import (
"bytes"
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net/http"
"net/url"
"qnc-server/config"
"qnc-server/model/model"
"qnc-server/model/response"
"qnc-server/utils"
"strconv"
"strings"
"time"
)
type RequestService struct {
}
func (r *RequestService) AliYunRequest(method string, urlStr string, params map[string]string, body map[string]any) (data string, err error) {
appCode := config.ConfigData.LawsuitQuery.AppCode
// 创建查询参数
// 构造 URL 参数
if method == http.MethodGet && len(params) > 0 {
queryParams := url.Values{}
for key, value := range params {
queryParams.Add(key, value)
}
urlStr = fmt.Sprintf("%s?%s", urlStr, queryParams.Encode())
}
req, err := http.NewRequest(string(method), urlStr, nil)
if err != nil {
log.Printf("【阿里云市场请求】创建请求失败 err:%v", err)
return "", err
}
// 设置请求头
req.Header.Set("Authorization", "APPCODE "+appCode)
if method == http.MethodPost && body != nil {
formData := url.Values{}
for key, value := range body {
formData.Set(key, fmt.Sprintf("%v", value))
}
req.Body = io.NopCloser(strings.NewReader(formData.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
}
// 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Printf("【阿里云市场请求】请求失败,\nURL%s \nerr%v", req.URL, err)
return
}
defer resp.Body.Close()
// 检查响应状态码
if resp.StatusCode != http.StatusOK {
log.Printf("请求失败,状态码: %d", resp.StatusCode)
return "", fmt.Errorf("【阿里云市场请求】请求失败,\nURL%s \n状态码: %d", req.URL, resp.StatusCode)
}
// 读取响应体
respBody, err := io.ReadAll(resp.Body)
if err != nil {
log.Printf("【阿里云市场请求】响应体读取失败,\nURL%s \n响应%s \nerr%v", req.URL, resp.Body, err)
return
}
return string(respBody), nil
}
// 羽山请求
func (r *RequestService) YuShanRequest(prodID string, reqData map[string]interface{}) (plainText []byte, err error) {
currentTime := time.Now()
unixMilliseconds := currentTime.UnixNano() / int64(time.Millisecond)
requestSN, _ := utils.GenerateRandomString()
apiKey := config.ConfigData.Antifraud.ApiKey
acctId := config.ConfigData.Antifraud.AcctId
httpUrl := config.ConfigData.Antifraud.HttpUrl
// 根据 ProdID 动态构建请求数据
reqBody := map[string]interface{}{
"prod_id": prodID,
"req_time": unixMilliseconds,
"request_sn": requestSN,
"req_data": reqData,
}
messageBytes, err := json.Marshal(reqBody)
if err != nil {
return
}
key, err := hex.DecodeString(apiKey)
if err != nil {
return
}
//加密
cipherText := utils.AES_CBC_Encrypt(messageBytes, key)
content := base64.StdEncoding.EncodeToString(cipherText)
respStr, err := httpDo(httpUrl, content, acctId)
if err != nil {
return
}
//解密
sDec, err := base64.StdEncoding.DecodeString(respStr)
if err != nil {
if utils.IsJSON(respStr) {
return []byte(respStr), nil
}
return
}
plainText = utils.AES_CBC_Decrypt(sDec, key)
return
}
// 解析羽山响应数据
func (r *RequestService) AnalysisDataYuShan(respByte []byte) (data []map[string]interface{}, err error) {
var resp model.YuShanResponse[[]map[string]interface{}]
err = json.Unmarshal(respByte, &resp)
if err != nil {
return nil, utils.ErrSystemError
}
if resp.Retcode == RetCodeSuccess {
data = resp.Retdata
return data, nil
} else if resp.Retcode == RetCodeEmpty {
return data, utils.ErrNoRecord
} else {
return data, errors.New(resp.Retmsg)
}
}
// 三要素解析
func (r *RequestService) ThreeElementsParse(threeElementsResp string) (resp response.ThreeElementsResp, err error) {
err = json.Unmarshal([]byte(threeElementsResp), &resp)
if err != nil {
log.Printf("【三要素响应解析】解析错误 err:%v", err)
return
}
return resp, nil
}
// westdex 西部数据API请求
func (r *RequestService) WestDexRequest(code string, reqData map[string]interface{}) (respStr string, err error) {
// 生成当前的13位时间戳
timestamp := strconv.FormatInt(time.Now().UnixNano()/int64(time.Millisecond), 10)
// 构造请求URL
secretId := "449159"
reqUrl := fmt.Sprintf("https://apimaster.westdex.com.cn/api/invoke/%s/%s?timestamp=%s", secretId, code, timestamp)
// 将请求参数编码为JSON格式
data := map[string]interface{}{
"data": reqData,
}
jsonData, err := json.Marshal(data)
if err != nil {
log.Printf("【西部数据请求】JSON编码错误: %v", err)
return
}
// 创建HTTP POST请求
req, err := http.NewRequest("POST", reqUrl, bytes.NewBuffer(jsonData))
if err != nil {
log.Printf("【西部数据请求】创建请求错误: %v", err)
return
}
// 设置请求头
req.Header.Set("Content-Type", "application/json")
// 发送请求
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Printf("【西部数据请求】发送请求错误: %v", err)
return
}
defer resp.Body.Close()
// 检查请求是否成功
if resp.StatusCode == 200 {
// 读取响应体
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
log.Printf("【西部数据请求】读取响应体错误: %v", err)
return "", err
}
// 手动调用 json.Unmarshal 触发自定义的 UnmarshalJSON 方法
var westDexResp response.WestdexResp
err = json.Unmarshal(bodyBytes, &westDexResp)
if err != nil {
log.Printf("【西部数据请求】JSON反序列化错误: %v", err)
return "", err
}
key := "121a1e41fc1690dd6b90afbcacd80cf4" // 你的AES密钥
if westDexResp.Code != "00000" {
//decryptedData, err := utils.WestDexDecrypt(westDexResp.Data, key)
if err != nil {
log.Printf("【西部数据请求】响应数据解密错误: %v", err)
return "", err
}
log.Printf("【西部数据请求】响应数据业务失败:%v", westDexResp.Message)
return "", errors.New("业务失败")
}
// 解密响应数据
decryptedData, err := utils.WestDexDecrypt(westDexResp.Data, key)
if err != nil {
log.Printf("【西部数据请求】响应数据解密错误: %v", err)
return "", err
}
// 输出解密后的数据
log.Printf("【西部数据请求】解密后的响应数据: %s", decryptedData)
return decryptedData, nil
}
log.Printf("【西部数据请求】请求失败,状态码: %s", resp.Status)
return
}

139
service/singleQuery.go Normal file
View File

@@ -0,0 +1,139 @@
package service
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"log"
"qnc-server/config"
"qnc-server/db"
"qnc-server/model/model"
"qnc-server/utils"
"time"
)
type SingleQueryService struct {
}
func (s *SingleQueryService) EvaluateMarriageReqYuShan(data *model.EvaluateMarriagePayload) (plainText []byte, err error) {
currentTime := time.Now()
unixMilliseconds := currentTime.UnixNano() / int64(time.Millisecond)
requestSN, _ := utils.GenerateRandomString()
apiKey := config.ConfigData.Antifraud.ApiKey
acctId := config.ConfigData.Antifraud.AcctId
httpUrl := config.ConfigData.Antifraud.HttpUrl
// 根据 ProdID 动态构建请求数据
reqData := map[string]interface{}{
"prod_id": data.ProdID,
"req_time": unixMilliseconds,
"request_sn": requestSN,
"req_data": map[string]interface{}{},
}
for _, param := range model.SingleQueryProdIDParams[data.ProdID] {
switch param {
case "cardNo":
reqData["req_data"].(map[string]interface{})["cardNo"] = data.CardNo
case "name":
reqData["req_data"].(map[string]interface{})["name"] = data.Name
case "pageNo":
reqData["req_data"].(map[string]interface{})["pageNo"] = 1
case "pageSize":
reqData["req_data"].(map[string]interface{})["pageSize"] = 100
}
}
messageBytes, err := json.Marshal(reqData)
if err != nil {
return
}
key, err := hex.DecodeString(apiKey)
if err != nil {
return
}
//加密
cipherText := utils.AES_CBC_Encrypt(messageBytes, key)
content := base64.StdEncoding.EncodeToString(cipherText)
respStr, err := httpDo(httpUrl, content, acctId)
if err != nil {
return
}
//解密
sDec, err := base64.StdEncoding.DecodeString(respStr)
if err != nil {
if utils.IsJSON(respStr) {
return []byte(respStr), nil
}
return
}
plainText = utils.AES_CBC_Decrypt(sDec, key)
return
}
// 对返回信息进行处理,返回一份完整的报告
func (a *SingleQueryService) DataAnalysis(respString string) (data map[string]interface{}, err error) {
var resp model.YuShanResponse[map[string]interface{}]
err = json.Unmarshal([]byte(respString), &resp)
if err != nil {
log.Printf("singleQuery Unmarshal error%v", err)
return nil, utils.ErrSystemError
}
if resp.Retcode == RetCodeSuccess {
data = resp.Retdata
return data, nil
} else if resp.Retcode == RetCodeEmpty {
log.Printf("singleQuery resp empty%s", resp.Retmsg)
return data, utils.ErrNoRecord
} else {
log.Printf("singleQuery resp error%s", resp.Retmsg)
return data, utils.ErrSystemError
}
}
// 对返回信息进行处理,返回一份完整的报告
func (a *SingleQueryService) MarryDataAnalysis(respString string) (data []map[string]interface{}, err error) {
var resp model.YuShanResponse[[]map[string]interface{}]
err = json.Unmarshal([]byte(respString), &resp)
if err != nil {
log.Printf("singleQuery Unmarshal error%v", err)
return nil, utils.ErrSystemError
}
if resp.Retcode == RetCodeSuccess {
data = resp.Retdata
return data, nil
} else if resp.Retcode == RetCodeEmpty {
log.Printf("singleQuery resp empty%s", resp.Retmsg)
return data, utils.ErrNoRecord
} else {
log.Printf("singleQuery resp error%s", resp.Retmsg)
return data, utils.ErrSystemError
}
}
func (a *SingleQueryService) MarryAnalysis(jsonStr string) (resp *model.MerryResponse, err error) {
err = json.Unmarshal([]byte(jsonStr), &resp)
if err != nil {
fmt.Printf("【婚姻结果解析错误】JSON解析错误: %v\n", err)
return
}
// 根据状态码分类处理
switch resp.Code {
case 201, 202, 203:
return resp, nil
case 300:
return nil, utils.ErrNoRecord
default:
return nil, errors.New(resp.Msg)
}
}
func (a *SingleQueryService) QueryEvaluateMarriageOrder(orderID uint) (m model.EvaluateMarriage, err error) {
err = db.DB.Where("order_id = ?", orderID).First(&m).Error
return
}
// 获取婚姻评估记录
func (a *SingleQueryService) QueryEvaluateMarriageRecord(id uint) (m model.EvaluateMarriage, err error) {
err = db.DB.Where("id = ?", id).First(&m).Error
return
}

361
service/user.go Normal file
View File

@@ -0,0 +1,361 @@
package service
import (
"context"
"crypto/sha1"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"github.com/redis/go-redis/v9"
"io"
"log"
"net/http"
"net/url"
"qnc-server/config"
"qnc-server/db"
"qnc-server/model/model"
"qnc-server/model/response"
"sort"
"sync"
"time"
)
type UserService struct {
}
// 全能查小程序Code获取openid Unionid
func (UserService *UserService) RequestWx(code string) (wxResponse response.WxLoginResp, err error) {
// 向微信发出登录请求
baseURL := config.ConfigData.System.WxLoginUrl
// 创建查询参数
params := url.Values{}
params.Add("appid", config.ConfigData.System.WxAppId)
params.Add("secret", config.ConfigData.System.WxSecret)
params.Add("js_code", code)
params.Add("grant_type", "authorization_code")
// 构建完整的请求 URL
requestURL := fmt.Sprintf("%s?%s", baseURL, params.Encode())
// 发送 GET 请求
resp, err := http.Get(requestURL)
if err != nil {
return
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
return
}
// 将响应体解析为结构体
err = json.Unmarshal(body, &wxResponse)
if err != nil {
return
}
if wxResponse.ErrCode != 0 {
err = errors.New(wxResponse.ErrMsg)
return
}
return
}
// Login 全能查 公众号 获取access_token
func (UserService *UserService) RequestWxH5(code string) (wxH5Response response.WxH5LoginResp, err error) {
// 向微信发出登录请求
baseURL := config.ConfigData.System.WxH5LoginUrl
// 创建查询参数
params := url.Values{}
params.Add("appid", config.ConfigData.System.WxH5AppId)
params.Add("secret", config.ConfigData.System.WxH5Secret)
params.Add("code", code)
params.Add("grant_type", "authorization_code")
// 构建完整的请求 URL
requestURL := fmt.Sprintf("%s?%s", baseURL, params.Encode())
// 发送 GET 请求
resp, err := http.Get(requestURL)
if err != nil {
return
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
return
}
err = json.Unmarshal(body, &wxH5Response)
if err != nil {
return
}
log.Printf("H5-以Code获取access_token响应%v", wxH5Response)
if wxH5Response.Errcode != 0 {
err = errors.New(wxH5Response.Errmsg)
return
}
return
}
// 刷新token
// access_token 2小时过期
// refresh_token 30天过期
//func (UserService *UserService) RefreshToken(openid string) (err error) {
//
//}
// snsapi_userinfo授权拉取用户信息
func (UserService *UserService) GetSnsUserInfo(accessToken string, openid string) (userinfoResp response.WeChatUserInfoResp, err error) {
//https://api.weixin.qq.com/sns/userinfo?access_token=ACCESS_TOKEN&openid=OPENID&lang=zh_CN
// 向微信发出登录请求
baseURL := config.ConfigData.System.WxH5UserinfoUrl
// 创建查询参数
params := url.Values{}
params.Add("access_token", accessToken)
params.Add("openid", openid)
params.Add("lang", "zh_CN")
// 构建完整的请求 URL
requestURL := fmt.Sprintf("%s?%s", baseURL, params.Encode())
log.Printf("请求url: %s", requestURL)
// 发送 GET 请求
resp, err := http.Get(requestURL)
if err != nil {
return
}
defer resp.Body.Close()
// 读取响应体
body, err := io.ReadAll(resp.Body)
if err != nil {
return
}
err = json.Unmarshal(body, &userinfoResp)
if err != nil {
return
}
log.Printf("H5-snsapi_userinfo授权拉取用户信息响应%v", userinfoResp)
if userinfoResp.Errcode != 0 {
err = errors.New(userinfoResp.Errmsg)
return
}
return
}
// 刷新token
// access_token 2小时过期
// refresh_token 30天过期
//func (UserService *UserService) RefreshToken(openid string) (err error) {
//
//}
// 注册用户,创建
func (UserService *UserService) Register(user model.User, authType model.AuthType, authIdentifier model.AuthIdentifier) (auth model.Authentication, err error) {
err = db.DB.Create(&user).Error
if err != nil {
return
}
auth = model.Authentication{
UserID: user.ID,
AuthType: authType,
OpenID: authIdentifier.OpenID,
UnionID: authIdentifier.UnionID,
Phone: authIdentifier.Phone,
}
err = db.DB.Create(&auth).Error
if err != nil {
return
}
return
}
// MatchingAuthentications配对是否有相交的校验
func (UserService *UserService) MatchingAuthentications(authIdentifier model.AuthIdentifier) (authentications []model.Authentication, err error) {
query := db.DB
if authIdentifier.OpenID != "" {
query = query.Or("open_id = ?", authIdentifier.OpenID)
}
if authIdentifier.UnionID != "" {
query = query.Or("union_id = ?", authIdentifier.UnionID)
}
if authIdentifier.Phone != "" {
query = query.Or("phone = ?", authIdentifier.Phone)
}
err = query.Find(&authentications).Error
return
}
// CreateAuthentications 创建校验
func (UserService *UserService) CreateAuthentications(userid uint, authIdentifier model.AuthIdentifier, AuthType model.AuthType) (authentications model.Authentication, err error) {
authentications = model.Authentication{
UserID: userid,
AuthType: AuthType,
OpenID: authIdentifier.OpenID,
UnionID: authIdentifier.UnionID,
Phone: authIdentifier.Phone,
}
err = db.DB.Create(&authentications).Error
if err != nil {
return
}
return
}
// GetUser 根据openid获取全能查用户记录
func (UserService *UserService) GetUser(id uint) (user *model.User, err error) {
err = db.DB.Where("id = ?", id).First(&user).Error
return
}
// GetUser 根据h5_openid获取全能查用户记录
func (UserService *UserService) GetUserByH5(h5Openid string) (user *model.User, err error) {
err = db.DB.Where("h5_openid = ?", h5Openid).First(&user).Error
return
}
// GetUser 根据unionid获取全能查用户记录
func (UserService *UserService) GetUserByUnionid(unionid string) (user *model.User, err error) {
err = db.DB.Where("unionid = ?", unionid).First(&user).Error
return
}
// GetUser 根据userid获取全能查用户记录
func (UserService *UserService) GetUserByUserid(userid uint) (user *model.User, err error) {
err = db.DB.Where("userid = ?", userid).First(&user).Error
return
}
// CreateUser 创建全能查用户
func (UserService *UserService) CreateUser(user *model.User) (err error) {
err = db.DB.Create(&user).Error
return err
}
// update 按unionid更新用户信息
func (UserService *UserService) UpdateUser(user *model.User) (err error) {
err = db.DB.Save(user).Error
return err
}
// h5 SDK 获取config部分
var (
mutex sync.Mutex
)
var ctx = context.Background()
// GetJsapiTicket 获取JSAPI Ticket
func (UserService *UserService) GetJsapiTicket() (string, error) {
jsapiTicket, err := db.RedisClient.Get(ctx, "JsapiTicket").Result()
if err != nil && !errors.Is(err, redis.Nil) {
return "", err
}
if jsapiTicket != "" {
return jsapiTicket, nil
}
mutex.Lock()
defer mutex.Unlock()
accessToken, err := UserService.getAccessToken()
if err != nil {
return "", err
}
ticket, err := UserService.fetchJsapiTicket(accessToken)
if err != nil {
return "", err
}
log.Printf("获取到新的JsapiTicket%s", ticket)
jsapiTicket = ticket
err = db.RedisClient.Set(ctx, "JsapiTicket", jsapiTicket, 7200*time.Second).Err()
if err != nil {
return "", err
}
return jsapiTicket, nil
}
func (UserService *UserService) getAccessToken() (string, error) {
reqUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", config.ConfigData.System.WxH5AppId, config.ConfigData.System.WxH5Secret)
resp, err := http.Get(reqUrl)
if err != nil {
return "", err
}
defer resp.Body.Close()
var result struct {
AccessToken string `json:"access_token"`
ExpiresIn int `json:"expires_in"`
Errcode int `json:"errcode"`
Errmsg string `json:"errmsg"`
}
err = json.NewDecoder(resp.Body).Decode(&result)
if err != nil {
return "", err
}
if result.Errcode != 0 {
log.Printf("获取AccessToken错误%s", result.Errmsg)
return "", errors.New(result.Errmsg)
}
return result.AccessToken, nil
}
func (UserService *UserService) fetchJsapiTicket(accessToken string) (string, error) {
reqUrl := fmt.Sprintf("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi", accessToken)
resp, err := http.Get(reqUrl)
if err != nil {
return "", err
}
defer resp.Body.Close()
var result struct {
Ticket string `json:"ticket"`
ExpiresIn int `json:"expires_in"`
Errcode int `json:"errcode"`
Errmsg string `json:"errmsg"`
}
err = json.NewDecoder(resp.Body).Decode(&result)
if err != nil {
return "", err
}
if result.Errcode != 0 {
log.Printf("获取JsapiTicket错误%s", result.Errmsg)
return "", errors.New(result.Errmsg)
}
return result.Ticket, nil
}
// CreateSignature 创建签名
func (UserService *UserService) CreateSignature(ticket, nonceStr, timestamp, url string) string {
// 将参数按 key=value 格式拼接
params := map[string]string{
"jsapi_ticket": ticket,
"noncestr": nonceStr,
"timestamp": timestamp,
"url": url,
}
// 按照 key 的字典序排序
keys := make([]string, 0, len(params))
for key := range params {
keys = append(keys, key)
}
sort.Strings(keys)
// 拼接成一个字符串
str := ""
for _, key := range keys {
if str != "" {
str += "&"
}
str += fmt.Sprintf("%s=%s", key, params[key])
}
// 对字符串进行 SHA1 加密
h := sha1.New()
h.Write([]byte(str))
return hex.EncodeToString(h.Sum(nil))
}

177
service/verify.go Normal file
View File

@@ -0,0 +1,177 @@
package service
import (
"encoding/base64"
"encoding/hex"
"encoding/json"
"errors"
"log"
"qnc-server/config"
"qnc-server/db"
"qnc-server/model/model"
"qnc-server/utils"
"time"
)
type VerifyService struct {
}
func (c *VerifyService) VerifReqYuShan(prodID string, data *map[string]interface{}) (plainText []byte, err error) {
currentTime := time.Now()
unixMilliseconds := currentTime.UnixNano() / int64(time.Millisecond)
requestSN, _ := utils.GenerateRandomString()
apiKey := config.ConfigData.YuShan.ApiKey
acctId := config.ConfigData.YuShan.AcctId
httpUrl := config.ConfigData.YuShan.HttpUrl
// 根据 ProdID 动态构建请求数据
reqData := map[string]interface{}{
"prod_id": prodID,
"req_time": unixMilliseconds,
"request_sn": requestSN,
"req_data": data,
}
messageBytes, err := json.Marshal(reqData)
if err != nil {
return
}
key, err := hex.DecodeString(apiKey)
if err != nil {
return
}
//加密
cipherText := utils.AES_CBC_Encrypt(messageBytes, key)
content := base64.StdEncoding.EncodeToString(cipherText)
respStr, err := httpDo(httpUrl, content, acctId)
if err != nil {
return
}
//解密
sDec, err := base64.StdEncoding.DecodeString(respStr)
if err != nil {
if utils.IsJSON(respStr) {
return []byte(respStr), nil
}
return
}
plainText = utils.AES_CBC_Decrypt(sDec, key)
return
}
// 对返回信息进行处理,返回一份完整的报告
func (c *VerifyService) VerifyDataAnalysis(respString string) (data model.YuShanResponse[map[string]interface{}], err error) {
err = json.Unmarshal([]byte(respString), &data)
if err != nil {
log.Printf("VerifInsurance Unmarshal error%v", err)
return data, utils.ErrUnmashal
}
if data.Retcode == RetCodeSuccess {
return data, nil
} else if data.Retcode == RetCodeEmpty {
log.Printf("Verif resp empty%s", data.Retmsg)
return data, utils.ErrNoRecord
} else {
log.Printf("Verif resp error%s", data.Retmsg)
return data, utils.ErrSystemError
}
}
func (c *VerifyService) VerifyCertDataAnalysis(respString string) (data model.YuShanResponse[[]map[string]interface{}], err error) {
err = json.Unmarshal([]byte(respString), &data)
if err != nil {
log.Printf("VerifInsurance Unmarshal error%v", err)
return data, utils.ErrUnmashal
}
if data.Retcode == RetCodeSuccess {
return data, nil
} else if data.Retcode == RetCodeEmpty {
log.Printf("Verif resp empty%s", data.Retmsg)
return data, utils.ErrNoRecord
} else {
log.Printf("Verif resp error%s", data.Retmsg)
return data, utils.ErrSystemError
}
}
func (c *VerifyService) GetRecordByFeature(feature string, id uint, userID uint) (interface{}, error) {
var result interface{}
switch feature {
case "shoujiheyan":
var record model.VerifyPhoneName
if err := db.DB.Where("order_id = ? AND userid = ?", id, userID).First(&record).Error; err != nil {
return nil, err
}
result = record.Resp
case "shenfenzhengheyan":
var record model.VerifyCardName
if err := db.DB.Where("order_id = ? AND userid = ?", id, userID).First(&record).Error; err != nil {
return nil, err
}
result = record.Resp
case "yinhangkaheimingdan":
var record model.VerifyBankCard
if err := db.DB.Where("order_id = ? AND userid = ?", id, userID).First(&record).Error; err != nil {
return nil, err
}
analysis, err := c.VerifyDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
result = analysis.Retdata
case "jinengzigezhengshu":
var record model.VerifySkillCert
if err := db.DB.Where("order_id = ? AND userid = ?", id, userID).First(&record).Error; err != nil {
return nil, err
}
analysis, err := c.VerifyCertDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
result = analysis.Retdata
default:
return nil, errors.New("Invalid feature")
}
return result, nil
}
func (c *VerifyService) GetRecordCaseByFeature(feature string) (interface{}, error) {
var result interface{}
switch feature {
case "shoujiheyan":
var record model.VerifyPhoneName
if err := db.DB.Where("id = ?", 1).First(&record).Error; err != nil {
return nil, err
}
result = record.Resp
case "shenfenzhengheyan":
var record model.VerifyCardName
if err := db.DB.Where("id = ?", 1).First(&record).Error; err != nil {
return nil, err
}
result = record.Resp
case "yinhangkaheimingdan":
var record model.VerifyBankCard
if err := db.DB.Where("id = ?", 1).First(&record).Error; err != nil {
return nil, err
}
analysis, err := c.VerifyDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
result = analysis.Retdata
case "jinengzigezhengshu":
var record model.VerifySkillCert
if err := db.DB.Where("id = ?", 1).First(&record).Error; err != nil {
return nil, err
}
analysis, err := c.VerifyCertDataAnalysis(record.Resp)
if err != nil {
return nil, err
}
result = analysis.Retdata
default:
return nil, errors.New("Invalid feature")
}
return result, nil
}

85
service/verifyCode.go Normal file
View File

@@ -0,0 +1,85 @@
package service
import (
"encoding/json"
"errors"
"fmt"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
dysmsapi "github.com/alibabacloud-go/dysmsapi-20170525/v3/client"
util "github.com/alibabacloud-go/tea-utils/v2/service"
"github.com/alibabacloud-go/tea/tea"
config "qnc-server/config"
"strings"
)
type VerifyCode struct {
}
func (v *VerifyCode) CreateClient() (result *dysmsapi.Client, err error) {
clientConfig := &openapi.Config{
AccessKeyId: &config.ConfigData.VerifyCode.AccessKeyID,
AccessKeySecret: &config.ConfigData.VerifyCode.AccessKeySecret,
}
clientConfig.Endpoint = tea.String(config.ConfigData.VerifyCode.EndpointURL)
result, err = dysmsapi.NewClient(clientConfig)
return
}
func (v *VerifyCode) Send(code string, phoneNumber string, templateCode string) (err error) {
client, err := v.CreateClient()
if err != nil {
return err
}
sendSmsRequest := &dysmsapi.SendSmsRequest{
SignName: tea.String(config.ConfigData.VerifyCode.SignName),
TemplateCode: tea.String(templateCode),
PhoneNumbers: tea.String(phoneNumber),
TemplateParam: tea.String(fmt.Sprintf("{code:\"%s\"}", code)),
}
runtime := &util.RuntimeOptions{}
err = func() (_e error) {
defer func() {
if r := tea.Recover(recover()); r != nil {
_e = r
}
}()
resp, err := client.SendSmsWithOptions(sendSmsRequest, runtime)
if err != nil {
var error = &tea.SDKError{}
if _t, ok := err.(*tea.SDKError); ok {
error = _t
} else {
error.Message = tea.String(err.Error())
}
fmt.Println(tea.StringValue(error.Message))
// 诊断地址
var data interface{}
d := json.NewDecoder(strings.NewReader(tea.StringValue(error.Data)))
err := d.Decode(&data)
if err != nil {
return err
}
if m, ok := data.(map[string]interface{}); ok {
recommend, _ := m["Recommend"]
fmt.Println(recommend)
}
_, err = util.AssertAsString(error.Message)
if err != nil {
return err
}
}
if *resp.Body.Code != "OK" {
return errors.New(*resp.Body.Message)
}
return nil
}()
if err != nil {
return err
}
return nil
}