多笔立减金,错误映射
This commit is contained in:
parent
9bd8646e57
commit
bbfc91da57
|
|
@ -209,6 +209,8 @@ message CmbNotifyRequest {
|
||||||
// 扩展字段
|
// 扩展字段
|
||||||
string ext = 13 [json_name = "ext"];
|
string ext = 13 [json_name = "ext"];
|
||||||
string attach = 14 [json_name = "attach"];
|
string attach = 14 [json_name = "attach"];
|
||||||
|
// 招行唯一流水号
|
||||||
|
string transactionId = 15 [json_name = "transactionId"];
|
||||||
}
|
}
|
||||||
|
|
||||||
message CmbNotifyReply {
|
message CmbNotifyReply {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
package businesserr
|
||||||
|
|
||||||
|
// ThirdErrCode 表示发券接口失败时返回的第三方业务错误码
|
||||||
|
// 格式为 tecxxx,用于统一抽象不同支付平台的错误
|
||||||
|
type ThirdErrCode string
|
||||||
|
|
||||||
|
// 定义具体的第三方错误码常量(微信/支付宝的都有)
|
||||||
|
const (
|
||||||
|
ThirdErrCodeDefault ThirdErrCode = "tec999" // 默认错误码
|
||||||
|
|
||||||
|
ThirdErrCodeStockNotEnough ThirdErrCode = "tec001" // 库存不足
|
||||||
|
ThirdErrCodeAdvanceFundingNotEnough ThirdErrCode = "tec002" // 垫资不足
|
||||||
|
ThirdErrCodeBatchNotStarted ThirdErrCode = "tec003" // 批次未开始
|
||||||
|
ThirdErrCodeBatchEnded ThirdErrCode = "tec004" // 批次已结束
|
||||||
|
ThirdErrCodeBatchOffline ThirdErrCode = "tec005" // 批次已下线
|
||||||
|
ThirdErrCodePaymentPlatformError ThirdErrCode = "tec006" // 微信/支付宝异常
|
||||||
|
|
||||||
|
ThirdErrCodeUserNotRealNameVerified ThirdErrCode = "tec007" // 用户没有实名认证
|
||||||
|
ThirdErrCodeUserNotFound ThirdErrCode = "tec008" // 用户账号不存在
|
||||||
|
ThirdErrCodeUserAccountFrozen ThirdErrCode = "tec009" // 用户账户被冻结
|
||||||
|
ThirdErrCodeUserHighRiskOrCheater ThirdErrCode = "tec010" // 用户为作弊用户或高风险用户
|
||||||
|
ThirdErrCodeUserNoMobileBound ThirdErrCode = "tec011" // 用户没有绑定手机号
|
||||||
|
ThirdErrCodeUserAccountAbnormal ThirdErrCode = "tec012" // 用户账号异常(没有明确的原因)
|
||||||
|
ThirdErrCodeUserParticipationExceeded ThirdErrCode = "tec013" // 用户参与次数超限
|
||||||
|
ThirdErrCodeAppIDOpenIDMismatch ThirdErrCode = "tec014" // appid与openid不匹配
|
||||||
|
|
||||||
|
ThirdErrCodeDailyLimit ThirdErrCode = "tec015" // 超批次当天限额
|
||||||
|
ThirdErrCodeCallHigh ThirdErrCode = "tec016" // 调用频率过高
|
||||||
|
)
|
||||||
|
|
||||||
|
// CmbAPIError 定义 API 错误结构体
|
||||||
|
type CmbAPIError struct {
|
||||||
|
StatusCode int `json:"status_code"`
|
||||||
|
ErrorCode ErrCode `json:"error_code"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Hint string `json:"hint"` // 解决方案
|
||||||
|
ThirdErrCode ThirdErrCode `json:"third_err_code"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var CmbAPIPublicErrorMap = map[ErrCode][]CmbAPIError{
|
||||||
|
|
||||||
|
BATCH_NOT_STARTED: {
|
||||||
|
{
|
||||||
|
StatusCode: 400,
|
||||||
|
ErrorCode: BATCH_NOT_STARTED,
|
||||||
|
Description: "批次未开始",
|
||||||
|
Hint: "批次未开始",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
BATCH_ENDED: {
|
||||||
|
{
|
||||||
|
StatusCode: 400,
|
||||||
|
ErrorCode: BATCH_ENDED,
|
||||||
|
Description: "批次已结束",
|
||||||
|
Hint: "批次已结束",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -1,19 +1,18 @@
|
||||||
package wechatrepoimpl
|
package businesserr
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/go-kratos/kratos/v2/errors"
|
"github.com/go-kratos/kratos/v2/errors"
|
||||||
|
"strings"
|
||||||
err2 "voucher/api/err"
|
err2 "voucher/api/err"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ErrCode 定义错误码类型
|
|
||||||
// https://pay.weixin.qq.com/doc/v3/merchant/4012463767
|
|
||||||
type ErrCode string
|
|
||||||
|
|
||||||
// 定义错误码常量
|
// 定义错误码常量
|
||||||
|
// https://pay.weixin.qq.com/doc/v3/merchant/4012463767
|
||||||
const (
|
const (
|
||||||
|
SYSTEM_ERROR ErrCode = "SYSTEM_ERROR"
|
||||||
|
SIGN_ERROR ErrCode = "SIGN_ERROR"
|
||||||
|
|
||||||
APPID_MCHID_NOT_MATCH ErrCode = "APPID_MCHID_NOT_MATCH"
|
APPID_MCHID_NOT_MATCH ErrCode = "APPID_MCHID_NOT_MATCH"
|
||||||
INVALID_REQUEST ErrCode = "INVALID_REQUEST"
|
INVALID_REQUEST ErrCode = "INVALID_REQUEST"
|
||||||
PARAM_ERROR ErrCode = "PARAM_ERROR"
|
PARAM_ERROR ErrCode = "PARAM_ERROR"
|
||||||
|
|
@ -26,86 +25,129 @@ const (
|
||||||
FREQUENCY_LIMITED ErrCode = "FREQUENCY_LIMITED"
|
FREQUENCY_LIMITED ErrCode = "FREQUENCY_LIMITED"
|
||||||
)
|
)
|
||||||
|
|
||||||
// APIError 定义 API 错误结构体
|
const (
|
||||||
type APIError struct {
|
BATCH_NOT_STARTED ErrCode = "BATCH_NOT_STARTED"
|
||||||
StatusCode int `json:"status_code"`
|
BATCH_ENDED ErrCode = "BATCH_ENDED"
|
||||||
ErrorCode ErrCode `json:"error_code"`
|
)
|
||||||
Description string `json:"description"`
|
|
||||||
Hint string `json:"hint"`
|
// CmbAPIErrorMap 定义错误映射,方便根据错误码获取错误信息
|
||||||
}
|
var CmbAPIErrorMap = map[ErrCode][]CmbAPIError{
|
||||||
|
|
||||||
|
SYSTEM_ERROR: {
|
||||||
|
{
|
||||||
|
StatusCode: 500,
|
||||||
|
ErrorCode: SYSTEM_ERROR,
|
||||||
|
Description: "系统异常,请稍后重试",
|
||||||
|
Hint: "请稍后重试",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SIGN_ERROR: {
|
||||||
|
{
|
||||||
|
StatusCode: 401,
|
||||||
|
ErrorCode: SIGN_ERROR,
|
||||||
|
Description: "验证不通过",
|
||||||
|
Hint: "请参阅 签名常见问题",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
// 定义错误映射,方便根据错误码获取错误信息
|
|
||||||
var _ = map[ErrCode][]APIError{
|
|
||||||
APPID_MCHID_NOT_MATCH: {
|
APPID_MCHID_NOT_MATCH: {
|
||||||
{
|
{
|
||||||
StatusCode: 400,
|
StatusCode: 400,
|
||||||
ErrorCode: APPID_MCHID_NOT_MATCH,
|
ErrorCode: APPID_MCHID_NOT_MATCH,
|
||||||
Description: "商户号与AppID不匹配",
|
Description: "商户号与AppID不匹配",
|
||||||
Hint: "调用接口的商户号需与接口传入的AppID有绑定关系,请参考常见问题Q4",
|
Hint: "调用接口的商户号需与接口传入的AppID有绑定关系,请参考常见问题Q4",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
INVALID_REQUEST: {
|
INVALID_REQUEST: {
|
||||||
|
{
|
||||||
|
StatusCode: 400,
|
||||||
|
ErrorCode: INVALID_REQUEST,
|
||||||
|
Description: "HTTP 请求不符合微信支付 APIv3 接口规则",
|
||||||
|
Hint: "请参阅 接口规则",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
StatusCode: 400,
|
StatusCode: 400,
|
||||||
ErrorCode: INVALID_REQUEST,
|
ErrorCode: INVALID_REQUEST,
|
||||||
Description: "OpenID与AppID不匹配",
|
Description: "OpenID与AppID不匹配",
|
||||||
Hint: "OpenID与AppID需有对应关系",
|
Hint: "OpenID与AppID需有对应关系",
|
||||||
|
ThirdErrCode: ThirdErrCodeAppIDOpenIDMismatch,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
StatusCode: 400,
|
StatusCode: 400,
|
||||||
ErrorCode: INVALID_REQUEST,
|
ErrorCode: INVALID_REQUEST,
|
||||||
Description: "非法的商户号",
|
Description: "非法的商户号",
|
||||||
Hint: "请检查商户号准确性",
|
Hint: "请检查商户号准确性",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
StatusCode: 400,
|
StatusCode: 400,
|
||||||
ErrorCode: INVALID_REQUEST,
|
ErrorCode: INVALID_REQUEST,
|
||||||
Description: "调用频率过高",
|
Description: "调用频率过高",
|
||||||
Hint: "请降低API调用频率",
|
Hint: "请降低API调用频率",
|
||||||
|
ThirdErrCode: ThirdErrCodeCallHigh,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
StatusCode: 400,
|
StatusCode: 400,
|
||||||
ErrorCode: INVALID_REQUEST,
|
ErrorCode: INVALID_REQUEST,
|
||||||
Description: "活动已结束或未激活",
|
Description: "活动已结束或未激活",
|
||||||
Hint: "请检查批次状态",
|
Hint: "请检查批次状态",
|
||||||
|
ThirdErrCode: ThirdErrCodeBatchEnded, //前置判断时间处理一下
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
StatusCode: 400,
|
StatusCode: 400,
|
||||||
ErrorCode: INVALID_REQUEST,
|
ErrorCode: INVALID_REQUEST,
|
||||||
Description: "批次信息获取失败,请确认参数是否有误",
|
Description: "批次信息获取失败,请确认参数是否有误",
|
||||||
Hint: "请检查创建商户号与批次号的对应关系",
|
Hint: "请检查创建商户号与批次号的对应关系",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
PARAM_ERROR: {
|
PARAM_ERROR: {
|
||||||
|
{
|
||||||
|
StatusCode: 400,
|
||||||
|
ErrorCode: PARAM_ERROR,
|
||||||
|
Description: "参数错误",
|
||||||
|
Hint: "请根据错误提示正确传入参数",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
StatusCode: 400,
|
StatusCode: 400,
|
||||||
ErrorCode: PARAM_ERROR,
|
ErrorCode: PARAM_ERROR,
|
||||||
Description: "AppID必填",
|
Description: "AppID必填",
|
||||||
Hint: "请输入AppID",
|
Hint: "请输入AppID",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
StatusCode: 400,
|
StatusCode: 400,
|
||||||
ErrorCode: PARAM_ERROR,
|
ErrorCode: PARAM_ERROR,
|
||||||
Description: "OpenID必填",
|
Description: "OpenID必填",
|
||||||
Hint: "请输入OpenID",
|
Hint: "请输入OpenID",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
StatusCode: 400,
|
StatusCode: 400,
|
||||||
ErrorCode: PARAM_ERROR,
|
ErrorCode: PARAM_ERROR,
|
||||||
Description: "批次号必填",
|
Description: "批次号必填",
|
||||||
Hint: "请输入批次号",
|
Hint: "请输入批次号",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
StatusCode: 400,
|
StatusCode: 400,
|
||||||
ErrorCode: PARAM_ERROR,
|
ErrorCode: PARAM_ERROR,
|
||||||
Description: "商户号必填",
|
Description: "商户号必填",
|
||||||
Hint: "请输入商户号",
|
Hint: "请输入商户号",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
StatusCode: 400,
|
StatusCode: 400,
|
||||||
ErrorCode: PARAM_ERROR,
|
ErrorCode: PARAM_ERROR,
|
||||||
Description: "非法的批次状态",
|
Description: "非法的批次状态",
|
||||||
Hint: "请检查批次状态,仅支持发放状态为“运营中”的代金券批次",
|
Hint: "请检查批次状态,仅支持发放状态为“运营中”的代金券批次",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
MCH_NOT_EXISTS: {
|
MCH_NOT_EXISTS: {
|
||||||
|
|
@ -114,6 +156,7 @@ var _ = map[ErrCode][]APIError{
|
||||||
ErrorCode: MCH_NOT_EXISTS,
|
ErrorCode: MCH_NOT_EXISTS,
|
||||||
Description: "商户号不合法",
|
Description: "商户号不合法",
|
||||||
Hint: "请检查商户号准确性",
|
Hint: "请检查商户号准确性",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
NOT_ENOUGH: {
|
NOT_ENOUGH: {
|
||||||
|
|
@ -122,24 +165,28 @@ var _ = map[ErrCode][]APIError{
|
||||||
ErrorCode: NOT_ENOUGH,
|
ErrorCode: NOT_ENOUGH,
|
||||||
Description: "批次预算不足",
|
Description: "批次预算不足",
|
||||||
Hint: "批次预算已发放完,请补充批次预算",
|
Hint: "批次预算已发放完,请补充批次预算",
|
||||||
|
ThirdErrCode: ThirdErrCodeAdvanceFundingNotEnough,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
StatusCode: 403,
|
StatusCode: 403,
|
||||||
ErrorCode: NOT_ENOUGH,
|
ErrorCode: NOT_ENOUGH,
|
||||||
Description: "发券超过单天限额",
|
Description: "发券超过单天限额",
|
||||||
Hint: "已超过该批次设置的单天发放限制额度,无法发放",
|
Hint: "已超过该批次设置的单天发放限制额度,无法发放",
|
||||||
|
ThirdErrCode: ThirdErrCodeDailyLimit,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
StatusCode: 403,
|
StatusCode: 403,
|
||||||
ErrorCode: NOT_ENOUGH,
|
ErrorCode: NOT_ENOUGH,
|
||||||
Description: "账户余额不足,请充值",
|
Description: "账户余额不足,请充值",
|
||||||
Hint: "商户号余额不足,无法继续发券,请充值",
|
Hint: "商户号余额不足,无法继续发券,请充值",
|
||||||
|
ThirdErrCode: ThirdErrCodeAdvanceFundingNotEnough,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
StatusCode: 403,
|
StatusCode: 403,
|
||||||
ErrorCode: NOT_ENOUGH,
|
ErrorCode: NOT_ENOUGH,
|
||||||
Description: "批次预算耗尽",
|
Description: "批次预算耗尽",
|
||||||
Hint: "该批次的预算已经耗尽",
|
Hint: "该批次的预算已经耗尽",
|
||||||
|
ThirdErrCode: ThirdErrCodeAdvanceFundingNotEnough,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
REQUEST_BLOCKED: {
|
REQUEST_BLOCKED: {
|
||||||
|
|
@ -148,36 +195,42 @@ var _ = map[ErrCode][]APIError{
|
||||||
ErrorCode: REQUEST_BLOCKED,
|
ErrorCode: REQUEST_BLOCKED,
|
||||||
Description: "商户无权发券",
|
Description: "商户无权发券",
|
||||||
Hint: "该批次不支持其他商户发放,请参考常见问题Q1",
|
Hint: "该批次不支持其他商户发放,请参考常见问题Q1",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
StatusCode: 403,
|
StatusCode: 403,
|
||||||
ErrorCode: REQUEST_BLOCKED,
|
ErrorCode: REQUEST_BLOCKED,
|
||||||
Description: "批次不支持跨商户发券",
|
Description: "批次不支持跨商户发券",
|
||||||
Hint: "该批次不支持其他商户发放,请参考常见问题Q1",
|
Hint: "该批次不支持其他商户发放,请参考常见问题Q1",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
StatusCode: 403,
|
StatusCode: 403,
|
||||||
ErrorCode: REQUEST_BLOCKED,
|
ErrorCode: REQUEST_BLOCKED,
|
||||||
Description: "用户被限领拦截",
|
Description: "用户被限领拦截",
|
||||||
Hint: "该用户已达到该批次的领取上限,请参考常见问题Q6",
|
Hint: "该用户已达到该批次的领取上限,请参考常见问题Q6",
|
||||||
|
ThirdErrCode: ThirdErrCodeUserParticipationExceeded,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
StatusCode: 403,
|
StatusCode: 403,
|
||||||
ErrorCode: REQUEST_BLOCKED,
|
ErrorCode: REQUEST_BLOCKED,
|
||||||
Description: "不能在API渠道发放",
|
Description: "不能在API渠道发放",
|
||||||
Hint: "请检查批次信息,仅支持发放微信支付代金券,不支持发放立减与折扣",
|
Hint: "请检查批次信息,仅支持发放微信支付代金券,不支持发放立减与折扣",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
StatusCode: 403,
|
StatusCode: 403,
|
||||||
ErrorCode: REQUEST_BLOCKED,
|
ErrorCode: REQUEST_BLOCKED,
|
||||||
Description: "不支持指定面额发券",
|
Description: "不支持指定面额发券",
|
||||||
Hint: "仅在发券时指定面额及门槛的场景才生效,常规发券场景请勿传入该信息",
|
Hint: "仅在发券时指定面额及门槛的场景才生效,常规发券场景请勿传入该信息",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
StatusCode: 403,
|
StatusCode: 403,
|
||||||
ErrorCode: REQUEST_BLOCKED,
|
ErrorCode: REQUEST_BLOCKED,
|
||||||
Description: "仅在广告场景下发放批次",
|
Description: "仅在广告场景下发放批次",
|
||||||
Hint: "该批次已在朋友圈广告发放,不支持在其他渠道发放",
|
Hint: "该批次已在朋友圈广告发放,不支持在其他渠道发放",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RULE_LIMIT: {
|
RULE_LIMIT: {
|
||||||
|
|
@ -186,12 +239,14 @@ var _ = map[ErrCode][]APIError{
|
||||||
ErrorCode: RULE_LIMIT,
|
ErrorCode: RULE_LIMIT,
|
||||||
Description: "用户已达最大领券次数",
|
Description: "用户已达最大领券次数",
|
||||||
Hint: "该用户已达到该批次的领取上限,请参考常见问题Q6",
|
Hint: "该用户已达到该批次的领取上限,请参考常见问题Q6",
|
||||||
|
ThirdErrCode: ThirdErrCodeUserParticipationExceeded,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
StatusCode: 403,
|
StatusCode: 403,
|
||||||
ErrorCode: RULE_LIMIT,
|
ErrorCode: RULE_LIMIT,
|
||||||
Description: "被自然人规则拦截",
|
Description: "被自然人规则拦截",
|
||||||
Hint: "该自然人已达到该批次的领取上限,请参考常见问题Q6",
|
Hint: "该自然人已达到该批次的领取上限,请参考常见问题Q6",
|
||||||
|
ThirdErrCode: ThirdErrCodeUserAccountAbnormal,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
USER_ACCOUNT_ABNORMAL: {
|
USER_ACCOUNT_ABNORMAL: {
|
||||||
|
|
@ -200,6 +255,7 @@ var _ = map[ErrCode][]APIError{
|
||||||
ErrorCode: USER_ACCOUNT_ABNORMAL,
|
ErrorCode: USER_ACCOUNT_ABNORMAL,
|
||||||
Description: "用户非法",
|
Description: "用户非法",
|
||||||
Hint: "用户命中微信支付风控模型,请参考常见问题Q5",
|
Hint: "用户命中微信支付风控模型,请参考常见问题Q5",
|
||||||
|
ThirdErrCode: ThirdErrCodeUserAccountFrozen,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
RESOURCE_NOT_EXISTS: {
|
RESOURCE_NOT_EXISTS: {
|
||||||
|
|
@ -208,6 +264,7 @@ var _ = map[ErrCode][]APIError{
|
||||||
ErrorCode: RESOURCE_NOT_EXISTS,
|
ErrorCode: RESOURCE_NOT_EXISTS,
|
||||||
Description: "批次不存在",
|
Description: "批次不存在",
|
||||||
Hint: "请检查批次及制券商户号信息",
|
Hint: "请检查批次及制券商户号信息",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
FREQUENCY_LIMITED: {
|
FREQUENCY_LIMITED: {
|
||||||
|
|
@ -216,6 +273,7 @@ var _ = map[ErrCode][]APIError{
|
||||||
ErrorCode: FREQUENCY_LIMITED,
|
ErrorCode: FREQUENCY_LIMITED,
|
||||||
Description: "当前请求人数过多,请稍后重试",
|
Description: "当前请求人数过多,请稍后重试",
|
||||||
Hint: "请降低API调用频率",
|
Hint: "请降低API调用频率",
|
||||||
|
ThirdErrCode: ThirdErrCodeCallHigh,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
@ -284,13 +342,13 @@ var WechatError = map[string]*errors.Error{
|
||||||
ErrorWechatAccountFail: err2.ErrorWechatAccountFail(ErrorWechatAccountFail),
|
ErrorWechatAccountFail: err2.ErrorWechatAccountFail(ErrorWechatAccountFail),
|
||||||
}
|
}
|
||||||
|
|
||||||
type ErrBody struct {
|
type WechatErrBody struct {
|
||||||
Code ErrCode `json:"Code"`
|
Code ErrCode `json:"Code"`
|
||||||
Message string `json:"Message"`
|
Message string `json:"Message"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetWechatError 根据错误描述获取具体的错误处理
|
// GetWechatError 根据错误描述获取具体的错误处理
|
||||||
func (s ErrBody) GetWechatError() *errors.Error {
|
func (s WechatErrBody) GetWechatError() *errors.Error {
|
||||||
lowerDesc := strings.ToLower(s.Message)
|
lowerDesc := strings.ToLower(s.Message)
|
||||||
for desc, err := range WechatError {
|
for desc, err := range WechatError {
|
||||||
if strings.ToLower(desc) == lowerDesc {
|
if strings.ToLower(desc) == lowerDesc {
|
||||||
|
|
@ -0,0 +1,197 @@
|
||||||
|
package businesserr
|
||||||
|
|
||||||
|
// 公共 & 业务错误码常量(统一定义,前缀 MULTI_)
|
||||||
|
// https://pay.weixin.qq.com/doc/v3/merchant/4012463767
|
||||||
|
const (
|
||||||
|
MULTI_PARAM_ERROR ErrCode = "PARAM_ERROR" // 参数错误(如缺少必填字段)
|
||||||
|
MULTI_INVALID_REQUEST ErrCode = "INVALID_REQUEST" // 非法请求(如 OpenID 与 AppID 不匹配、批次状态异常等)
|
||||||
|
MULTI_SIGN_ERROR ErrCode = "SIGN_ERROR" // 签名验证失败
|
||||||
|
MULTI_SYSTEM_ERROR ErrCode = "SYSTEM_ERROR" // 系统异常
|
||||||
|
MULTI_APPID_MCHID_NOT_MATCH ErrCode = "APPID_MCHID_NOT_MATCH" // 商户号与 AppID 不匹配
|
||||||
|
MULTI_MCH_NOT_EXISTS ErrCode = "MCH_NOT_EXISTS" // 商户号不合法
|
||||||
|
MULTI_NOT_ENOUGH ErrCode = "NOT_ENOUGH" // 资源不足(预算/余额/限额耗尽)
|
||||||
|
MULTI_REQUEST_BLOCKED ErrCode = "REQUEST_BLOCKED" // 请求被拦截(跨商户、渠道限制等)
|
||||||
|
MULTI_RULE_LIMIT ErrCode = "RULE_LIMIT" // 用户或自然人达到领取上限
|
||||||
|
MULTI_USER_ACCOUNT_ABNORMAL ErrCode = "USER_ACCOUNT_ABNORMAL" // 用户账号异常(风控、未实名等)
|
||||||
|
MULTI_RESOURCE_NOT_EXISTS ErrCode = "RESOURCE_NOT_EXISTS" // 批次不存在
|
||||||
|
)
|
||||||
|
|
||||||
|
// MULTIAPIErrorMap 定义错误映射,方便根据错误码获取所有可能的错误场景
|
||||||
|
var CmbMULTIAPIErrorMap = map[ErrCode][]CmbAPIError{
|
||||||
|
MULTI_SYSTEM_ERROR: {
|
||||||
|
{
|
||||||
|
StatusCode: 500,
|
||||||
|
ErrorCode: MULTI_SYSTEM_ERROR,
|
||||||
|
Description: "系统异常,请稍后重试",
|
||||||
|
Hint: "请稍后重试",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MULTI_SIGN_ERROR: {
|
||||||
|
{
|
||||||
|
StatusCode: 401,
|
||||||
|
ErrorCode: MULTI_SIGN_ERROR,
|
||||||
|
Description: "验证不通过",
|
||||||
|
Hint: "请参阅 签名常见问题",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MULTI_APPID_MCHID_NOT_MATCH: {
|
||||||
|
{
|
||||||
|
StatusCode: 400,
|
||||||
|
ErrorCode: MULTI_APPID_MCHID_NOT_MATCH,
|
||||||
|
Description: "商户号与AppID不匹配",
|
||||||
|
Hint: "调用接口的商户号需与接口传入的AppID有绑定关系,请参考常见问题Q4",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MULTI_INVALID_REQUEST: {
|
||||||
|
{
|
||||||
|
StatusCode: 400,
|
||||||
|
ErrorCode: MULTI_INVALID_REQUEST,
|
||||||
|
Description: "HTTP 请求不符合微信支付 APIv3 接口规则",
|
||||||
|
Hint: "请参阅 接口规则",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StatusCode: 400,
|
||||||
|
ErrorCode: MULTI_INVALID_REQUEST,
|
||||||
|
Description: "非法的商户号",
|
||||||
|
Hint: "请检查商户号准确性",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StatusCode: 400,
|
||||||
|
ErrorCode: MULTI_INVALID_REQUEST,
|
||||||
|
Description: "OpenID与AppID不匹配",
|
||||||
|
Hint: "OpenID与AppID需有对应关系",
|
||||||
|
ThirdErrCode: ThirdErrCodeAppIDOpenIDMismatch,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MULTI_PARAM_ERROR: {
|
||||||
|
{
|
||||||
|
StatusCode: 400,
|
||||||
|
ErrorCode: MULTI_PARAM_ERROR,
|
||||||
|
Description: "参数错误",
|
||||||
|
Hint: "请根据错误提示正确传入参数",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StatusCode: 400,
|
||||||
|
ErrorCode: MULTI_PARAM_ERROR,
|
||||||
|
Description: "AppID必填",
|
||||||
|
Hint: "请输入AppID",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StatusCode: 400,
|
||||||
|
ErrorCode: MULTI_PARAM_ERROR,
|
||||||
|
Description: "OpenID必填",
|
||||||
|
Hint: "请输入OpenID",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StatusCode: 400,
|
||||||
|
ErrorCode: MULTI_PARAM_ERROR,
|
||||||
|
Description: "批次号必填",
|
||||||
|
Hint: "请输入批次号",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StatusCode: 400,
|
||||||
|
ErrorCode: MULTI_PARAM_ERROR,
|
||||||
|
Description: "商户号必填",
|
||||||
|
Hint: "请输入商户号",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MULTI_MCH_NOT_EXISTS: {
|
||||||
|
{
|
||||||
|
StatusCode: 403,
|
||||||
|
ErrorCode: MULTI_MCH_NOT_EXISTS,
|
||||||
|
Description: "商户号不合法",
|
||||||
|
Hint: "请检查商户号准确性",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MULTI_NOT_ENOUGH: {
|
||||||
|
{
|
||||||
|
StatusCode: 403,
|
||||||
|
ErrorCode: MULTI_NOT_ENOUGH,
|
||||||
|
Description: "批次预算耗尽",
|
||||||
|
Hint: "该批次的预算已经耗尽",
|
||||||
|
ThirdErrCode: ThirdErrCodeAdvanceFundingNotEnough,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StatusCode: 403,
|
||||||
|
ErrorCode: MULTI_NOT_ENOUGH,
|
||||||
|
Description: "账户余额不足,请充值",
|
||||||
|
Hint: "商户号余额不足,无法继续发券,请充值",
|
||||||
|
ThirdErrCode: ThirdErrCodeAdvanceFundingNotEnough,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StatusCode: 403,
|
||||||
|
ErrorCode: MULTI_NOT_ENOUGH,
|
||||||
|
Description: "发券超过单天限额",
|
||||||
|
Hint: "已超过该批次设置的单天发放限制额度,无法发放",
|
||||||
|
ThirdErrCode: ThirdErrCodeDailyLimit,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MULTI_REQUEST_BLOCKED: {
|
||||||
|
{
|
||||||
|
StatusCode: 403,
|
||||||
|
ErrorCode: MULTI_REQUEST_BLOCKED,
|
||||||
|
Description: "参数错误,请检查批次参数",
|
||||||
|
Hint: "活动未开始或已结束",
|
||||||
|
ThirdErrCode: ThirdErrCodeBatchNotStarted, // 时间前置判断一下
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StatusCode: 403,
|
||||||
|
ErrorCode: MULTI_REQUEST_BLOCKED,
|
||||||
|
Description: "仅在广告场景下发放批次",
|
||||||
|
Hint: "该批次已在朋友圈广告发放,不支持在其他渠道发放",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StatusCode: 403,
|
||||||
|
ErrorCode: MULTI_REQUEST_BLOCKED,
|
||||||
|
Description: "商户号收款功能受限,无法发券",
|
||||||
|
Hint: "商户号收款功能受限",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
StatusCode: 403,
|
||||||
|
ErrorCode: MULTI_REQUEST_BLOCKED,
|
||||||
|
Description: "批次不支持跨商户发券",
|
||||||
|
Hint: "该批次不支持其他商户发放",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MULTI_RULE_LIMIT: {
|
||||||
|
{
|
||||||
|
StatusCode: 403,
|
||||||
|
ErrorCode: MULTI_RULE_LIMIT,
|
||||||
|
Description: "用户已达最大领券次数",
|
||||||
|
Hint: "该用户已达到该批次的领取上限",
|
||||||
|
ThirdErrCode: ThirdErrCodeUserParticipationExceeded,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MULTI_USER_ACCOUNT_ABNORMAL: {
|
||||||
|
{
|
||||||
|
StatusCode: 403,
|
||||||
|
ErrorCode: MULTI_USER_ACCOUNT_ABNORMAL,
|
||||||
|
Description: "用户未实名",
|
||||||
|
Hint: "该用户无实名信息,无法领券。商家可联系微信支付或让用户联系微信支付客服处理。",
|
||||||
|
ThirdErrCode: ThirdErrCodeUserNotRealNameVerified,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MULTI_RESOURCE_NOT_EXISTS: {
|
||||||
|
{
|
||||||
|
StatusCode: 404,
|
||||||
|
ErrorCode: MULTI_RESOURCE_NOT_EXISTS,
|
||||||
|
Description: "批次不存在",
|
||||||
|
Hint: "请检查批次及制券商户号信息",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
package businesserr
|
||||||
|
|
||||||
|
// ErrCode 定义错误码类型
|
||||||
|
type ErrCode string
|
||||||
|
|
||||||
|
type BusinessErr struct {
|
||||||
|
Code ErrCode `json:"code"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *BusinessErr) Error() string {
|
||||||
|
return e.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
BatchNotStartedError = &BusinessErr{Code: ErrCode("400"), Message: "批次未开始"}
|
||||||
|
BatchEndedError = &BusinessErr{Code: ErrCode("400"), Message: "批次已结束"}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (err *BusinessErr) HandleThirdErrCode() CmbAPIError {
|
||||||
|
|
||||||
|
errCode := err.Code
|
||||||
|
|
||||||
|
emp, ok := CmbAPIPublicErrorMap[errCode]
|
||||||
|
if ok {
|
||||||
|
for _, e := range emp {
|
||||||
|
if e.Description == err.Message {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
em, ok := CmbAPIErrorMap[errCode]
|
||||||
|
if ok {
|
||||||
|
for _, e := range em {
|
||||||
|
if e.Description == err.Message {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CmbAPIError{
|
||||||
|
StatusCode: 499,
|
||||||
|
ErrorCode: errCode,
|
||||||
|
Description: err.Message,
|
||||||
|
Hint: "",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (err *BusinessErr) HandleMULTIThirdErrCode() CmbAPIError {
|
||||||
|
|
||||||
|
errCode := err.Code
|
||||||
|
|
||||||
|
emp, ok := CmbAPIPublicErrorMap[errCode]
|
||||||
|
if ok {
|
||||||
|
for _, e := range emp {
|
||||||
|
if e.Description == err.Message {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
em2, ok := CmbMULTIAPIErrorMap[errCode]
|
||||||
|
if ok {
|
||||||
|
for _, e := range em2 {
|
||||||
|
if e.Description == err.Message {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CmbAPIError{
|
||||||
|
StatusCode: 499,
|
||||||
|
ErrorCode: errCode,
|
||||||
|
Description: err.Message,
|
||||||
|
Hint: "",
|
||||||
|
ThirdErrCode: ThirdErrCodeDefault,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -69,6 +69,7 @@ func (v *Cmb) bizContent(_ context.Context, order *bo.OrderBo, orderNotify *bo.O
|
||||||
OrgNo: v.bc.Cmb.OrgNo,
|
OrgNo: v.bc.Cmb.OrgNo,
|
||||||
Attach: order.Attach,
|
Attach: order.Attach,
|
||||||
Ext: "",
|
Ext: "",
|
||||||
|
TransactionId: order.OutBizNo, // 招行订单号
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmbStatus == vo.CmbStatusUse {
|
if cmbStatus == vo.CmbStatusUse {
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,18 @@ package biz
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
err2 "voucher/api/err"
|
err2 "voucher/api/err"
|
||||||
"voucher/internal/biz/bo"
|
"voucher/internal/biz/bo"
|
||||||
|
"voucher/internal/biz/businesserr"
|
||||||
"voucher/internal/biz/vo"
|
"voucher/internal/biz/vo"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (this *VoucherBiz) CmbOrder(ctx context.Context, req *bo.OrderCreateReqBo) (orderNo string, err error) {
|
func (this *VoucherBiz) CmbOrder(ctx context.Context, req *bo.OrderCreateReqBo) (*bo.OrderBo, error) {
|
||||||
|
|
||||||
order, err3 := this.GetByOutBizNo(ctx, req)
|
order, err3 := this.GetByOutBizNo(ctx, req)
|
||||||
if err3 != nil {
|
if err3 != nil {
|
||||||
return "", err3
|
return nil, err3
|
||||||
}
|
}
|
||||||
|
|
||||||
if order != nil {
|
if order != nil {
|
||||||
|
|
@ -20,24 +22,32 @@ func (this *VoucherBiz) CmbOrder(ctx context.Context, req *bo.OrderCreateReqBo)
|
||||||
if order.Status.IsFail() || order.Status.IsIng() {
|
if order.Status.IsFail() || order.Status.IsIng() {
|
||||||
|
|
||||||
if err4 := this.orderRetry(ctx, order); err4 != nil {
|
if err4 := this.orderRetry(ctx, order); err4 != nil {
|
||||||
return "", err4
|
return nil, err4
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return order.OrderNo, err
|
return order, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
product, err3 := this.ProductRepo.GetByProductNo(ctx, req.ProductNo)
|
product, err3 := this.ProductRepo.GetByProductNo(ctx, req.ProductNo)
|
||||||
if err3 != nil {
|
if err3 != nil {
|
||||||
return "", err3
|
return nil, err3
|
||||||
|
}
|
||||||
|
|
||||||
|
nowTime := time.Now()
|
||||||
|
if nowTime.Before(*product.StartTime) {
|
||||||
|
return nil, businesserr.BatchNotStartedError
|
||||||
|
}
|
||||||
|
if nowTime.After(*product.EndTime) {
|
||||||
|
return nil, businesserr.BatchEndedError
|
||||||
}
|
}
|
||||||
|
|
||||||
order, err3 = this.order(ctx, req, product)
|
order, err3 = this.order(ctx, req, product)
|
||||||
if err3 != nil {
|
if err3 != nil {
|
||||||
return "", err3
|
return nil, err3
|
||||||
}
|
}
|
||||||
|
|
||||||
return order.OrderNo, nil
|
return order, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *VoucherBiz) order(ctx context.Context, req *bo.OrderCreateReqBo, product *bo.ProductBo) (*bo.OrderBo, error) {
|
func (this *VoucherBiz) order(ctx context.Context, req *bo.OrderCreateReqBo, product *bo.ProductBo) (*bo.OrderBo, error) {
|
||||||
|
|
@ -130,8 +140,9 @@ func (this *VoucherBiz) fail(ctx context.Context, order *bo.OrderBo, errReq erro
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if errMsg == `Post "https://api.mch.weixin.qq.com/v3/marketing/favor/users/oW97fjrv_ntYBPjMsaaEMRSj6TPA/coupons": EOF` {
|
|
||||||
// 微信:不同微信号,领取了很多次,自然人限领,发放频率限制,微信研发排期,后续这个错误信息应该会更新,近期没那么快同步上去
|
// 微信:不同微信号,领取了很多次,自然人限领,发放频率限制,微信研发排期,后续这个错误信息应该会更新,近期没那么快同步上去
|
||||||
|
eqMsg := fmt.Sprintf(`Post "https://api.mch.weixin.qq.com/v3/marketing/favor/users/%s/coupons": EOF`, order.Account)
|
||||||
|
if errMsg == eqMsg {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ type OrderRepo interface {
|
||||||
Success(ctx context.Context, id uint64, voucherNo string) error
|
Success(ctx context.Context, id uint64, voucherNo string) error
|
||||||
Fail(ctx context.Context, id uint64, remark string) error
|
Fail(ctx context.Context, id uint64, remark string) error
|
||||||
Used(ctx context.Context, id uint64) error
|
Used(ctx context.Context, id uint64) error
|
||||||
NotifyUsed(ctx context.Context, id uint64, transactionId string) error
|
NotifyUsed(ctx context.Context, id uint64, transactionId string, lastUseTime time.Time) error
|
||||||
MultiLastUsed(ctx context.Context, id uint64, lastUseTime time.Time) error
|
MultiLastUsed(ctx context.Context, id uint64, lastUseTime time.Time) error
|
||||||
MultiOverUsed(ctx context.Context, id uint64, lastUseTime time.Time, remark string) error
|
MultiOverUsed(ctx context.Context, id uint64, lastUseTime time.Time, remark string) error
|
||||||
Available(ctx context.Context, id uint64) error
|
Available(ctx context.Context, id uint64) error
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ func (this *VoucherBiz) notifyUsed(ctx context.Context, order *bo.OrderBo, req *
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := this.OrderRepo.NotifyUsed(ctx, order.ID, req.PlainText.ConsumeInformation.TransactionID); err != nil {
|
if err := this.OrderRepo.NotifyUsed(ctx, order.ID, req.PlainText.ConsumeInformation.TransactionID, req.PlainText.ConsumeInformation.ConsumeTime); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -549,7 +549,7 @@ func (p *OrderRepoImpl) MultiOverUsed(ctx context.Context, id uint64, lastUseTim
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *OrderRepoImpl) NotifyUsed(ctx context.Context, id uint64, transactionId string) error {
|
func (p *OrderRepoImpl) NotifyUsed(ctx context.Context, id uint64, transactionId string, lastUseTime time.Time) error {
|
||||||
now := time.Now()
|
now := time.Now()
|
||||||
|
|
||||||
tx := p.DB(ctx).
|
tx := p.DB(ctx).
|
||||||
|
|
@ -560,7 +560,7 @@ func (p *OrderRepoImpl) NotifyUsed(ctx context.Context, id uint64, transactionId
|
||||||
Status: vo.OrderStatusUse.GetValue(),
|
Status: vo.OrderStatusUse.GetValue(),
|
||||||
TransactionId: transactionId,
|
TransactionId: transactionId,
|
||||||
Remark: "微信回调核销",
|
Remark: "微信回调核销",
|
||||||
LastUseTime: &now,
|
LastUseTime: &lastUseTime,
|
||||||
UpdateTime: &now,
|
UpdateTime: &now,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
package wechatrepoimpl
|
package wechatrepoimpl
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/go-kratos/kratos/v2/log"
|
||||||
"github.com/wechatpay-apiv3/wechatpay-go/core"
|
"github.com/wechatpay-apiv3/wechatpay-go/core"
|
||||||
err2 "voucher/api/err"
|
err2 "voucher/api/err"
|
||||||
"voucher/internal/biz/bo"
|
"voucher/internal/biz/bo"
|
||||||
|
"voucher/internal/biz/businesserr"
|
||||||
"voucher/internal/biz/wechatrepo"
|
"voucher/internal/biz/wechatrepo"
|
||||||
"voucher/internal/conf"
|
"voucher/internal/conf"
|
||||||
"voucher/internal/data"
|
"voucher/internal/data"
|
||||||
|
|
@ -43,11 +46,20 @@ func (w *BankMultiActivityImpl) Order(order *bo.OrderBo) (couponId string, err e
|
||||||
|
|
||||||
var e *utils.ApiException
|
var e *utils.ApiException
|
||||||
if errors.As(err, &e) {
|
if errors.As(err, &e) {
|
||||||
apiErr, err3 := marketing.BuildErr(e.Body())
|
// 格式:{"code":"INVALID_REQUEST","message":"对应单号已超出重试期;请查单确认后决定是否换单请求"}
|
||||||
if err3 != nil {
|
var beer *businesserr.BusinessErr
|
||||||
return "", fmt.Errorf("ApiException analysis err: %+v", err3)
|
if err = json.Unmarshal(e.Body(), &beer); err != nil {
|
||||||
|
log.Errorf("微信错误返回body解析报错,body:%s,err:%s", string(e.Body()), err.Error())
|
||||||
|
return "", err2.ErrorWechatFAIL(fmt.Sprintf("微信错误返回内容解析错误:%s", err.Error()))
|
||||||
}
|
}
|
||||||
return "", err2.ErrorWechatFAIL("%s-%s", apiErr.Code, apiErr.Message)
|
|
||||||
|
//apiErr, err3 := marketing.BuildErr(e.Body())
|
||||||
|
//if err3 != nil {
|
||||||
|
// return "", fmt.Errorf("ApiException analysis err: %+v", err3)
|
||||||
|
//}
|
||||||
|
//return "", err2.ErrorWechatFAIL("%s-%s", apiErr.Code, apiErr.Message)
|
||||||
|
|
||||||
|
return "", beer
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", err
|
return "", err
|
||||||
|
|
|
||||||
|
|
@ -9,14 +9,18 @@ import (
|
||||||
"github.com/wechatpay-apiv3/wechatpay-go/services/cashcoupons"
|
"github.com/wechatpay-apiv3/wechatpay-go/services/cashcoupons"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
err2 "voucher/api/err"
|
err2 "voucher/api/err"
|
||||||
"voucher/internal/biz/bo"
|
"voucher/internal/biz/bo"
|
||||||
|
"voucher/internal/biz/businesserr"
|
||||||
"voucher/internal/biz/vo"
|
"voucher/internal/biz/vo"
|
||||||
"voucher/internal/biz/wechatrepo"
|
"voucher/internal/biz/wechatrepo"
|
||||||
"voucher/internal/conf"
|
"voucher/internal/conf"
|
||||||
"voucher/internal/data"
|
"voucher/internal/data"
|
||||||
|
"voucher/internal/pkg/helper"
|
||||||
"voucher/internal/pkg/request"
|
"voucher/internal/pkg/request"
|
||||||
|
"voucher/internal/pkg/supplier/qixing"
|
||||||
)
|
)
|
||||||
|
|
||||||
// CpnRepoImpl .
|
// CpnRepoImpl .
|
||||||
|
|
@ -52,18 +56,19 @@ func (c *CpnRepoImpl) GetClient(ctx context.Context) (*core.Client, error) {
|
||||||
|
|
||||||
func (c *CpnRepoImpl) bodyErr(_ context.Context, result *core.APIResult) error {
|
func (c *CpnRepoImpl) bodyErr(_ context.Context, result *core.APIResult) error {
|
||||||
|
|
||||||
|
// // 格式:{"code":"INVALID_REQUEST","message":"对应单号已超出重试期;请查单确认后决定是否换单请求"}
|
||||||
bodyBytes, err := io.ReadAll(result.Response.Body)
|
bodyBytes, err := io.ReadAll(result.Response.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err2.ErrorWechatFAIL(fmt.Sprintf("读取微信错误返回body报错:%s", err.Error()))
|
return err2.ErrorWechatFAIL(fmt.Sprintf("读取微信错误返回body报错:%s", err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
var errBody ErrBody
|
var beer *businesserr.BusinessErr
|
||||||
if err = json.Unmarshal(bodyBytes, &errBody); err != nil {
|
if err = json.Unmarshal(bodyBytes, &beer); err != nil {
|
||||||
log.Errorf("微信错误返回body解析报错,body:%s,err:%s", string(bodyBytes), err.Error())
|
log.Errorf("微信错误返回body解析报错,body:%s,err:%s", string(bodyBytes), err.Error())
|
||||||
return err2.ErrorWechatFAIL(fmt.Sprintf("微信错误返回内容解析错误:%s", err.Error()))
|
return err2.ErrorWechatFAIL(fmt.Sprintf("微信错误返回内容解析错误:%s", err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
return errBody.GetWechatError()
|
return beer
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CpnRepoImpl) Order(ctx context.Context, order *bo.OrderBo) (string, error) {
|
func (c *CpnRepoImpl) Order(ctx context.Context, order *bo.OrderBo) (string, error) {
|
||||||
|
|
@ -97,12 +102,85 @@ func (c *CpnRepoImpl) Order(ctx context.Context, order *bo.OrderBo) (string, err
|
||||||
return *resp.CouponId, nil
|
return *resp.CouponId, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CpnRepoImpl) Query(ctx context.Context, orderWechat *bo.OrderBo) (vo.OrderStatus, error) {
|
func (c *CpnRepoImpl) Query(ctx context.Context, order *bo.OrderBo) (vo.OrderStatus, error) {
|
||||||
|
|
||||||
|
// todo 确认下,多笔立减金用普通立减金的接口查询也能查,结果是准确的吗
|
||||||
|
|
||||||
|
// 福州启蒙 - 启星
|
||||||
|
if order.MerchantNo == "1715349578" {
|
||||||
|
//return c.QxQuery(ctx, order)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.LsxdQuery(ctx, order)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CpnRepoImpl) QxQuery(ctx context.Context, order *bo.OrderBo) (vo.OrderStatus, error) {
|
||||||
|
|
||||||
|
if order.ActivityId == "" {
|
||||||
|
return 0, fmt.Errorf("商户号 %s 只支持多笔立减金查询", order.MerchantNo)
|
||||||
|
}
|
||||||
|
|
||||||
|
b := qixing.QxQueryReq{
|
||||||
|
OrderNo: order.OrderNo,
|
||||||
|
CouponId: order.VoucherNo,
|
||||||
|
OpenId: order.Account,
|
||||||
|
}
|
||||||
|
|
||||||
|
var strToBeSigned strings.Builder
|
||||||
|
|
||||||
|
kvRows := helper.SortStructJsonTag(b)
|
||||||
|
for _, kv := range kvRows {
|
||||||
|
if kv.Key == "sign" || kv.Value == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
strToBeSigned.WriteString(fmt.Sprintf("%s=%s&", kv.Key, kv.Value))
|
||||||
|
}
|
||||||
|
s := strToBeSigned.String() + "config.AppKey"
|
||||||
|
|
||||||
|
b.Sign = helper.Md5(s)
|
||||||
|
|
||||||
|
body, err := json.Marshal(b)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
isSuccess := func(code int) bool {
|
||||||
|
return code == http.StatusOK
|
||||||
|
}
|
||||||
|
|
||||||
|
h := http.Header{
|
||||||
|
"Content-Type": []string{"application/json"},
|
||||||
|
}
|
||||||
|
|
||||||
|
_, body, err = request.Post(
|
||||||
|
ctx,
|
||||||
|
c.bc.WechatNotifyMQ.RegisterTagUrl,
|
||||||
|
body,
|
||||||
|
request.WithHeaders(h),
|
||||||
|
request.WithStatusCodeFunc(isSuccess),
|
||||||
|
request.WithTimeout(time.Second*10),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var resp qixing.QxQueryResp
|
||||||
|
if err := json.Unmarshal(body, &resp); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统一返回状态类型
|
||||||
|
return resp.Data.CouponState.GetStatus()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CpnRepoImpl) LsxdQuery(ctx context.Context, order *bo.OrderBo) (vo.OrderStatus, error) {
|
||||||
|
|
||||||
|
// 需要判断是多笔立减金查询还是普通立减金查询,此处需要更正查询方式
|
||||||
|
|
||||||
req := cashcoupons.QueryCouponRequest{
|
req := cashcoupons.QueryCouponRequest{
|
||||||
CouponId: core.String(orderWechat.VoucherNo),
|
CouponId: core.String(order.VoucherNo),
|
||||||
Appid: core.String(orderWechat.AppID),
|
Appid: core.String(order.AppID),
|
||||||
Openid: core.String(orderWechat.Account),
|
Openid: core.String(order.Account),
|
||||||
}
|
}
|
||||||
|
|
||||||
client, err := c.GetClient(ctx)
|
client, err := c.GetClient(ctx)
|
||||||
|
|
|
||||||
|
|
@ -1,39 +1 @@
|
||||||
package wechatrepoimpl
|
package wechatrepoimpl
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
errors2 "github.com/go-kratos/kratos/v2/errors"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestGetErrorByDescription(t *testing.T) {
|
|
||||||
e := &ErrBody{
|
|
||||||
Code: "INVALID_REQUEST",
|
|
||||||
Message: "活动已结束或未激活",
|
|
||||||
}
|
|
||||||
t.Log(e.GetWechatError())
|
|
||||||
|
|
||||||
err := gorm.ErrRecordNotFound
|
|
||||||
|
|
||||||
t.Log(errors.Is(err, gorm.ErrRecordNotFound))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestErr(t *testing.T) {
|
|
||||||
e := &ErrBody{
|
|
||||||
Code: "INVALID_REQUEST",
|
|
||||||
Message: "活动已结束或未激活",
|
|
||||||
}
|
|
||||||
|
|
||||||
se := errors2.FromError(e.GetWechatError())
|
|
||||||
|
|
||||||
t.Log(se.Reason)
|
|
||||||
t.Log(se.Message)
|
|
||||||
|
|
||||||
e2 := errors.New("活动已结束或未激活")
|
|
||||||
se2 := errors2.FromError(e2)
|
|
||||||
|
|
||||||
t.Log(se2.Reason)
|
|
||||||
t.Log(len(se2.Reason))
|
|
||||||
t.Log(se2.Message)
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
package helper
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
type KeyValue struct {
|
||||||
|
Key string
|
||||||
|
Value string
|
||||||
|
}
|
||||||
|
|
||||||
|
func SortStruct(data any) []KeyValue {
|
||||||
|
v := reflect.ValueOf(data).Elem()
|
||||||
|
t := v.Type()
|
||||||
|
var kv []KeyValue
|
||||||
|
for i := 0; i < v.NumField(); i++ {
|
||||||
|
field := v.Field(i)
|
||||||
|
key := LowercaseFirstLetter(t.Field(i).Name)
|
||||||
|
value := fmt.Sprintf("%v", field.Interface())
|
||||||
|
kv = append(kv, KeyValue{Key: key, Value: value})
|
||||||
|
}
|
||||||
|
sort.SliceStable(kv, func(i, j int) bool {
|
||||||
|
return kv[i].Key < kv[j].Key
|
||||||
|
})
|
||||||
|
return kv
|
||||||
|
}
|
||||||
|
|
||||||
|
func SortStructJsonTag(data any) []KeyValue {
|
||||||
|
// 获取 data 结构体的类型和值
|
||||||
|
dataType := reflect.TypeOf(data).Elem()
|
||||||
|
dataValue := reflect.ValueOf(data).Elem()
|
||||||
|
|
||||||
|
var kv []KeyValue
|
||||||
|
for i := 0; i < dataType.NumField(); i++ {
|
||||||
|
field := dataType.Field(i)
|
||||||
|
// 获取字段的值
|
||||||
|
fieldValue := dataValue.FieldByName(field.Name).Interface()
|
||||||
|
// 获取 JSON 字段名
|
||||||
|
jsonTagName := field.Tag.Get("json")
|
||||||
|
if jsonTagName != "" {
|
||||||
|
kv = append(kv, KeyValue{Key: jsonTagName, Value: fmt.Sprintf("%v", fieldValue)})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排序
|
||||||
|
sort.SliceStable(kv, func(i, j int) bool {
|
||||||
|
return kv[i].Key < kv[j].Key
|
||||||
|
})
|
||||||
|
|
||||||
|
return kv
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
package bo
|
package qixing
|
||||||
|
|
||||||
import "github.com/go-playground/validator/v10"
|
import (
|
||||||
|
"github.com/go-playground/validator/v10"
|
||||||
|
)
|
||||||
|
|
||||||
type QiXingRequestBo struct {
|
type QiXingRequestBo struct {
|
||||||
Content string `json:"content" validate:"required"`
|
Content string `json:"content" validate:"required"`
|
||||||
|
|
@ -0,0 +1,59 @@
|
||||||
|
package qixing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
"voucher/internal/biz/vo"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CouponState string
|
||||||
|
|
||||||
|
const (
|
||||||
|
CouponStateUnknown CouponState = "COUPON_STATE_UNKNOW" // 未知状态
|
||||||
|
CouponStateSend CouponState = "COUPON_STATE_SEND" // 可用
|
||||||
|
CouponStateUsed CouponState = "COUPON_STATE_USED" // 已实扣
|
||||||
|
CouponStateExpired CouponState = "COUPON_STATE_EXPIRED" // 已过期
|
||||||
|
)
|
||||||
|
|
||||||
|
var CpnStatusMap = map[CouponState]vo.OrderStatus{
|
||||||
|
CouponStateSend: vo.OrderStatusSuccess,
|
||||||
|
CouponStateUsed: vo.OrderStatusUse,
|
||||||
|
CouponStateExpired: vo.OrderStatusExpired,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o CouponState) GetStatus() (vo.OrderStatus, error) {
|
||||||
|
if resultStatus, ok := CpnStatusMap[o]; ok {
|
||||||
|
return resultStatus, nil
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("CpnStatus[%s]未定义", o)
|
||||||
|
}
|
||||||
|
|
||||||
|
type QxQueryReq struct {
|
||||||
|
OrderNo string `json:"order_no"`
|
||||||
|
CouponId string `json:"coupon_id"`
|
||||||
|
OpenId string `json:"open_id"`
|
||||||
|
Sign string `json:"sign"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type QxQueryResp struct {
|
||||||
|
Code any `json:"code"`
|
||||||
|
Msg string `json:"msg"`
|
||||||
|
Data *struct {
|
||||||
|
StockCreatorMchID string `json:"stock_creator_mchid"`
|
||||||
|
StockID string `json:"stock_id"`
|
||||||
|
CouponID string `json:"coupon_id"`
|
||||||
|
CouponName string `json:"coupon_name"`
|
||||||
|
CouponState CouponState `json:"coupon_state"`
|
||||||
|
ReceiveTime time.Time `json:"receive_time"`
|
||||||
|
AvailableBeginTime time.Time `json:"available_begin_time"`
|
||||||
|
AvailableEndTime time.Time `json:"available_end_time"`
|
||||||
|
ActivityID string `json:"activity_id"`
|
||||||
|
MaxUseNumber int `json:"max_use_number"`
|
||||||
|
AvailableNumber int `json:"available_number"`
|
||||||
|
UsedNumber int `json:"used_number"`
|
||||||
|
UseAmountList struct {
|
||||||
|
UsedAmounts []string `json:"used_amounts"`
|
||||||
|
} `json:"use_amount_list"`
|
||||||
|
OpenID string `json:"openid"`
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
|
@ -3,29 +3,28 @@ package service
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/go-kratos/kratos/v2/errors"
|
|
||||||
err2 "voucher/api/err"
|
|
||||||
v1 "voucher/api/v1"
|
v1 "voucher/api/v1"
|
||||||
"voucher/internal/biz/bo"
|
"voucher/internal/biz/bo"
|
||||||
|
"voucher/internal/biz/businesserr"
|
||||||
"voucher/internal/biz/vo"
|
"voucher/internal/biz/vo"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (c *CmbService) Order(ctx context.Context, request *v1.CmbRequest) (*v1.CmbReply, error) {
|
func (c *CmbService) Order(ctx context.Context, request *v1.CmbRequest) (*v1.CmbReply, error) {
|
||||||
|
|
||||||
orderNo, err := c.order(ctx, request)
|
order, err := c.order(ctx, request)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.OrderFail(ctx, err)
|
return c.OrderFail(ctx, order, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.OrderSuccess(ctx, orderNo)
|
return c.OrderSuccess(ctx, order.OrderNo)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CmbService) order(ctx context.Context, request *v1.CmbRequest) (string, error) {
|
func (c *CmbService) order(ctx context.Context, request *v1.CmbRequest) (*bo.OrderBo, error) {
|
||||||
|
|
||||||
bizContent, err := c.CmbMixRepo.OrderVerify(ctx, request)
|
bizContent, err := c.CmbMixRepo.OrderVerify(ctx, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
boReq := &bo.OrderCreateReqBo{
|
boReq := &bo.OrderCreateReqBo{
|
||||||
|
|
@ -39,12 +38,12 @@ func (c *CmbService) order(ctx context.Context, request *v1.CmbRequest) (string,
|
||||||
NotifyUrl: c.bc.Cmb.NotifyUrl,
|
NotifyUrl: c.bc.Cmb.NotifyUrl,
|
||||||
}
|
}
|
||||||
|
|
||||||
orderNo, err := c.VoucherBiz.CmbOrder(ctx, boReq)
|
order, err := c.VoucherBiz.CmbOrder(ctx, boReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return orderNo, nil
|
return order, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CmbService) OrderSuccess(ctx context.Context, orderNo string) (*v1.CmbReply, error) {
|
func (c *CmbService) OrderSuccess(ctx context.Context, orderNo string) (*v1.CmbReply, error) {
|
||||||
|
|
@ -60,19 +59,37 @@ func (c *CmbService) OrderSuccess(ctx context.Context, orderNo string) (*v1.CmbR
|
||||||
return c.GetResponse(ctx, replyBizContent)
|
return c.GetResponse(ctx, replyBizContent)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *CmbService) OrderFail(ctx context.Context, err error) (*v1.CmbReply, error) {
|
func (c *CmbService) OrderFail(ctx context.Context, order *bo.OrderBo, err error) (*v1.CmbReply, error) {
|
||||||
|
|
||||||
se := errors.FromError(err)
|
bizReply := &v1.CmbOrderReply{}
|
||||||
|
|
||||||
if len(se.Reason) == 0 {
|
if e, ok := err.(*businesserr.BusinessErr); ok {
|
||||||
se.Reason = err2.CmbErr_CMB_UNKNOWN.String()
|
|
||||||
|
if order.ActivityId != "" {
|
||||||
|
cmbAPIError := e.HandleMULTIThirdErrCode()
|
||||||
|
bizReply = &v1.CmbOrderReply{
|
||||||
|
RespCode: vo.CmbResponseStatusFail.GetValue(),
|
||||||
|
RespMsg: cmbAPIError.Description,
|
||||||
|
CodeNo: "",
|
||||||
|
ThirdErrCode: string(cmbAPIError.ThirdErrCode),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
cmbAPIError := e.HandleThirdErrCode()
|
||||||
|
bizReply = &v1.CmbOrderReply{
|
||||||
|
RespCode: vo.CmbResponseStatusFail.GetValue(),
|
||||||
|
RespMsg: cmbAPIError.Description,
|
||||||
|
CodeNo: "",
|
||||||
|
ThirdErrCode: string(cmbAPIError.ThirdErrCode),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bizReply := &v1.CmbOrderReply{
|
} else {
|
||||||
|
bizReply = &v1.CmbOrderReply{
|
||||||
RespCode: vo.CmbResponseStatusFail.GetValue(),
|
RespCode: vo.CmbResponseStatusFail.GetValue(),
|
||||||
RespMsg: se.Message,
|
RespMsg: err.Error(),
|
||||||
CodeNo: "",
|
CodeNo: "",
|
||||||
ThirdErrCode: se.Reason,
|
ThirdErrCode: string(businesserr.ThirdErrCodeDefault),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
replyBizContent, _ := json.Marshal(bizReply)
|
replyBizContent, _ := json.Marshal(bizReply)
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import (
|
||||||
"voucher/internal/biz/bo"
|
"voucher/internal/biz/bo"
|
||||||
"voucher/internal/conf"
|
"voucher/internal/conf"
|
||||||
"voucher/internal/pkg/helper"
|
"voucher/internal/pkg/helper"
|
||||||
|
"voucher/internal/pkg/supplier/qixing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TripartiteService struct {
|
type TripartiteService struct {
|
||||||
|
|
@ -40,7 +41,7 @@ func (srv *TripartiteService) QiXingNotify(ctx http.Context) error {
|
||||||
return srv.ResponseErr(ctx, fmt.Sprintf("read body error: %v", err))
|
return srv.ResponseErr(ctx, fmt.Sprintf("read body error: %v", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
var req *bo.QiXingRequestBo
|
var req *qixing.QiXingRequestBo
|
||||||
if err = json.Unmarshal(bodyBytes, &req); err != nil {
|
if err = json.Unmarshal(bodyBytes, &req); err != nil {
|
||||||
log.Errorf("qixing notify bodyBytes err ip:%s,error:%v,body:%s", ip, err, string(bodyBytes))
|
log.Errorf("qixing notify bodyBytes err ip:%s,error:%v,body:%s", ip, err, string(bodyBytes))
|
||||||
return srv.ResponseErr(ctx, fmt.Sprintf("json unmarshal bodyBytes error: %v", err))
|
return srv.ResponseErr(ctx, fmt.Sprintf("json unmarshal bodyBytes error: %v", err))
|
||||||
|
|
@ -89,13 +90,13 @@ func (srv *TripartiteService) QiXingNotify(ctx http.Context) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *TripartiteService) ResponseOK(ctx http.Context) error {
|
func (this *TripartiteService) ResponseOK(ctx http.Context) error {
|
||||||
return ctx.JSON(http2.StatusOK, &bo.QiXingResponse{
|
return ctx.JSON(http2.StatusOK, &qixing.QiXingResponse{
|
||||||
Msg: "SUCCESS",
|
Msg: "SUCCESS",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *TripartiteService) ResponseErr(ctx http.Context, msg string) error {
|
func (this *TripartiteService) ResponseErr(ctx http.Context, msg string) error {
|
||||||
return ctx.JSON(http2.StatusOK, &bo.QiXingResponse{
|
return ctx.JSON(http2.StatusOK, &qixing.QiXingResponse{
|
||||||
Msg: msg,
|
Msg: msg,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,13 @@ package test
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
"voucher/internal/biz/bo"
|
"voucher/internal/biz/businesserr"
|
||||||
"voucher/internal/pkg/helper"
|
"voucher/internal/pkg/helper"
|
||||||
|
"voucher/internal/pkg/supplier/qixing"
|
||||||
|
"voucher/internal/pkg/wechat/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_MarketingSend(t *testing.T) {
|
func Test_MarketingSend(t *testing.T) {
|
||||||
|
|
@ -25,22 +28,39 @@ func Test_MarketingSend(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_MarketingQuery(t *testing.T) {
|
func Test_MarketingQuery(t *testing.T) {
|
||||||
|
|
||||||
|
appId := "wx619991cc795028f5"
|
||||||
|
openId := "oSNb4fixFZ4uRvcP6F25_FySgUE0"
|
||||||
|
couponId := "147079366189"
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
appId, openId, couponId string
|
appId, openId, couponId string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "查询绑定多笔立减活动的代金券详情", // 查询的商户非创建方商户 查询商户要为创建方商户
|
name: "查询绑定多笔立减活动的代金券详情", // 查询的商户非创建方商户 查询商户要为创建方商户
|
||||||
appId: "wx619991cc795028f5",
|
appId: appId,
|
||||||
openId: "oSNb4ftgnWC22Z0cWTjsQebdr2Yk",
|
openId: openId,
|
||||||
couponId: "139923450432",
|
couponId: couponId,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
resp, err := MarketingQuery(tt.appId, tt.openId, tt.couponId)
|
resp, err := MarketingQuery(tt.appId, tt.openId, tt.couponId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
||||||
|
var e *utils.ApiException
|
||||||
|
if errors.As(err, &e) {
|
||||||
|
// 格式:{"code":"INVALID_REQUEST","message":"对应单号已超出重试期;请查单确认后决定是否换单请求"}
|
||||||
|
var beer *businesserr.BusinessErr
|
||||||
|
if err = json.Unmarshal(e.Body(), &beer); err != nil {
|
||||||
|
t.Errorf("微信错误返回body解析报错,body:%s,err:%s", string(e.Body()), err.Error())
|
||||||
|
}
|
||||||
|
t.Logf("MarketingQuery error,body:%s,err:%s", string(e.Body()), e.ErrCode)
|
||||||
|
} else {
|
||||||
t.Errorf("MarketingQuery() error = %v", err)
|
t.Errorf("MarketingQuery() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
t.Logf("MarketingQuery() = %v", resp)
|
t.Logf("MarketingQuery() = %v", resp)
|
||||||
|
|
@ -78,7 +98,7 @@ func Test_QixingNotifyData(t *testing.T) {
|
||||||
|
|
||||||
ciphertext := helper.Md5(content + "DrY1zLkOH01q0sN66vrmkdpbWsyb41ho")
|
ciphertext := helper.Md5(content + "DrY1zLkOH01q0sN66vrmkdpbWsyb41ho")
|
||||||
|
|
||||||
req := bo.QiXingRequestBo{
|
req := qixing.QiXingRequestBo{
|
||||||
Content: content,
|
Content: content,
|
||||||
Timestamp: time.Now().UnixMilli(),
|
Timestamp: time.Now().UnixMilli(),
|
||||||
Ciphertext: ciphertext,
|
Ciphertext: ciphertext,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,166 @@
|
||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
v1 "voucher/api/v1"
|
||||||
|
"voucher/internal/biz/vo"
|
||||||
|
"voucher/internal/pkg/cmb"
|
||||||
|
"voucher/internal/pkg/helper"
|
||||||
|
"voucher/internal/pkg/request"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_CMBRequest(t *testing.T) {
|
||||||
|
|
||||||
|
bizParma := &v1.CmbNotifyRequest{
|
||||||
|
Ticket: "190662195271022592015", // 优惠券券码,codeNo
|
||||||
|
Status: "0", // 更新后串码状态,0:可使用,1:已使用
|
||||||
|
TransDate: "", // 验码日期,格式yyyy-mm-dd hh:mm:ss.sss
|
||||||
|
OrgNo: "LANSEXIONGDI", // 固定值
|
||||||
|
Ext: "", // 扩展字段
|
||||||
|
Attach: "bFUWzuzvJfBshobjcQPFTvpqcH1AvGqHJtiV44mdsWEQKCPXgydk8ft/b227S3TM", // 扩展字段
|
||||||
|
TransactionId: "2000000000001422730", // 招行唯一流水号
|
||||||
|
}
|
||||||
|
|
||||||
|
bizParmaJsonBytes, err := json.Marshal(bizParma)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptBody, err := cmb.EncryptBody(&cmb.Encrypts{SoaPubKey: "0416445bc16cbf42e47002ad9fe7c7af67d902b48be1eb69b98f6a006b0918630e1127f5f2fff83b2ecb30fc7fd72c34c33f37c7c355dffde3589f66800f0036ca", JsonParam: string(bizParmaJsonBytes)})
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &v1.CmbRequest{
|
||||||
|
Mid: "d6fdd78b6fd13a808818286b9cad9687",
|
||||||
|
Aid: "5efaa21263b94f669a1c90ed0279df20",
|
||||||
|
Date: time.Now().Format("20060102150405"),
|
||||||
|
Random: string(cmb.RandomBytes(16)),
|
||||||
|
KeyAlias: "CO_PUB_KEY_SM2",
|
||||||
|
CmbKeyAlias: "SM2_CMBLIFE",
|
||||||
|
EncryptBody: encryptBody,
|
||||||
|
Sign: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
str := fmt.Sprintf("%s?%s", vo.CmbNotifyFuncName, cmb.SortStructStr(req))
|
||||||
|
|
||||||
|
sign, err := cmb.SignBody(str, "8d39ff3d2559258c163f4510f082727f51531e1953ab203d5ab1ea4a6d94fd73")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Sign = sign
|
||||||
|
|
||||||
|
kvRows := helper.SortStructFieldsByKey(req)
|
||||||
|
|
||||||
|
uv := url.Values{}
|
||||||
|
|
||||||
|
for _, kv := range kvRows {
|
||||||
|
uv.Set(kv.Key, fmt.Sprintf("%v", kv.Value))
|
||||||
|
}
|
||||||
|
|
||||||
|
h := http.Header{
|
||||||
|
"Content-Type": []string{"application/x-www-form-urlencoded"},
|
||||||
|
}
|
||||||
|
|
||||||
|
uri := "https://sandbox.cdcc.cmbchina.com/AccessGateway/transIn/updateCodeStatus.json"
|
||||||
|
r := uri + "?" + uv.Encode()
|
||||||
|
|
||||||
|
respHeader, bodyBytes, err := request.Post(context.Background(), r, nil, request.WithHeaders(h), request.WithTimeout(time.Second*10))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("请求地址:%s", uri)
|
||||||
|
t.Logf("业务参数:%s", string(bizParmaJsonBytes))
|
||||||
|
t.Logf("请求响应体:%s", string(bodyBytes))
|
||||||
|
t.Logf("请求响应头:%+v", respHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_CmMultiRequest(t *testing.T) {
|
||||||
|
|
||||||
|
bizParma := &v1.CmbMultiNotifyRequest{
|
||||||
|
TransactionId: "2000000000001636670", // 招行唯一流水号
|
||||||
|
ActivityId: "11941580000000008",
|
||||||
|
CouponId: "118770338167",
|
||||||
|
AcquiredDate: "2025-08-25 14:56:54.123",
|
||||||
|
Status: "1", // 更新后串码状态,0:可使用,1:已使用
|
||||||
|
CouponStatus: "1", // String|M 整张券总状态0:可使用,1:已使用
|
||||||
|
TransDate: "2025-08-28 12:09:41.123", // 验码日期,格式yyyy-mm-dd hh:mm:ss.sss
|
||||||
|
TransAmount: "10",
|
||||||
|
OrderId: "4200002772202508280813272296",
|
||||||
|
Ticket: "195987259358664294429", // 优惠券券码,codeNo
|
||||||
|
OrgNo: "LANSEXIONGDI", // 固定值
|
||||||
|
Attach: "bFUWzuzvJfBshobjcQPFTvpqcH1AvGqHJtiV44mdsWEQKCPXgydk8ft/b227S3TM", // 扩展字段
|
||||||
|
Ext: "", // 扩展字段
|
||||||
|
}
|
||||||
|
|
||||||
|
bizParmaJsonBytes, err := json.Marshal(bizParma)
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
encryptBody, err := cmb.EncryptBody(&cmb.Encrypts{SoaPubKey: "0416445bc16cbf42e47002ad9fe7c7af67d902b48be1eb69b98f6a006b0918630e1127f5f2fff83b2ecb30fc7fd72c34c33f37c7c355dffde3589f66800f0036ca", JsonParam: string(bizParmaJsonBytes)})
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req := &v1.CmbRequest{
|
||||||
|
Mid: "d6fdd78b6fd13a808818286b9cad9687",
|
||||||
|
Aid: "5efaa21263b94f669a1c90ed0279df20",
|
||||||
|
Date: time.Now().Format("20060102150405"),
|
||||||
|
Random: string(cmb.RandomBytes(16)),
|
||||||
|
KeyAlias: "CO_PUB_KEY_SM2",
|
||||||
|
CmbKeyAlias: "SM2_CMBLIFE",
|
||||||
|
EncryptBody: encryptBody,
|
||||||
|
Sign: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
str := fmt.Sprintf("%s?%s", vo.CmbNotifyFuncNameUpdateCodeStatusForMulti, cmb.SortStructStr(req))
|
||||||
|
|
||||||
|
sign, err := cmb.SignBody(str, "8d39ff3d2559258c163f4510f082727f51531e1953ab203d5ab1ea4a6d94fd73")
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
req.Sign = sign
|
||||||
|
|
||||||
|
kvRows := helper.SortStructFieldsByKey(req)
|
||||||
|
|
||||||
|
uv := url.Values{}
|
||||||
|
|
||||||
|
for _, kv := range kvRows {
|
||||||
|
uv.Set(kv.Key, fmt.Sprintf("%v", kv.Value))
|
||||||
|
}
|
||||||
|
|
||||||
|
h := http.Header{
|
||||||
|
"Content-Type": []string{"application/x-www-form-urlencoded"},
|
||||||
|
}
|
||||||
|
|
||||||
|
uri := "https://sandbox.cdcc.cmbchina.com/AccessGateway/transIn/updateCodeStatusForMulti.json"
|
||||||
|
r := uri + "?" + uv.Encode()
|
||||||
|
|
||||||
|
respHeader, bodyBytes, err := request.Post(context.Background(), r, nil, request.WithHeaders(h), request.WithTimeout(time.Second*10))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Logf("请求地址:%s", uri)
|
||||||
|
t.Logf("业务参数:%s", string(bizParmaJsonBytes))
|
||||||
|
t.Logf("请求响应体:%s", string(bodyBytes))
|
||||||
|
t.Logf("请求响应头:%+v", respHeader)
|
||||||
|
}
|
||||||
|
|
@ -10,6 +10,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"time"
|
"time"
|
||||||
|
"voucher/internal/biz/businesserr"
|
||||||
"voucher/internal/biz/do"
|
"voucher/internal/biz/do"
|
||||||
"voucher/internal/conf"
|
"voucher/internal/conf"
|
||||||
"voucher/internal/data"
|
"voucher/internal/data"
|
||||||
|
|
@ -33,14 +34,13 @@ var bc2 = &conf.Bootstrap{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func SendCoupon() {
|
func SendCoupon() error {
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
dir, err := os.Getwd()
|
dir, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("os.Getwd() error = %v", err)
|
return fmt.Errorf("os.Getwd() error = %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
parentDir := filepath.Dir(dir)
|
parentDir := filepath.Dir(dir)
|
||||||
|
|
||||||
|
|
@ -52,8 +52,7 @@ func SendCoupon() {
|
||||||
}
|
}
|
||||||
client, err := data.GetClient(ctx, server)
|
client, err := data.GetClient(ctx, server)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
return fmt.Errorf("data.GetClient(ctx, server) error = %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
req := cashcoupons.SendCouponRequest{
|
req := cashcoupons.SendCouponRequest{
|
||||||
|
|
@ -62,7 +61,7 @@ func SendCoupon() {
|
||||||
Appid: core.String("wxd27e255810842ba8"),
|
Appid: core.String("wxd27e255810842ba8"),
|
||||||
Openid: core.String("o3dEt5b_1lFtKc-aAT3tiYjJIGwk"),
|
Openid: core.String("o3dEt5b_1lFtKc-aAT3tiYjJIGwk"),
|
||||||
StockId: core.String("21502886"),
|
StockId: core.String("21502886"),
|
||||||
StockCreatorMchid: core.String("1652465541"),
|
StockCreatorMchid: core.String("16524655411111"),
|
||||||
}
|
}
|
||||||
fmt.Printf("\nreq:%+v", req)
|
fmt.Printf("\nreq:%+v", req)
|
||||||
|
|
||||||
|
|
@ -71,15 +70,26 @@ func SendCoupon() {
|
||||||
resp, result, err := svc.SendCoupon(ctx, req)
|
resp, result, err := svc.SendCoupon(ctx, req)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
|
||||||
return
|
bodyBytes, err := io.ReadAll(result.Response.Body)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("读取微信错误返回body报错:%s", err.Error())
|
||||||
|
}
|
||||||
|
fmt.Printf("\nbodyBytes:%s\n", string(bodyBytes))
|
||||||
|
|
||||||
|
var beer *businesserr.BusinessErr
|
||||||
|
if err = json.Unmarshal(bodyBytes, &beer); err != nil {
|
||||||
|
return fmt.Errorf("微信错误返回body解析报错,body:%s,err:%s", string(bodyBytes), err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return beer
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("\nresp:%+v\n result:%+v", resp, result)
|
fmt.Printf("\nresp:%+v\n result:%+v", resp, result)
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func QueryCoupon() {
|
func QueryCoupon(appId, openId, couponId string) {
|
||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
|
|
@ -102,10 +112,6 @@ func QueryCoupon() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
appId := "wx619991cc795028f5"
|
|
||||||
openId := "oSNb4fnWoktz7YVNIXE5bvoB3-1w"
|
|
||||||
couponId := "106048490308"
|
|
||||||
|
|
||||||
req := cashcoupons.QueryCouponRequest{
|
req := cashcoupons.QueryCouponRequest{
|
||||||
CouponId: core.String(couponId),
|
CouponId: core.String(couponId),
|
||||||
Appid: core.String(appId),
|
Appid: core.String(appId),
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
"voucher/internal/biz/businesserr"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_SendCoupon(t *testing.T) {
|
func Test_SendCoupon(t *testing.T) {
|
||||||
|
|
@ -14,7 +15,11 @@ func Test_SendCoupon(t *testing.T) {
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
SendCoupon()
|
if err := SendCoupon(); err != nil {
|
||||||
|
if ee, ok := err.(*businesserr.BusinessErr); ok {
|
||||||
|
t.Errorf("errorcode:%s,errmsg:%s", ee.Code, ee.Message)
|
||||||
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -22,14 +27,26 @@ func Test_SendCoupon(t *testing.T) {
|
||||||
func Test_QueryCoupon(t *testing.T) {
|
func Test_QueryCoupon(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
|
appId string
|
||||||
|
openId string
|
||||||
|
couponId string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "查询券订单信息",
|
name: "查询券订单信息",
|
||||||
|
appId: "wx619991cc795028f5",
|
||||||
|
openId: "oSNb4fnRWr7HdgbDOO8TD66LXofE",
|
||||||
|
couponId: "147089832782",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "查询券订单信息",
|
||||||
|
appId: "wx619991cc795028f5",
|
||||||
|
openId: "oSNb4foo87dBNx1D9KTH-bB-G6YA",
|
||||||
|
couponId: "147094824239",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
QueryCoupon()
|
QueryCoupon(tt.appId, tt.openId, tt.couponId)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -64,3 +81,34 @@ func Test_QueryCallback(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestGetErrorByDescription(t *testing.T) {
|
||||||
|
//e := &businesserr.ErrBody{
|
||||||
|
// Code: "INVALID_REQUEST",
|
||||||
|
// Message: "活动已结束或未激活",
|
||||||
|
//}
|
||||||
|
//t.Log(e.GetWechatError())
|
||||||
|
//
|
||||||
|
//err := gorm.ErrRecordNotFound
|
||||||
|
//
|
||||||
|
//t.Log(errors.Is(err, gorm.ErrRecordNotFound))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestErr(t *testing.T) {
|
||||||
|
//e := &businesserr.ErrBody{
|
||||||
|
// Code: "INVALID_REQUEST",
|
||||||
|
// Message: "活动已结束或未激活",
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//se := errors2.FromError(e.GetWechatError())
|
||||||
|
//
|
||||||
|
//t.Log(se.Reason)
|
||||||
|
//t.Log(se.Message)
|
||||||
|
//
|
||||||
|
//e2 := errors.New("活动已结束或未激活")
|
||||||
|
//se2 := errors2.FromError(e2)
|
||||||
|
//
|
||||||
|
//t.Log(se2.Reason)
|
||||||
|
//t.Log(len(se2.Reason))
|
||||||
|
//t.Log(se2.Message)
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue