This commit is contained in:
Mrx
2026-01-27 15:08:00 +08:00
parent 33dd85fd2f
commit 1a44eab144
33 changed files with 999 additions and 3733 deletions

View File

@@ -99,6 +99,24 @@ func (l *AlipayCallbackLogic) handleQueryOrderPayment(w http.ResponseWriter, not
return nil
}
// 更新 query_user_record 表的 platform_order_id
queryUserRecords, findRecordErr := l.svcCtx.QueryUserRecordModel.FindAll(l.ctx,
l.svcCtx.QueryUserRecordModel.SelectBuilder().
Where("query_no = ?", notification.OutTradeNo).
Where("del_state = ?", 0).
Limit(1), "")
if findRecordErr == nil && len(queryUserRecords) > 0 {
record := queryUserRecords[0]
record.PlatformOrderId = lzUtils.StringToNullString(notification.TradeNo)
record.Version = record.Version + 1
if updateRecordErr := l.svcCtx.QueryUserRecordModel.UpdateWithVersion(l.ctx, nil, record); updateRecordErr != nil {
logx.Errorf("支付宝支付回调,更新查询用户记录失败: %+v", updateRecordErr)
// 更新失败不影响主流程,只记录日志
} else {
logx.Infof("支付宝支付回调更新查询用户记录成功query_no: %s, platform_order_id: %s", notification.OutTradeNo, notification.TradeNo)
}
}
if order.Status == "paid" {
if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil {
logx.Errorf("异步任务调度失败: %v", asyncErr)

View File

@@ -3,19 +3,23 @@ package pay
import (
"context"
"database/sql"
"encoding/hex"
"encoding/json"
"fmt"
"os"
"strconv"
"strings"
"time"
"qnc-server/app/main/api/internal/svc"
"qnc-server/app/main/api/internal/types"
"qnc-server/app/main/model"
"qnc-server/common/ctxdata"
"qnc-server/common/globalkey"
"qnc-server/common/xerr"
"qnc-server/pkg/lzkit/crypto"
"qnc-server/pkg/lzkit/lzUtils"
"strconv"
"strings"
"time"
"github.com/Masterminds/squirrel"
"github.com/google/uuid"
"github.com/pkg/errors"
"github.com/redis/go-redis/v9"
@@ -241,6 +245,16 @@ func (l *PaymentLogic) QueryOrderPayment(req *types.PaymentReq, session sqlx.Ses
return nil, errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "生成订单, 获取代理链接失败: %+v", findAgentLinkErr)
}
amount = agentLinkModel.SetPrice
// 检查被查询人身份证在72小时内的已支付订单次数
// 如果是代理渠道订单需要检查该身份证在72小时内已支付的订单是否超过2次
logx.Infof("生成订单, 检测到代理渠道订单,开始检查订单限制, AgentIdentifier: %s", data.AgentIdentifier)
checkErr := l.checkIdCardPaidOrdersIn72Hours(data.Params)
if checkErr != nil {
logx.Errorf("生成订单, 订单限制检查失败: %v", checkErr)
return nil, checkErr
}
logx.Infof("生成订单, 订单限制检查通过,允许创建订单")
} else {
amount = product.SellPrice
}
@@ -264,6 +278,24 @@ func (l *PaymentLogic) QueryOrderPayment(req *types.PaymentReq, session sqlx.Ses
}
orderID := order.Id
// 更新 query_user_record 表的 order_id通过 query_no 匹配)
queryUserRecords, findRecordErr := l.svcCtx.QueryUserRecordModel.FindAll(l.ctx,
l.svcCtx.QueryUserRecordModel.SelectBuilder().
Where("query_no = ?", outTradeNo).
Where("del_state = ?", 0).
Limit(1), "")
if findRecordErr == nil && len(queryUserRecords) > 0 {
record := queryUserRecords[0]
record.OrderId = lzUtils.StringToNullString(orderID)
record.Version = record.Version + 1
if updateRecordErr := l.svcCtx.QueryUserRecordModel.UpdateWithVersion(l.ctx, session, record); updateRecordErr != nil {
logx.Errorf("更新查询用户记录 order_id 失败: %+v", updateRecordErr)
// 更新失败不影响主流程,只记录日志
} else {
logx.Infof("更新查询用户记录成功query_no: %s, order_id: %s", outTradeNo, orderID)
}
}
// 如果是代理推广订单,创建完整的代理订单记录
if data.AgentIdentifier != "" && agentLinkModel != nil {
// 获取代理信息
@@ -473,3 +505,158 @@ func (l *PaymentLogic) getConfigFloat(configKey string) (float64, error) {
}
return value, nil
}
// checkIdCardPaidOrdersIn72Hours 检查被查询人身份证在72小时内的已支付订单次数
// 如果72小时内已支付订单大于2次则返回错误
// encryptedParams: 加密的参数字符串data.Params需要解密后获取身份证号
func (l *PaymentLogic) checkIdCardPaidOrdersIn72Hours(encryptedParams string) error {
logx.Infof("检查订单限制, 开始检查代理渠道订单限制, encryptedParams长度: %d", len(encryptedParams))
// 1. 解密参数获取身份证号
secretKey := l.svcCtx.Config.Encrypt.SecretKey
key, decodeErr := hex.DecodeString(secretKey)
if decodeErr != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "检查订单限制, 解析加密密钥失败 err: %v", decodeErr)
}
// 解密 data.Params加密的 JSON 字符串)
decryptData, aesDecryptErr := crypto.AesDecrypt(encryptedParams, key)
if aesDecryptErr != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "检查订单限制, 解密参数失败 err: %v", aesDecryptErr)
}
// 解析解密后的 JSON 获取参数
var params map[string]interface{}
if unmarshalErr := json.Unmarshal(decryptData, &params); unmarshalErr != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "检查订单限制, 解析参数失败 err: %v, decryptData: %s", unmarshalErr, string(decryptData))
}
idCard, ok := params["id_card"].(string)
if !ok || idCard == "" {
// 如果没有身份证号,跳过检查(可能是其他类型的查询)
logx.Infof("检查订单限制, 未找到身份证号,跳过检查")
return nil
}
logx.Infof("检查订单限制, 被查询人身份证号: %s", idCard)
// 2. 加密身份证号用于查询
logx.Infof("检查订单限制, 开始加密身份证号: %s", idCard)
encryptedIdCard, encryptErr := crypto.EncryptIDCard(idCard, key)
if encryptErr != nil {
logx.Errorf("检查订单限制, 加密身份证号失败: %v", encryptErr)
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "检查订单限制, 加密身份证号失败 err: %v", encryptErr)
}
logx.Infof("检查订单限制, 身份证号加密成功, encryptedIdCard长度: %d", len(encryptedIdCard))
// 3. 查询该身份证对应的所有查询记录agent_identifier IS NOT NULL
// 统计该身份证的记录数然后通过order_id关联订单表查询已支付的订单
encryptedIdCardPreview := encryptedIdCard
if len(encryptedIdCard) > 20 {
encryptedIdCardPreview = encryptedIdCard[:20] + "..."
}
logx.Infof("检查订单限制, 开始查询数据库")
logx.Infof("检查订单限制, 查询条件: id_card=%s, del_state=0, agent_identifier IS NOT NULL, agent_identifier != ''", encryptedIdCardPreview)
logx.Infof("检查订单限制, 注意不要求order_id必须存在先查询所有记录")
queryUserRecords, findRecordErr := l.svcCtx.QueryUserRecordModel.FindAll(l.ctx,
l.svcCtx.QueryUserRecordModel.SelectBuilder().
Where("id_card = ?", encryptedIdCard).
Where("del_state = ?", globalkey.DelStateNo).
Where("agent_identifier IS NOT NULL").
Where("agent_identifier != ''"),
"")
if findRecordErr != nil {
if errors.Is(findRecordErr, model.ErrNotFound) {
logx.Infof("检查订单限制, 查询结果: 未找到记录 (ErrNotFound)")
} else {
logx.Errorf("检查订单限制, 查询数据库失败: %v", findRecordErr)
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "检查订单限制, 查询用户记录失败 err: %v", findRecordErr)
}
} else {
logx.Infof("检查订单限制, 查询数据库成功")
}
logx.Infof("检查订单限制, 查询到 %d 条该身份证的查询记录agent_identifier IS NOT NULL", len(queryUserRecords))
// 统计记录详情
if len(queryUserRecords) > 0 {
logx.Infof("检查订单限制, ========== 记录详情 ==========")
for i, record := range queryUserRecords {
orderId := ""
hasOrderId := false
if record.OrderId.Valid && record.OrderId.String != "" {
orderId = record.OrderId.String
hasOrderId = true
}
agentId := ""
if record.AgentIdentifier.Valid {
agentId = record.AgentIdentifier.String
}
logx.Infof("检查订单限制, 记录[%d/%d]: order_id=%s (有order_id: %v), agent_identifier=%s, query_no=%s, create_time=%s, product=%s",
i+1, len(queryUserRecords), orderId, hasOrderId, agentId, record.QueryNo, record.CreateTime.Format("2006-01-02 15:04:05"), record.Product)
}
logx.Infof("检查订单限制, ========== 记录详情结束 ==========")
}
if len(queryUserRecords) == 0 {
// 没有历史订单记录,可以继续
logx.Infof("检查订单限制, 身份证 %s 无历史订单记录,允许支付", idCard)
return nil
}
// 4. 提取所有订单ID去重只提取有order_id的记录
logx.Infof("检查订单限制, 开始提取订单ID总记录数: %d", len(queryUserRecords))
orderIdSet := make(map[string]bool)
orderIds := make([]string, 0)
recordsWithOrderId := 0
for i, record := range queryUserRecords {
if record.OrderId.Valid && record.OrderId.String != "" {
recordsWithOrderId++
orderId := record.OrderId.String
if !orderIdSet[orderId] {
orderIdSet[orderId] = true
orderIds = append(orderIds, orderId)
logx.Infof("检查订单限制, 记录[%d]有order_id: %s", i+1, orderId)
} else {
logx.Infof("检查订单限制, 记录[%d]order_id重复: %s (已存在)", i+1, orderId)
}
} else {
logx.Infof("检查订单限制, 记录[%d]无order_id跳过", i+1)
}
}
logx.Infof("检查订单限制, 有order_id的记录数: %d, 提取到 %d 个唯一订单ID: %v", recordsWithOrderId, len(orderIds), orderIds)
if len(orderIds) == 0 {
logx.Infof("检查订单限制, 身份证 %s 无有效订单ID允许支付", idCard)
return nil
}
// 5. 查询72小时内已支付的订单数量只统计已支付状态的订单
// 计算72小时前的时间
seventyTwoHoursAgo := time.Now().Add(-72 * time.Hour)
logx.Infof("检查订单限制, 查询时间范围: %s 至今", seventyTwoHoursAgo.Format("2006-01-02 15:04:05"))
logx.Infof("检查订单限制, 查询条件: status='paid', pay_time IS NOT NULL, pay_time >= %s", seventyTwoHoursAgo.Format("2006-01-02 15:04:05"))
// 只统计已支付状态的订单status='paid'
paidOrderCount, countErr := l.svcCtx.OrderModel.FindCount(l.ctx,
l.svcCtx.OrderModel.SelectBuilder().
Where(squirrel.Eq{"id": orderIds}).
Where("status = ?", model.OrderStatusPaid). // 只查询已支付状态的订单
Where("pay_time IS NOT NULL"). // 必须有支付时间
Where("pay_time >= ?", seventyTwoHoursAgo). // 72小时内
Where("del_state = ?", globalkey.DelStateNo),
"id")
if countErr != nil {
return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "检查订单限制, 查询已支付订单数量失败 err: %v", countErr)
}
logx.Infof("检查订单限制, 身份证 %s 在72小时内已支付的订单数量: %d (限制: 已支付订单数量>2次则拒绝)", idCard, paidOrderCount)
// 6. 如果72小时内已支付订单数量大于2次则拒绝支付
if paidOrderCount > 2 {
return errors.Wrapf(xerr.NewErrMsg("该身份证在72小时内已支付订单超过2次无法进行代理渠道的报告查询支付"), "")
}
return nil
}

View File

@@ -97,6 +97,24 @@ func (l *WechatPayCallbackLogic) handleQueryOrderPayment(w http.ResponseWriter,
return nil
}
// 更新 query_user_record 表的 platform_order_id
queryUserRecords, findRecordErr := l.svcCtx.QueryUserRecordModel.FindAll(l.ctx,
l.svcCtx.QueryUserRecordModel.SelectBuilder().
Where("query_no = ?", *notification.OutTradeNo).
Where("del_state = ?", 0).
Limit(1), "")
if findRecordErr == nil && len(queryUserRecords) > 0 {
record := queryUserRecords[0]
record.PlatformOrderId = lzUtils.StringToNullString(*notification.TransactionId)
record.Version = record.Version + 1
if updateRecordErr := l.svcCtx.QueryUserRecordModel.UpdateWithVersion(l.ctx, nil, record); updateRecordErr != nil {
logx.Errorf("微信支付回调,更新查询用户记录失败: %+v", updateRecordErr)
// 更新失败不影响主流程,只记录日志
} else {
logx.Infof("微信支付回调更新查询用户记录成功query_no: %s, platform_order_id: %s", *notification.OutTradeNo, *notification.TransactionId)
}
}
if order.Status == "paid" {
if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil {
logx.Errorf("异步任务调度失败: %v", asyncErr)