From 4234ddf34d3fe4a33b2b09b59e12550a84674bfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=AD=90=E9=93=AD?= Date: Tue, 16 Sep 2025 09:56:00 +0800 Subject: [PATCH 001/144] order_notify --- internal/biz/wechat_notify.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index 3aa7e79..f276fb5 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -117,7 +117,7 @@ func (this *VoucherBiz) expired(ctx context.Context, order *bo.OrderBo) error { func (this *VoucherBiz) notify(ctx context.Context, order *bo.OrderBo) error { - if order.ActivityId == "" { + if order.ActivityId != "" { return nil // 多笔立减活动,不做通知(?不知道有没有核销通知,多笔核销情况未知) } From c7a16b3ae98ff021edd1b8726f473161ed0ec7ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=AD=90=E9=93=AD?= Date: Tue, 16 Sep 2025 10:14:44 +0800 Subject: [PATCH 002/144] order_notify --- configs/config.yaml | 6 ++ internal/biz/do/rds_mq.go | 9 +++ internal/biz/order_notify_retry.go | 56 ++++++++++++++ internal/conf/conf.pb.go | 100 ++++++++++++++----------- internal/conf/conf.proto | 1 + internal/server/http.go | 1 + internal/service/order_norify_retry.go | 45 +++++++++++ internal/service/script.go | 23 ++++++ 8 files changed, 198 insertions(+), 43 deletions(-) create mode 100644 internal/biz/order_notify_retry.go create mode 100644 internal/service/order_norify_retry.go diff --git a/configs/config.yaml b/configs/config.yaml index 68d4e6c..31a43e5 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -114,6 +114,12 @@ rdsMQ: numWorkers: 1 #协程数量,不配置默认为10 waitTime: 1s #处理完成后等待时间 isOpen: false #是否启动消费 true/false + orderNotifyRetry: + name: "orderNotifyRetry" + retryNum: 1 #重试次数 + numWorkers: 1 #协程数量,不配置默认为10 + waitTime: 1s #处理完成后等待时间 + isOpen: false #是否启动消费 true/false aliYunSms: accessKeyId: diff --git a/internal/biz/do/rds_mq.go b/internal/biz/do/rds_mq.go index 6daf51e..e9c17f6 100644 --- a/internal/biz/do/rds_mq.go +++ b/internal/biz/do/rds_mq.go @@ -1,5 +1,14 @@ package do +type OrderNotifyRetry struct { + ProductNo string `json:"product_no"` + BatchNo string `json:"batch_no"` + StartTime string `json:"start_time"` + EndTime string `json:"end_time"` + OrderNo string `json:"order_no"` + OutBizNo string `json:"out_biz_no"` +} + type WechatQuery struct { ProductNo string `json:"product_no"` BatchNo string `json:"batch_no"` diff --git a/internal/biz/order_notify_retry.go b/internal/biz/order_notify_retry.go new file mode 100644 index 0000000..b7af66c --- /dev/null +++ b/internal/biz/order_notify_retry.go @@ -0,0 +1,56 @@ +package biz + +import ( + "context" + "encoding/json" + "fmt" + "github.com/go-kratos/kratos/v2/transport/http" + "time" + "voucher/internal/biz/do" +) + +func (this *VoucherBiz) PushOrderNotifyRetry(ctx http.Context, req *do.OrderNotifyRetry) error { + + queue := this.bc.RdsMQ.GetOrderNotifyRetry() + if queue == nil { + return fmt.Errorf("队列不存在") + } + + msg, err := json.Marshal(req) + if err != nil { + return err + } + + strMsg := string(msg) + + _, err = this.rdb.Rdb.RPush(ctx, queue.Name, strMsg).Result() + if err != nil { + return fmt.Errorf("添加到队列失败:%v", err) + } + + return nil +} + +func (this *VoucherBiz) OrderNotifyRetry(ctx context.Context, msg string) error { + + var req *do.OrderNotifyRetry + + if err := json.Unmarshal([]byte(msg), &req); err != nil { + return err + } + + if req.StartTime == "" || req.EndTime == "" { + return fmt.Errorf("start_time or end_time is empty") + } + + start, err := time.Parse(time.DateTime, req.StartTime) + if err != nil { + return err + } + end, err := time.Parse(time.DateTime, req.EndTime) + if err != nil { + return err + } + + return this.timeSliceQuery(ctx, start, end) +} diff --git a/internal/conf/conf.pb.go b/internal/conf/conf.pb.go index da881e7..f4b735c 100644 --- a/internal/conf/conf.pb.go +++ b/internal/conf/conf.pb.go @@ -868,6 +868,7 @@ type RdsMQ struct { WechatTimeSliceQuery *RdsMQ_Queue `protobuf:"bytes,2,opt,name=wechatTimeSliceQuery,proto3" json:"wechatTimeSliceQuery,omitempty"` WechatRetry *RdsMQ_Queue `protobuf:"bytes,3,opt,name=wechatRetry,proto3" json:"wechatRetry,omitempty"` RetryNotify *RdsMQ_Queue `protobuf:"bytes,4,opt,name=retryNotify,proto3" json:"retryNotify,omitempty"` + OrderNotifyRetry *RdsMQ_Queue `protobuf:"bytes,5,opt,name=orderNotifyRetry,proto3" json:"orderNotifyRetry,omitempty"` } func (x *RdsMQ) Reset() { @@ -930,6 +931,13 @@ func (x *RdsMQ) GetRetryNotify() *RdsMQ_Queue { return nil } +func (x *RdsMQ) GetOrderNotifyRetry() *RdsMQ_Queue { + if x != nil { + return x.OrderNotifyRetry + } + return nil +} + type AliYunSms struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1679,7 +1687,7 @@ var file_conf_conf_proto_rawDesc = []byte{ 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x72, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0xbe, 0x03, 0x0a, 0x05, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x12, 0x3d, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x87, 0x04, 0x0a, 0x05, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x12, 0x3d, 0x0a, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, @@ -1696,35 +1704,40 @@ var file_conf_conf_proto_rawDesc = []byte{ 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, - 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x1a, 0xa6, 0x01, 0x0a, - 0x05, 0x51, 0x75, 0x65, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, - 0x4f, 0x70, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4f, 0x70, - 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x75, 0x6d, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x75, 0x6d, 0x12, 0x1e, - 0x0a, 0x0a, 0x6e, 0x75, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x0a, 0x6e, 0x75, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x35, - 0x0a, 0x08, 0x77, 0x61, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x77, 0x61, 0x69, - 0x74, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xb9, 0x01, 0x0a, 0x09, 0x41, 0x6c, 0x69, 0x59, 0x75, 0x6e, - 0x53, 0x6d, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, - 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, - 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, - 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, - 0x69, 0x67, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, - 0x69, 0x67, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, - 0x67, 0x22, 0x3a, 0x0a, 0x04, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x75, 0x73, - 0x69, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x75, 0x73, - 0x69, 0x6e, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, - 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x17, 0x5a, - 0x15, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2f, 0x63, 0x70, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, - 0x66, 0x3b, 0x63, 0x6f, 0x6e, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x47, 0x0a, 0x10, + 0x6f, 0x72, 0x64, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x74, 0x72, 0x79, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, + 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, + 0x65, 0x75, 0x65, 0x52, 0x10, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, + 0x52, 0x65, 0x74, 0x72, 0x79, 0x1a, 0xa6, 0x01, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x75, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, + 0x65, 0x74, 0x72, 0x79, 0x4e, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x72, + 0x65, 0x74, 0x72, 0x79, 0x4e, 0x75, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x75, 0x6d, 0x57, 0x6f, + 0x72, 0x6b, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6e, 0x75, 0x6d, + 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x35, 0x0a, 0x08, 0x77, 0x61, 0x69, 0x74, 0x54, + 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, + 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x77, 0x61, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xb9, + 0x01, 0x0a, 0x09, 0x41, 0x6c, 0x69, 0x59, 0x75, 0x6e, 0x53, 0x6d, 0x73, 0x12, 0x20, 0x0a, 0x0b, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x28, + 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, + 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, + 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x4e, 0x61, 0x6d, 0x65, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x4e, 0x61, 0x6d, 0x65, + 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x57, 0x61, 0x72, 0x6e, + 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, + 0x61, 0x74, 0x65, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x0a, 0x04, 0x4c, 0x6f, + 0x67, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x75, 0x73, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x75, 0x73, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x12, 0x16, + 0x0a, 0x06, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x17, 0x5a, 0x15, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, + 0x72, 0x2f, 0x63, 0x70, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x3b, 0x63, 0x6f, 0x6e, 0x66, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1784,19 +1797,20 @@ var file_conf_conf_proto_depIdxs = []int32{ 19, // 17: voucher.config.RdsMQ.wechatTimeSliceQuery:type_name -> voucher.config.RdsMQ.Queue 19, // 18: voucher.config.RdsMQ.wechatRetry:type_name -> voucher.config.RdsMQ.Queue 19, // 19: voucher.config.RdsMQ.retryNotify:type_name -> voucher.config.RdsMQ.Queue - 20, // 20: voucher.config.Server.HTTP.timeout:type_name -> google.protobuf.Duration - 20, // 21: voucher.config.Data.Database.maxLifetime:type_name -> google.protobuf.Duration - 20, // 22: voucher.config.Data.Redis.readTimeout:type_name -> google.protobuf.Duration - 20, // 23: voucher.config.Data.Redis.writeTimeout:type_name -> google.protobuf.Duration - 20, // 24: voucher.config.Data.Redis.connMaxIdleTime:type_name -> google.protobuf.Duration - 4, // 25: voucher.config.RocketMQ.EventMapEntry.value:type_name -> voucher.config.EventMap - 17, // 26: voucher.config.Cron.CommandMapEntry.value:type_name -> voucher.config.Cron.CommandMap - 20, // 27: voucher.config.RdsMQ.Queue.waitTime:type_name -> google.protobuf.Duration - 28, // [28:28] is the sub-list for method output_type - 28, // [28:28] is the sub-list for method input_type - 28, // [28:28] is the sub-list for extension type_name - 28, // [28:28] is the sub-list for extension extendee - 0, // [0:28] is the sub-list for field type_name + 19, // 20: voucher.config.RdsMQ.orderNotifyRetry:type_name -> voucher.config.RdsMQ.Queue + 20, // 21: voucher.config.Server.HTTP.timeout:type_name -> google.protobuf.Duration + 20, // 22: voucher.config.Data.Database.maxLifetime:type_name -> google.protobuf.Duration + 20, // 23: voucher.config.Data.Redis.readTimeout:type_name -> google.protobuf.Duration + 20, // 24: voucher.config.Data.Redis.writeTimeout:type_name -> google.protobuf.Duration + 20, // 25: voucher.config.Data.Redis.connMaxIdleTime:type_name -> google.protobuf.Duration + 4, // 26: voucher.config.RocketMQ.EventMapEntry.value:type_name -> voucher.config.EventMap + 17, // 27: voucher.config.Cron.CommandMapEntry.value:type_name -> voucher.config.Cron.CommandMap + 20, // 28: voucher.config.RdsMQ.Queue.waitTime:type_name -> google.protobuf.Duration + 29, // [29:29] is the sub-list for method output_type + 29, // [29:29] is the sub-list for method input_type + 29, // [29:29] is the sub-list for extension type_name + 29, // [29:29] is the sub-list for extension extendee + 0, // [0:29] is the sub-list for field type_name } func init() { file_conf_conf_proto_init() } diff --git a/internal/conf/conf.proto b/internal/conf/conf.proto index 58ee42f..b31dadd 100644 --- a/internal/conf/conf.proto +++ b/internal/conf/conf.proto @@ -134,6 +134,7 @@ message RdsMQ { Queue wechatTimeSliceQuery = 2; Queue wechatRetry = 3; Queue retryNotify = 4; + Queue orderNotifyRetry = 5; } message AliYunSms { diff --git a/internal/server/http.go b/internal/server/http.go index 0c52e1b..2bc1968 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -36,6 +36,7 @@ func NewHTTPServer( return ctx.String(http2.StatusOK, "pong") }) + srv.Route("/voucher/").POST("orderNotifyRetry", cmb.OrderNotifyRetry) srv.Route("/voucher/").POST("notifyRetry/{id}", cmb.NotifyRetry) srv.Route("/voucher/").POST("queryOrder/{order_no}", cmb.QueryOrder) srv.Route("/voucher/").POST("queryStock/{product_no}", cmb.QueryStock) diff --git a/internal/service/order_norify_retry.go b/internal/service/order_norify_retry.go new file mode 100644 index 0000000..89f6c2e --- /dev/null +++ b/internal/service/order_norify_retry.go @@ -0,0 +1,45 @@ +package service + +import ( + "context" + "fmt" + "github.com/go-kratos/kratos/v2/log" + "voucher/internal/pkg/rdsmq" +) + +func (s *VoucherService) GetOrderNotifyRetryConfig() *rdsmq.ConsumeConfig { + + queue := s.bc.RdsMQ.GetOrderNotifyRetry() + if queue == nil { + return nil + } + + if !queue.GetIsOpen() { + log.Warn(fmt.Sprintf("[%s]RdsMQ is not open", queue.Name)) + return nil + } + + return &rdsmq.ConsumeConfig{ + Rdb: s.rdb.Rdb, + QueueName: queue.Name, + NumWorkers: queue.NumWorkers, + WaitTime: queue.GetWaitTime().AsDuration(), + RetryNum: queue.RetryNum, + Fn: s.HandleOrderNotifyRetry, + Logger: s.logHelper, + } +} + +func (s *VoucherService) HandleOrderNotifyRetry(ctx context.Context, msg string) error { + + if msg == "" { + s.logHelper.Errorf("RdsMQ keySend error: msg is empty") + return nil + } + + if err := s.VoucherBiz.OrderNotifyRetry(ctx, msg); err != nil { + s.logHelper.Error(err) + } + + return nil +} diff --git a/internal/service/script.go b/internal/service/script.go index 2a11c8a..e1ee1f2 100644 --- a/internal/service/script.go +++ b/internal/service/script.go @@ -11,6 +11,29 @@ import ( "voucher/internal/biz/do" ) +func (this *CmbService) OrderNotifyRetry(ctx http.Context) error { + + bodyBytes, err := io.ReadAll(ctx.Request().Body) + if err != nil { + return err + } + + var req *do.OrderNotifyRetry + if err = json.Unmarshal(bodyBytes, &req); err != nil { + return err + } + + if req == nil { + return fmt.Errorf("req is empty") + } + + if req.StartTime == "" || req.EndTime == "" { + return fmt.Errorf("start_time or end_time is empty") + } + + return this.VoucherBiz.PushOrderNotifyRetry(ctx, req) +} + func (this *CmbService) NotifyRetry(ctx http.Context) error { id := ctx.Vars().Get("id") if id == "" { From 40f51dd4dd0dfe4a37dfdf8e2092af3f0ac2cbcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=AD=90=E9=93=AD?= Date: Tue, 16 Sep 2025 10:15:03 +0800 Subject: [PATCH 003/144] order_notify --- configs/config_test.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/configs/config_test.yaml b/configs/config_test.yaml index 4505078..fb616a2 100644 --- a/configs/config_test.yaml +++ b/configs/config_test.yaml @@ -103,6 +103,12 @@ rdsMQ: numWorkers: 1 #协程数量,不配置默认为10 waitTime: 1s #处理完成后等待时间 isOpen: false #是否启动消费 true/false + orderNotifyRetry: + name: "orderNotifyRetry" + retryNum: 1 #重试次数 + numWorkers: 1 #协程数量,不配置默认为10 + waitTime: 1s #处理完成后等待时间 + isOpen: false #是否启动消费 true/false aliYunSms: accessKeyId: LTAI5tM1X4HuqUwT8D74qXAH From 9e984cdb5a66693e4d8945c80e1d3ab3db41500e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=AD=90=E9=93=AD?= Date: Tue, 16 Sep 2025 10:22:18 +0800 Subject: [PATCH 004/144] order_notify --- internal/server/rds_consume.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/internal/server/rds_consume.go b/internal/server/rds_consume.go index 9a888f4..52e2805 100644 --- a/internal/server/rds_consume.go +++ b/internal/server/rds_consume.go @@ -34,14 +34,18 @@ func NewRdbConsumer( manager.Add(cf1) } - if cf2 := voucherService.GetWechatRetryConfig(); cf2 != nil { - manager.Add(cf2) - } + //if cf2 := voucherService.GetWechatRetryConfig(); cf2 != nil { + // manager.Add(cf2) + //} if cf3 := voucherService.GetRetryNotifyConfig(); cf3 != nil { manager.Add(cf3) } + if cf4 := voucherService.GetOrderNotifyRetryConfig(); cf4 != nil { + manager.Add(cf4) + } + return &RdbConsumer{hLog: hLog, conf: conf, manager: manager} } From 530652a7d25a83fb801d27d62242b9e36e0a009a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=AD=90=E9=93=AD?= Date: Tue, 16 Sep 2025 10:40:44 +0800 Subject: [PATCH 005/144] order_notify --- internal/biz/cron_notice.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/biz/cron_notice.go b/internal/biz/cron_notice.go index 279e954..1b76846 100644 --- a/internal/biz/cron_notice.go +++ b/internal/biz/cron_notice.go @@ -163,9 +163,9 @@ func (this *VoucherBiz) notice(ctx context.Context, order *bo.OrderBo, useNum, s return err } - if order.Status == status { - return nil // 券状态未改变,忽略不处理 - } + //if order.Status == status { + // return nil // 券状态未改变,忽略不处理 + //} order.Status = status From d2df9235f6b9c27388b1a0f468f8e5d462b943a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=AD=90=E9=93=AD?= Date: Wed, 24 Sep 2025 17:01:38 +0800 Subject: [PATCH 006/144] order_notify --- internal/biz/cron_notice.go | 6 +++--- internal/data/wechatrepoimpl/cpn.go | 6 ++++++ internal/data/wechatrepoimpl/cpn_status.go | 6 ++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/internal/biz/cron_notice.go b/internal/biz/cron_notice.go index 1b76846..279e954 100644 --- a/internal/biz/cron_notice.go +++ b/internal/biz/cron_notice.go @@ -163,9 +163,9 @@ func (this *VoucherBiz) notice(ctx context.Context, order *bo.OrderBo, useNum, s return err } - //if order.Status == status { - // return nil // 券状态未改变,忽略不处理 - //} + if order.Status == status { + return nil // 券状态未改变,忽略不处理 + } order.Status = status diff --git a/internal/data/wechatrepoimpl/cpn.go b/internal/data/wechatrepoimpl/cpn.go index f40cbfa..5fd9078 100644 --- a/internal/data/wechatrepoimpl/cpn.go +++ b/internal/data/wechatrepoimpl/cpn.go @@ -122,6 +122,12 @@ func (c *CpnRepoImpl) Query(ctx context.Context, orderWechat *bo.OrderBo) (vo.Or return 0, err } + cpnStatus := CpnStatus(*resp.Status) + + if cpnStatus.IsRevoked() { + + } + return CpnStatus(*resp.Status).GetStatus() } diff --git a/internal/data/wechatrepoimpl/cpn_status.go b/internal/data/wechatrepoimpl/cpn_status.go index 16351ee..d9a72bf 100644 --- a/internal/data/wechatrepoimpl/cpn_status.go +++ b/internal/data/wechatrepoimpl/cpn_status.go @@ -13,12 +13,14 @@ const ( CpnStatusAvailable = "SENDED" CpnStatusUsed = "USED" CpnStatusExpired = "EXPIRED" + CpnStatusRevoked = "REVOKED" ) var CpnStatusTextMap = map[CpnStatus]string{ CpnStatusAvailable: "可用", CpnStatusUsed: "已实扣", CpnStatusExpired: "已过期", + CpnStatusRevoked: "已失效", } var CpnStatusMap = map[CpnStatus]vo.OrderStatus{ @@ -34,6 +36,10 @@ func (o CpnStatus) GetText() string { return "未知" } +func (o CpnStatus) IsRevoked() bool { + return o == CpnStatusRevoked +} + func (o CpnStatus) GetStatus() (vo.OrderStatus, error) { if resultStatus, ok := CpnStatusMap[o]; ok { return resultStatus, nil From d6df1e96bb9a705bc6a1726990a7bc204b0ec618 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=AD=90=E9=93=AD?= Date: Wed, 24 Sep 2025 17:05:25 +0800 Subject: [PATCH 007/144] order_notify --- internal/data/wechatrepoimpl/cpn_status.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/data/wechatrepoimpl/cpn_status.go b/internal/data/wechatrepoimpl/cpn_status.go index d9a72bf..eea61cf 100644 --- a/internal/data/wechatrepoimpl/cpn_status.go +++ b/internal/data/wechatrepoimpl/cpn_status.go @@ -14,6 +14,7 @@ const ( CpnStatusUsed = "USED" CpnStatusExpired = "EXPIRED" CpnStatusRevoked = "REVOKED" + CpnStatusRecover = "RECOVER" ) var CpnStatusTextMap = map[CpnStatus]string{ @@ -21,6 +22,7 @@ var CpnStatusTextMap = map[CpnStatus]string{ CpnStatusUsed: "已实扣", CpnStatusExpired: "已过期", CpnStatusRevoked: "已失效", + CpnStatusRecover: "已回收", } var CpnStatusMap = map[CpnStatus]vo.OrderStatus{ @@ -44,5 +46,5 @@ func (o CpnStatus) GetStatus() (vo.OrderStatus, error) { if resultStatus, ok := CpnStatusMap[o]; ok { return resultStatus, nil } - return 0, fmt.Errorf("CpnStatus[%s]未定义", o) + return 0, fmt.Errorf("CpnStatus[%s-%s]未定义", o, o.GetText()) } From 23bd19bc9987f050d8b002e43958fdcd51f095e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E5=AD=90=E9=93=AD?= Date: Fri, 26 Sep 2025 10:02:04 +0800 Subject: [PATCH 008/144] order_notify --- internal/biz/wechat_notify.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index f276fb5..32d8bf4 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -56,10 +56,11 @@ func (this *VoucherBiz) getOrder(ctx context.Context, req *bo.WechatVoucherNotif if errors.Is(err, gorm.ErrRecordNotFound) { - return nil, fmt.Errorf("微信回调消费,订单不存在,StockCreatorMchid:%s,StockID:%s,CouponID:%s,CreateTime:%s", + return nil, fmt.Errorf("微信回调消费,订单不存在,StockCreatorMchid:%s,StockID:%s,CouponID:%s,TransactionID:%s,CreateTime:%s", req.PlainText.StockCreatorMchid, req.PlainText.StockID, req.PlainText.CouponID, + req.PlainText.ConsumeInformation.TransactionID, req.PlainText.CreateTime, ) } From 5a3fefdd196cf62ac31cd9a0a34365f0bf31dc80 Mon Sep 17 00:00:00 2001 From: ziming Date: Tue, 14 Oct 2025 13:43:18 +0800 Subject: [PATCH 009/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E5=95=86=E6=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/coupon.go | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/test/coupon.go b/test/coupon.go index eddbb02..d899b10 100644 --- a/test/coupon.go +++ b/test/coupon.go @@ -48,12 +48,12 @@ func SendCoupon() { } req := cashcoupons.SendCouponRequest{ - OutRequestNo: core.String("LQ1948534036766040066"), + OutRequestNo: core.String("FZQM1948534036766040066"), // {CouponId:129623470711} // 微信为发券方商户分配的公众账号ID,接口传入的所有appid应该为公众号的appid(在mp.weixin.qq.com申请的),不能为APP的appid(在open.weixin.qq.com申请的)。 Appid: core.String("wxd27e255810842ba8"), Openid: core.String("o3dEt5cA8jt3Kz5wNzAO6-3YQHsE"), - StockId: core.String("20391098"), - StockCreatorMchid: core.String("1652465541"), + StockId: core.String("21104160"), + StockCreatorMchid: core.String("1715349578"), } fmt.Printf("\nreq:%+v", req) @@ -115,9 +115,13 @@ func QueryCoupon() { // Openid: core.String(openId), //} - appId := "wx5d3e839568f24b2b" - openId := "ocuH-0Nymo4sJLRNabIBbg9H2XCo" - couponId := "116076813524" + //appId := "wx5d3e839568f24b2b" + //openId := "ocuH-0Nymo4sJLRNabIBbg9H2XCo" + //couponId := "116076813524" + + appId := "wxd27e255810842ba8" + openId := "o3dEt5cA8jt3Kz5wNzAO6-3YQHsE" + couponId := "129623470711" req := cashcoupons.QueryCouponRequest{ CouponId: core.String(couponId), @@ -170,8 +174,8 @@ func QueryProduct() { } req := cashcoupons.QueryStockRequest{ - StockId: core.String("20847510"), - StockCreatorMchid: core.String("1652465541"), + StockId: core.String("21104160"), + StockCreatorMchid: core.String("1715349578"), } svc := cashcoupons.StockApiService{Client: client} From 482e8d7f82d5899879da1406b8130f878d2a38b7 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 16 Oct 2025 11:35:55 +0800 Subject: [PATCH 010/144] order by --- internal/data/repoimpl/order.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/data/repoimpl/order.go b/internal/data/repoimpl/order.go index 0e3c64a..0062dd6 100644 --- a/internal/data/repoimpl/order.go +++ b/internal/data/repoimpl/order.go @@ -149,7 +149,8 @@ func (p *OrderRepoImpl) FindInBatches(ctx context.Context, req *bo.FindInBatches Where("activity_id = ''"). Where("`status` IN (?)", []uint8{vo.OrderStatusSuccess.GetValue(), vo.OrderStatusUse.GetValue()}). Where("receive_success_time BETWEEN ? AND ?", req.StartTime, req.EndTime). - FindInBatches(&results, 100, func(tx *gorm.DB, batch int) error { + Order(""). // 显式清除排序,移除默认的 ORDER BY + FindInBatches(&results, 1000, func(tx *gorm.DB, batch int) error { // tx.RowsAffected 提供当前批处理中记录的计数(the count of records in the current batch) // 'batch' 变量表示当前批号(the current batch number) // 返回 error 将阻止更多的批处理 From 93873c6fd6d7db5f738914c84fea85f39e73b246 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 16 Oct 2025 11:37:06 +0800 Subject: [PATCH 011/144] order by --- internal/data/repoimpl/order.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/data/repoimpl/order.go b/internal/data/repoimpl/order.go index 0062dd6..b62e9ea 100644 --- a/internal/data/repoimpl/order.go +++ b/internal/data/repoimpl/order.go @@ -55,6 +55,8 @@ func (p *OrderRepoImpl) SpecifyFindInBatches(ctx context.Context, req *bo.FindIn tx = tx.Where("voucher_no IN (?)", req.VoucherNos) } + tx.Order("") // 显式清除排序,移除默认的 ORDER BY + var results = make([]*model.Order, 0) result := tx.FindInBatches(&results, 100, func(tx *gorm.DB, batch int) error { @@ -93,6 +95,8 @@ func (p *OrderRepoImpl) FinSucByStockIdInBatches(ctx context.Context, req *do.We var results = make([]*model.Order, 0) + tx.Order("") // 显式清除排序,移除默认的 ORDER BY + result := tx.FindInBatches(&results, 100, func(tx *gorm.DB, batch int) error { return fun(ctx, p.ToBos(results)) @@ -112,6 +116,7 @@ func (p *OrderRepoImpl) FinFailByStockIdInBatches(ctx context.Context, batchNo s result := p.DB(ctx). Where("batch_no = ?", batchNo). Where("`status` = ?", vo.OrderStatusFail.GetValue()). + Order(""). // 显式清除排序,移除默认的 ORDER BY FindInBatches(&results, 100, func(tx *gorm.DB, batch int) error { return fun(ctx, p.ToBos(results)) }) @@ -130,6 +135,7 @@ func (p *OrderRepoImpl) FindIngInBatches(ctx context.Context, fun func(ctx conte result := p.DB(ctx). Where("`status` = ?", vo.OrderStatusIng.GetValue()). Limit(20). + Order(""). // 显式清除排序,移除默认的 ORDER BY FindInBatches(&results, 10, func(tx *gorm.DB, batch int) error { return fun(ctx, p.ToBos(results)) }) From a01f04a949578901511dfbf1d646874efb10cc62 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 16 Oct 2025 12:01:26 +0800 Subject: [PATCH 012/144] order by --- internal/data/gorm.go | 4 ++-- internal/data/repoimpl/order.go | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/data/gorm.go b/internal/data/gorm.go index 3ef0314..1459afe 100644 --- a/internal/data/gorm.go +++ b/internal/data/gorm.go @@ -36,8 +36,8 @@ func db(data *conf.Data_Database) *gorm.DB { panic("failed to gormDB " + err.Error()) } - sqlDB.SetMaxIdleConns(100) - sqlDB.SetMaxOpenConns(1000) + sqlDB.SetMaxIdleConns(50) + sqlDB.SetMaxOpenConns(200) return gormDB } diff --git a/internal/data/repoimpl/order.go b/internal/data/repoimpl/order.go index b62e9ea..830974b 100644 --- a/internal/data/repoimpl/order.go +++ b/internal/data/repoimpl/order.go @@ -55,7 +55,7 @@ func (p *OrderRepoImpl) SpecifyFindInBatches(ctx context.Context, req *bo.FindIn tx = tx.Where("voucher_no IN (?)", req.VoucherNos) } - tx.Order("") // 显式清除排序,移除默认的 ORDER BY + tx.Order("receive_success_time asc") // 显式清除排序,移除默认的 ORDER BY var results = make([]*model.Order, 0) @@ -95,9 +95,9 @@ func (p *OrderRepoImpl) FinSucByStockIdInBatches(ctx context.Context, req *do.We var results = make([]*model.Order, 0) - tx.Order("") // 显式清除排序,移除默认的 ORDER BY + tx.Order("receive_success_time asc") // 显式清除排序,移除默认的 ORDER BY - result := tx.FindInBatches(&results, 100, func(tx *gorm.DB, batch int) error { + result := tx.FindInBatches(&results, 1000, func(tx *gorm.DB, batch int) error { return fun(ctx, p.ToBos(results)) }) @@ -116,7 +116,7 @@ func (p *OrderRepoImpl) FinFailByStockIdInBatches(ctx context.Context, batchNo s result := p.DB(ctx). Where("batch_no = ?", batchNo). Where("`status` = ?", vo.OrderStatusFail.GetValue()). - Order(""). // 显式清除排序,移除默认的 ORDER BY + Order("receive_success_time asc"). // 显式清除排序,移除默认的 ORDER BY FindInBatches(&results, 100, func(tx *gorm.DB, batch int) error { return fun(ctx, p.ToBos(results)) }) @@ -135,7 +135,7 @@ func (p *OrderRepoImpl) FindIngInBatches(ctx context.Context, fun func(ctx conte result := p.DB(ctx). Where("`status` = ?", vo.OrderStatusIng.GetValue()). Limit(20). - Order(""). // 显式清除排序,移除默认的 ORDER BY + Order("receive_success_time asc"). // 显式清除排序,移除默认的 ORDER BY FindInBatches(&results, 10, func(tx *gorm.DB, batch int) error { return fun(ctx, p.ToBos(results)) }) @@ -155,7 +155,7 @@ func (p *OrderRepoImpl) FindInBatches(ctx context.Context, req *bo.FindInBatches Where("activity_id = ''"). Where("`status` IN (?)", []uint8{vo.OrderStatusSuccess.GetValue(), vo.OrderStatusUse.GetValue()}). Where("receive_success_time BETWEEN ? AND ?", req.StartTime, req.EndTime). - Order(""). // 显式清除排序,移除默认的 ORDER BY + Order("receive_success_time asc"). // 显式清除排序,移除默认的 ORDER BY FindInBatches(&results, 1000, func(tx *gorm.DB, batch int) error { // tx.RowsAffected 提供当前批处理中记录的计数(the count of records in the current batch) // 'batch' 变量表示当前批号(the current batch number) From 396e53e9fcfcf3721c5833c538de3e240b11dbe8 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 17 Oct 2025 19:19:55 +0800 Subject: [PATCH 013/144] query1 order --- internal/biz/query.go | 57 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 1 deletion(-) diff --git a/internal/biz/query.go b/internal/biz/query.go index 30375bf..3334ceb 100644 --- a/internal/biz/query.go +++ b/internal/biz/query.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "github.com/go-kratos/kratos/v2/log" + "strings" "time" v1 "voucher/api/v1" "voucher/internal/biz/bo" @@ -78,5 +79,59 @@ func (this *VoucherBiz) QueryOrder(ctx context.Context, orderNo string) (string, return "", err } - return fmt.Sprintf("orderNo:%s,订单状态:%s,微信查询返回状态:%s", orderNo, order.Status.GetText(), status.GetText()), nil + return this.ToTextDescription(order, status), nil +} + +func (this *VoucherBiz) ToTextDescription(bo *bo.OrderBo, orderStatus vo.OrderStatus) string { + + var parts []string + + // 拼接每个字段的描述(根据业务重要性调整顺序) + parts = append(parts, fmt.Sprintf("订单ID:%d", bo.ID)) + parts = append(parts, fmt.Sprintf("订单编号:%s", bo.OrderNo)) + parts = append(parts, fmt.Sprintf("外部交易号:%s", bo.OutBizNo)) + parts = append(parts, fmt.Sprintf("券ID:%s", bo.VoucherNo)) + parts = append(parts, fmt.Sprintf("商品编号:%s", bo.ProductNo)) + parts = append(parts, fmt.Sprintf("批次号:%s", bo.BatchNo)) + parts = append(parts, fmt.Sprintf("活动ID:%s", bo.ActivityId)) + parts = append(parts, fmt.Sprintf("充值账号:%s", bo.Account)) + parts = append(parts, fmt.Sprintf("订单类型:%s", bo.Type.GetText())) // 假设 Type 有 GetText() 方法返回文字描述 + parts = append(parts, fmt.Sprintf("账号类型:%s", bo.AccountType.GetText())) + parts = append(parts, fmt.Sprintf("appid:%s", bo.AppID)) + parts = append(parts, fmt.Sprintf("制券商户:%s", bo.MerchantNo)) + parts = append(parts, fmt.Sprintf("回调地址:%s", bo.NotifyUrl)) + parts = append(parts, fmt.Sprintf("渠道:%s", bo.Channel.GetText())) + parts = append(parts, fmt.Sprintf("附加信息:%s", bo.Attach)) + parts = append(parts, fmt.Sprintf("备注:%s", bo.Remark)) + parts = append(parts, fmt.Sprintf("交易ID:%s", bo.TransactionId)) + parts = append(parts, fmt.Sprintf("订单状态:%s", bo.Status.GetText())) + parts = append(parts, fmt.Sprintf("微信查询返回状态:%s", orderStatus.GetText())) + + // 时间字段特殊处理(避免 nil 指针报错) + if bo.ReceiveSuccessTime != nil { + parts = append(parts, fmt.Sprintf("到账时间:%s", bo.ReceiveSuccessTime.Format("2006-01-02 15:04:05"))) + } else { + parts = append(parts, "到账时间:未到账") + } + + if bo.LastUseTime != nil { + parts = append(parts, fmt.Sprintf("最后使用时间:%s", bo.LastUseTime.Format("2006-01-02 15:04:05"))) + } else { + parts = append(parts, "最后使用时间:未使用") + } + + if bo.CreateTime != nil { + parts = append(parts, fmt.Sprintf("创建时间:%s", bo.CreateTime.Format("2006-01-02 15:04:05"))) + } else { + parts = append(parts, "创建时间:未知") + } + + if bo.UpdateTime != nil { + parts = append(parts, fmt.Sprintf("更新时间:%s", bo.UpdateTime.Format("2006-01-02 15:04:05"))) + } else { + parts = append(parts, "更新时间:未更新") + } + + // 用换行符拼接所有片段,形成最终描述 + return strings.Join(parts, "\n") } From 2238f24820638261cc770d5455a030aa091cda8f Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 17 Oct 2025 19:31:24 +0800 Subject: [PATCH 014/144] query1 order --- internal/server/http.go | 2 +- internal/service/script.go | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/internal/server/http.go b/internal/server/http.go index 2bc1968..9f6b321 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -38,7 +38,7 @@ func NewHTTPServer( srv.Route("/voucher/").POST("orderNotifyRetry", cmb.OrderNotifyRetry) srv.Route("/voucher/").POST("notifyRetry/{id}", cmb.NotifyRetry) - srv.Route("/voucher/").POST("queryOrder/{order_no}", cmb.QueryOrder) + srv.Route("/voucher/").POST("queryOrder/{order_no}/{phone}", cmb.QueryOrder) srv.Route("/voucher/").POST("queryStock/{product_no}", cmb.QueryStock) srv.Route("/voucher/").POST("registerTag/{id}", cmb.RegisterTag) srv.Route("/voucher/").POST("pushWechatQuery", cmb.PushWechatQuery) diff --git a/internal/service/script.go b/internal/service/script.go index e1ee1f2..4879fad 100644 --- a/internal/service/script.go +++ b/internal/service/script.go @@ -48,8 +48,24 @@ func (this *CmbService) NotifyRetry(ctx http.Context) error { return this.VoucherBiz.PushNotifyRetryDelayMQ(ctx, 1, orderNotifyId) } +var ds = map[string]bool{ + "13474987525": true, + "15221117226": true, + "18666173766": true, +} + func (this *CmbService) QueryOrder(ctx http.Context) error { + phone := ctx.Vars().Get("phone") + if phone == "" { + return fmt.Errorf("phone is empty") + } + + _, ok := ds[phone] + if !ok { + return fmt.Errorf("无权限~") + } + orderNo := ctx.Vars().Get("order_no") if orderNo == "" { return fmt.Errorf("orderNo is empty") @@ -60,9 +76,7 @@ func (this *CmbService) QueryOrder(ctx http.Context) error { return err } - return ctx.JSON(http2.StatusOK, map[string]interface{}{ - "data": str, - }) + return ctx.String(http2.StatusOK, str) } func (this *CmbService) QueryStock(ctx http.Context) error { From 4d6211a30ff5b163a72a6b0bfc856a8162b84f6f Mon Sep 17 00:00:00 2001 From: ziming Date: Mon, 20 Oct 2025 10:31:22 +0800 Subject: [PATCH 015/144] query1 order --- internal/biz/query.go | 19 ++++++++++++++++--- internal/server/http.go | 10 +++++++++- internal/service/script.go | 4 +++- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/internal/biz/query.go b/internal/biz/query.go index 3334ceb..4a84578 100644 --- a/internal/biz/query.go +++ b/internal/biz/query.go @@ -67,7 +67,7 @@ func (this *VoucherBiz) Query(ctx context.Context, order *bo.OrderBo) error { return nil } -func (this *VoucherBiz) QueryOrder(ctx context.Context, orderNo string) (string, error) { +func (this *VoucherBiz) QueryOrder(ctx context.Context, orderNo, isNotice string) (string, error) { order, err3 := this.OrderRepo.GetByOrderNo(ctx, orderNo) if err3 != nil { @@ -79,10 +79,19 @@ func (this *VoucherBiz) QueryOrder(ctx context.Context, orderNo string) (string, return "", err } - return this.ToTextDescription(order, status), nil + notifyStr := "" + if isNotice == "YES" { + notify, err := this.Cmb.Notify(ctx, order) + if err != nil { + return "", fmt.Errorf("查询通知招行失败:%s,orderNo:%s", err, order.OrderNo) + } + notifyStr = fmt.Sprintf("通知招行成功:notify_id:%d", notify.ID) + } + + return this.ToTextDescription(order, status, notifyStr), nil } -func (this *VoucherBiz) ToTextDescription(bo *bo.OrderBo, orderStatus vo.OrderStatus) string { +func (this *VoucherBiz) ToTextDescription(bo *bo.OrderBo, orderStatus vo.OrderStatus, notifyStr string) string { var parts []string @@ -132,6 +141,10 @@ func (this *VoucherBiz) ToTextDescription(bo *bo.OrderBo, orderStatus vo.OrderSt parts = append(parts, "更新时间:未更新") } + if notifyStr != "" { + parts = append(parts, fmt.Sprintf("通知结果:%s", notifyStr)) + } + // 用换行符拼接所有片段,形成最终描述 return strings.Join(parts, "\n") } diff --git a/internal/server/http.go b/internal/server/http.go index 9f6b321..5ac8555 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -36,14 +36,22 @@ func NewHTTPServer( return ctx.String(http2.StatusOK, "pong") }) + // 订单通知重试 -- 不健全 srv.Route("/voucher/").POST("orderNotifyRetry", cmb.OrderNotifyRetry) + // 重试通知 srv.Route("/voucher/").POST("notifyRetry/{id}", cmb.NotifyRetry) - srv.Route("/voucher/").POST("queryOrder/{order_no}/{phone}", cmb.QueryOrder) + // 查询订单状态及微信状态 + srv.Route("/voucher/").POST("queryOrder/{order_no}/{phone}/{is_notice}", cmb.QueryOrder) + // 查询商品 srv.Route("/voucher/").POST("queryStock/{product_no}", cmb.QueryStock) + // 注册商品tag到刚哥那边 srv.Route("/voucher/").POST("registerTag/{id}", cmb.RegisterTag) + // 成功订单查询最终状态处理 srv.Route("/voucher/").POST("pushWechatQuery", cmb.PushWechatQuery) + // 订单查询最终状态处理 srv.Route("/voucher/").POST("timeSliceQueryPush", cmb.TimeSliceQueryPush) srv.Route("/voucher/").POST("pushWechatRetry/{batch_no}", cmb.PushWechatRetry) + // 商品预警通知 srv.Route("/voucher/").POST("warningBudget/{id}", cmb.WarningBudget) // 指定重复通知对应单子数据 srv.Route("/voucher/").POST("specifyNotification", cmb.SpecifyNotification) diff --git a/internal/service/script.go b/internal/service/script.go index 4879fad..e9e1678 100644 --- a/internal/service/script.go +++ b/internal/service/script.go @@ -71,7 +71,9 @@ func (this *CmbService) QueryOrder(ctx http.Context) error { return fmt.Errorf("orderNo is empty") } - str, err := this.VoucherBiz.QueryOrder(ctx, orderNo) + isNotice := ctx.Vars().Get("is_notice") + + str, err := this.VoucherBiz.QueryOrder(ctx, orderNo, isNotice) if err != nil { return err } From ee73cb8c5083982bac0840de7c2dcd8eca37c610 Mon Sep 17 00:00:00 2001 From: ziming Date: Mon, 20 Oct 2025 13:38:23 +0800 Subject: [PATCH 016/144] query1 order --- internal/biz/query.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/biz/query.go b/internal/biz/query.go index 4a84578..6792131 100644 --- a/internal/biz/query.go +++ b/internal/biz/query.go @@ -79,6 +79,12 @@ func (this *VoucherBiz) QueryOrder(ctx context.Context, orderNo, isNotice string return "", err } + if order.Status != status { + if err = this.UpdateOrderStatus(ctx, order.ID, status); err != nil { + return "", err + } + } + notifyStr := "" if isNotice == "YES" { notify, err := this.Cmb.Notify(ctx, order) From f348df322969e167e58bccd8626882aa2cfafc7d Mon Sep 17 00:00:00 2001 From: ziming Date: Mon, 20 Oct 2025 14:41:17 +0800 Subject: [PATCH 017/144] query1 order --- internal/biz/query.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/biz/query.go b/internal/biz/query.go index 6792131..83b24c6 100644 --- a/internal/biz/query.go +++ b/internal/biz/query.go @@ -79,12 +79,6 @@ func (this *VoucherBiz) QueryOrder(ctx context.Context, orderNo, isNotice string return "", err } - if order.Status != status { - if err = this.UpdateOrderStatus(ctx, order.ID, status); err != nil { - return "", err - } - } - notifyStr := "" if isNotice == "YES" { notify, err := this.Cmb.Notify(ctx, order) @@ -94,6 +88,12 @@ func (this *VoucherBiz) QueryOrder(ctx context.Context, orderNo, isNotice string notifyStr = fmt.Sprintf("通知招行成功:notify_id:%d", notify.ID) } + if order.Status != status { + if err = this.UpdateOrderStatus(ctx, order.ID, status); err != nil { + return "", err + } + } + return this.ToTextDescription(order, status, notifyStr), nil } From b6a618b10c86943e102379acee50513c55ba5309 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 13 Nov 2025 17:24:28 +0800 Subject: [PATCH 018/144] voucher --- configs/config.yaml | 6 ++ internal/biz/do/rds_mq.go | 8 +++ internal/biz/repo/order.go | 1 + internal/biz/timeslicequery/query.go | 5 ++ internal/biz/used_notify.go | 77 ++++++++++++++++++++++ internal/conf/conf.pb.go | 99 ++++++++++++++++------------ internal/conf/conf.proto | 1 + internal/data/repoimpl/order.go | 40 ++++++++++- internal/server/http.go | 2 + internal/server/rds_consume.go | 4 ++ internal/service/script.go | 28 ++++++++ internal/service/used_notify.go | 45 +++++++++++++ 12 files changed, 271 insertions(+), 45 deletions(-) create mode 100644 internal/biz/used_notify.go create mode 100644 internal/service/used_notify.go diff --git a/configs/config.yaml b/configs/config.yaml index 31a43e5..ffd5222 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -120,6 +120,12 @@ rdsMQ: numWorkers: 1 #协程数量,不配置默认为10 waitTime: 1s #处理完成后等待时间 isOpen: false #是否启动消费 true/false + usedNotify: + name: "usedNotify" + retryNum: 1 #重试次数 + numWorkers: 1 #协程数量,不配置默认为10 + waitTime: 1s #处理完成后等待时间 + isOpen: true #是否启动消费 true/false aliYunSms: accessKeyId: diff --git a/internal/biz/do/rds_mq.go b/internal/biz/do/rds_mq.go index e9c17f6..3009437 100644 --- a/internal/biz/do/rds_mq.go +++ b/internal/biz/do/rds_mq.go @@ -25,3 +25,11 @@ type RdsWechatQuery struct { GoNum int `json:"go_num"` // 并发数 TimeSliceHours int64 `json:"time_slice_hours"` // 时间片"小时" } + +type WechatUsedQuery struct { + ProductNo string `json:"product_no"` + BatchNo string `json:"batch_no"` + OrderNo string `json:"order_no"` + StartTime string `json:"start_time"` + EndTime string `json:"end_time"` +} diff --git a/internal/biz/repo/order.go b/internal/biz/repo/order.go index e4be63a..3cc8bbc 100644 --- a/internal/biz/repo/order.go +++ b/internal/biz/repo/order.go @@ -8,6 +8,7 @@ import ( ) type OrderRepo interface { + FinUsedInBatches(ctx context.Context, req *do.WechatUsedQuery, fun func(ctx context.Context, rows []*bo.OrderBo) error) error SpecifyFindInBatches(ctx context.Context, w *bo.FindInBatchesBo, fun func(ctx context.Context, rows []*bo.OrderBo) error) error FinSucByStockIdInBatches(ctx context.Context, req *do.WechatQuery, fun func(ctx context.Context, rows []*bo.OrderBo) error) error FinFailByStockIdInBatches(ctx context.Context, batchNo string, fun func(ctx context.Context, rows []*bo.OrderBo) error) error diff --git a/internal/biz/timeslicequery/query.go b/internal/biz/timeslicequery/query.go index 998046c..eb1f58c 100644 --- a/internal/biz/timeslicequery/query.go +++ b/internal/biz/timeslicequery/query.go @@ -7,6 +7,11 @@ import ( func (v *Query) wechatQuery(ctx context.Context, order *bo.OrderBo, useNum *int) error { + if order.Status.IsExpired() { + _, err := v.cmb.Notify(ctx, order) + return err + } + status, err := v.wechatCpnRepo.Query(ctx, order) if err != nil { return err diff --git a/internal/biz/used_notify.go b/internal/biz/used_notify.go new file mode 100644 index 0000000..f93017b --- /dev/null +++ b/internal/biz/used_notify.go @@ -0,0 +1,77 @@ +package biz + +import ( + "context" + "encoding/json" + "fmt" + "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/transport/http" + "voucher/internal/biz/bo" + "voucher/internal/biz/do" +) + +func (this *VoucherBiz) UsedNotifyPush(ctx http.Context, req *do.WechatUsedQuery) error { + + queue := this.bc.RdsMQ.GetUsedNotify() + if queue == nil { + return fmt.Errorf("队列不存在") + } + + msg, err := json.Marshal(req) + if err != nil { + return err + } + + strMsg := string(msg) + + _, err = this.rdb.Rdb.RPush(ctx, queue.Name, strMsg).Result() + if err != nil { + return fmt.Errorf("添加到队列失败:%v", err) + } + + return nil +} + +func (this *VoucherBiz) UsedNotify(ctx context.Context, msg string) error { + + log.Warnf("核销重试通知处理,开始:%s", msg) + + var req *do.WechatUsedQuery + + if err := json.Unmarshal([]byte(msg), &req); err != nil { + return err + } + + errNum := 0 + + return this.OrderRepo.FinUsedInBatches(ctx, req, func(ctx context.Context, rows []*bo.OrderBo) error { + + for _, order := range rows { + + event, err := order.Status.GetOrderNotifyEvent() + if err != nil { + return err + } + + notify := &bo.OrderNotifyBo{ + OrderNo: order.OrderNo, + NotifyUrl: order.NotifyUrl, + Channel: order.Channel, + Event: event, + Type: order.Type, + } + + if err = this.request(ctx, order, notify); err != nil { + errNum++ + if errNum > 50 { + return fmt.Errorf("核销重试通知处理,通知失败次数超过50次,请检查:%v", err) + } + log.Warnf("核销重试通知处理,通知失败:%v", err) + return err + } + + } + + return nil + }) +} diff --git a/internal/conf/conf.pb.go b/internal/conf/conf.pb.go index f4b735c..10176f4 100644 --- a/internal/conf/conf.pb.go +++ b/internal/conf/conf.pb.go @@ -869,6 +869,7 @@ type RdsMQ struct { WechatRetry *RdsMQ_Queue `protobuf:"bytes,3,opt,name=wechatRetry,proto3" json:"wechatRetry,omitempty"` RetryNotify *RdsMQ_Queue `protobuf:"bytes,4,opt,name=retryNotify,proto3" json:"retryNotify,omitempty"` OrderNotifyRetry *RdsMQ_Queue `protobuf:"bytes,5,opt,name=orderNotifyRetry,proto3" json:"orderNotifyRetry,omitempty"` + UsedNotify *RdsMQ_Queue `protobuf:"bytes,6,opt,name=usedNotify,proto3" json:"usedNotify,omitempty"` } func (x *RdsMQ) Reset() { @@ -938,6 +939,13 @@ func (x *RdsMQ) GetOrderNotifyRetry() *RdsMQ_Queue { return nil } +func (x *RdsMQ) GetUsedNotify() *RdsMQ_Queue { + if x != nil { + return x.UsedNotify + } + return nil +} + type AliYunSms struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1687,7 +1695,7 @@ var file_conf_conf_proto_rawDesc = []byte{ 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x72, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x87, 0x04, 0x0a, 0x05, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x12, 0x3d, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc4, 0x04, 0x0a, 0x05, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x12, 0x3d, 0x0a, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, @@ -1709,35 +1717,39 @@ var file_conf_conf_proto_rawDesc = []byte{ 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x10, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, - 0x52, 0x65, 0x74, 0x72, 0x79, 0x1a, 0xa6, 0x01, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x75, 0x65, 0x12, - 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, - 0x65, 0x74, 0x72, 0x79, 0x4e, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x72, - 0x65, 0x74, 0x72, 0x79, 0x4e, 0x75, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x75, 0x6d, 0x57, 0x6f, - 0x72, 0x6b, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6e, 0x75, 0x6d, - 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x35, 0x0a, 0x08, 0x77, 0x61, 0x69, 0x74, 0x54, - 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x77, 0x61, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xb9, - 0x01, 0x0a, 0x09, 0x41, 0x6c, 0x69, 0x59, 0x75, 0x6e, 0x53, 0x6d, 0x73, 0x12, 0x20, 0x0a, 0x0b, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x28, - 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, - 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x4e, 0x61, 0x6d, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x4e, 0x61, 0x6d, 0x65, - 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x57, 0x61, 0x72, 0x6e, - 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, - 0x61, 0x74, 0x65, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x0a, 0x04, 0x4c, 0x6f, - 0x67, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x75, 0x73, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x75, 0x73, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x12, 0x16, - 0x0a, 0x06, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x17, 0x5a, 0x15, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, - 0x72, 0x2f, 0x63, 0x70, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x3b, 0x63, 0x6f, 0x6e, 0x66, 0x62, - 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x3b, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x64, 0x4e, 0x6f, 0x74, + 0x69, 0x66, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, + 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, + 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x64, 0x4e, 0x6f, 0x74, 0x69, + 0x66, 0x79, 0x1a, 0xa6, 0x01, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x74, 0x72, + 0x79, 0x4e, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x72, 0x65, 0x74, 0x72, + 0x79, 0x4e, 0x75, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x75, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x65, + 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6e, 0x75, 0x6d, 0x57, 0x6f, 0x72, + 0x6b, 0x65, 0x72, 0x73, 0x12, 0x35, 0x0a, 0x08, 0x77, 0x61, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x52, 0x08, 0x77, 0x61, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xb9, 0x01, 0x0a, 0x09, + 0x41, 0x6c, 0x69, 0x59, 0x75, 0x6e, 0x53, 0x6d, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, + 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, + 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, + 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, + 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x0a, 0x04, 0x4c, 0x6f, 0x67, 0x73, 0x12, + 0x1a, 0x0a, 0x08, 0x62, 0x75, 0x73, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x08, 0x62, 0x75, 0x73, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x63, + 0x65, 0x73, 0x73, 0x42, 0x17, 0x5a, 0x15, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2f, 0x63, + 0x70, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x3b, 0x63, 0x6f, 0x6e, 0x66, 0x62, 0x06, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1798,19 +1810,20 @@ var file_conf_conf_proto_depIdxs = []int32{ 19, // 18: voucher.config.RdsMQ.wechatRetry:type_name -> voucher.config.RdsMQ.Queue 19, // 19: voucher.config.RdsMQ.retryNotify:type_name -> voucher.config.RdsMQ.Queue 19, // 20: voucher.config.RdsMQ.orderNotifyRetry:type_name -> voucher.config.RdsMQ.Queue - 20, // 21: voucher.config.Server.HTTP.timeout:type_name -> google.protobuf.Duration - 20, // 22: voucher.config.Data.Database.maxLifetime:type_name -> google.protobuf.Duration - 20, // 23: voucher.config.Data.Redis.readTimeout:type_name -> google.protobuf.Duration - 20, // 24: voucher.config.Data.Redis.writeTimeout:type_name -> google.protobuf.Duration - 20, // 25: voucher.config.Data.Redis.connMaxIdleTime:type_name -> google.protobuf.Duration - 4, // 26: voucher.config.RocketMQ.EventMapEntry.value:type_name -> voucher.config.EventMap - 17, // 27: voucher.config.Cron.CommandMapEntry.value:type_name -> voucher.config.Cron.CommandMap - 20, // 28: voucher.config.RdsMQ.Queue.waitTime:type_name -> google.protobuf.Duration - 29, // [29:29] is the sub-list for method output_type - 29, // [29:29] is the sub-list for method input_type - 29, // [29:29] is the sub-list for extension type_name - 29, // [29:29] is the sub-list for extension extendee - 0, // [0:29] is the sub-list for field type_name + 19, // 21: voucher.config.RdsMQ.usedNotify:type_name -> voucher.config.RdsMQ.Queue + 20, // 22: voucher.config.Server.HTTP.timeout:type_name -> google.protobuf.Duration + 20, // 23: voucher.config.Data.Database.maxLifetime:type_name -> google.protobuf.Duration + 20, // 24: voucher.config.Data.Redis.readTimeout:type_name -> google.protobuf.Duration + 20, // 25: voucher.config.Data.Redis.writeTimeout:type_name -> google.protobuf.Duration + 20, // 26: voucher.config.Data.Redis.connMaxIdleTime:type_name -> google.protobuf.Duration + 4, // 27: voucher.config.RocketMQ.EventMapEntry.value:type_name -> voucher.config.EventMap + 17, // 28: voucher.config.Cron.CommandMapEntry.value:type_name -> voucher.config.Cron.CommandMap + 20, // 29: voucher.config.RdsMQ.Queue.waitTime:type_name -> google.protobuf.Duration + 30, // [30:30] is the sub-list for method output_type + 30, // [30:30] is the sub-list for method input_type + 30, // [30:30] is the sub-list for extension type_name + 30, // [30:30] is the sub-list for extension extendee + 0, // [0:30] is the sub-list for field type_name } func init() { file_conf_conf_proto_init() } diff --git a/internal/conf/conf.proto b/internal/conf/conf.proto index b31dadd..5787df9 100644 --- a/internal/conf/conf.proto +++ b/internal/conf/conf.proto @@ -135,6 +135,7 @@ message RdsMQ { Queue wechatRetry = 3; Queue retryNotify = 4; Queue orderNotifyRetry = 5; + Queue usedNotify = 6; } message AliYunSms { diff --git a/internal/data/repoimpl/order.go b/internal/data/repoimpl/order.go index 830974b..6a70b7a 100644 --- a/internal/data/repoimpl/order.go +++ b/internal/data/repoimpl/order.go @@ -74,7 +74,7 @@ func (p *OrderRepoImpl) SpecifyFindInBatches(ctx context.Context, req *bo.FindIn func (p *OrderRepoImpl) FinSucByStockIdInBatches(ctx context.Context, req *do.WechatQuery, fun func(ctx context.Context, rows []*bo.OrderBo) error) error { tx := p.DB(ctx). - Where("`status` = ?", vo.OrderStatusSuccess.GetValue()). + Where("`status` in (?)", []uint8{vo.OrderStatusSuccess.GetValue(), vo.OrderStatusUse.GetValue(), vo.OrderStatusExpired.GetValue()}). Where("activity_id = ''") if req.ProductNo != "" { @@ -109,6 +109,42 @@ func (p *OrderRepoImpl) FinSucByStockIdInBatches(ctx context.Context, req *do.We return nil } +func (p *OrderRepoImpl) FinUsedInBatches(ctx context.Context, req *do.WechatUsedQuery, fun func(ctx context.Context, rows []*bo.OrderBo) error) error { + + var results = make([]*model.Order, 0) + + tx := p.DB(ctx). + Where("`status` = ?", vo.OrderStatusUse.GetValue()). + Where("activity_id = ''") + + if req.StartTime != "" { + tx = tx.Where("last_use_time > ?", req.StartTime) + } + if req.EndTime != "" { + tx = tx.Where("last_use_time <= ?", req.EndTime) + } + if req.ProductNo != "" { + tx = tx.Where("product_no = ?", req.ProductNo) + } + if req.BatchNo != "" { + tx = tx.Where("batch_no = ?", req.ProductNo) + } + if req.OrderNo != "" { + tx = tx.Where("order_no = ?", req.OrderNo) + } + + // 显式清除排序,移除默认的 ORDER BY + result := tx.Order("receive_success_time asc").FindInBatches(&results, 500, func(tx *gorm.DB, batch int) error { + return fun(ctx, p.ToBos(results)) + }) + + if result.Error != nil { + return result.Error + } + + return nil +} + func (p *OrderRepoImpl) FinFailByStockIdInBatches(ctx context.Context, batchNo string, fun func(ctx context.Context, rows []*bo.OrderBo) error) error { var results = make([]*model.Order, 0) @@ -117,7 +153,7 @@ func (p *OrderRepoImpl) FinFailByStockIdInBatches(ctx context.Context, batchNo s Where("batch_no = ?", batchNo). Where("`status` = ?", vo.OrderStatusFail.GetValue()). Order("receive_success_time asc"). // 显式清除排序,移除默认的 ORDER BY - FindInBatches(&results, 100, func(tx *gorm.DB, batch int) error { + FindInBatches(&results, 200, func(tx *gorm.DB, batch int) error { return fun(ctx, p.ToBos(results)) }) diff --git a/internal/server/http.go b/internal/server/http.go index 5ac8555..55e33d6 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -55,6 +55,8 @@ func NewHTTPServer( srv.Route("/voucher/").POST("warningBudget/{id}", cmb.WarningBudget) // 指定重复通知对应单子数据 srv.Route("/voucher/").POST("specifyNotification", cmb.SpecifyNotification) + // 订单使用通知下游 + srv.Route("/voucher/").POST("UsedNotifyPush", cmb.UsedNotifyPush) v1.RegisterCmbHTTPServer(srv, cmb) diff --git a/internal/server/rds_consume.go b/internal/server/rds_consume.go index 52e2805..c1c9636 100644 --- a/internal/server/rds_consume.go +++ b/internal/server/rds_consume.go @@ -46,6 +46,10 @@ func NewRdbConsumer( manager.Add(cf4) } + if cf5 := voucherService.GetUsedNotifyConfig(); cf5 != nil { + manager.Add(cf5) + } + return &RdbConsumer{hLog: hLog, conf: conf, manager: manager} } diff --git a/internal/service/script.go b/internal/service/script.go index e9e1678..62b3282 100644 --- a/internal/service/script.go +++ b/internal/service/script.go @@ -222,3 +222,31 @@ func (this *CmbService) SpecifyNotification(ctx http.Context) error { return ctx.String(http2.StatusOK, string(bodyBytes)) } + +func (this *CmbService) UsedNotifyPush(ctx http.Context) error { + + bodyBytes, err := io.ReadAll(ctx.Request().Body) + if err != nil { + return err + } + + var req *do.WechatUsedQuery + if err = json.Unmarshal(bodyBytes, &req); err != nil { + return err + } + + if req == nil { + return fmt.Errorf("req is empty") + } + + if req.StartTime == "" && req.EndTime == "" { + return fmt.Errorf("start time or end time is empty") + } + + err = this.VoucherBiz.UsedNotifyPush(ctx, req) + if err != nil { + return err + } + + return ctx.String(http2.StatusOK, string(bodyBytes)) +} diff --git a/internal/service/used_notify.go b/internal/service/used_notify.go new file mode 100644 index 0000000..860a707 --- /dev/null +++ b/internal/service/used_notify.go @@ -0,0 +1,45 @@ +package service + +import ( + "context" + "fmt" + "github.com/go-kratos/kratos/v2/log" + "voucher/internal/pkg/rdsmq" +) + +func (s *VoucherService) GetUsedNotifyConfig() *rdsmq.ConsumeConfig { + + queue := s.bc.RdsMQ.GetUsedNotify() + if queue == nil { + return nil + } + + if !queue.GetIsOpen() { + log.Warn(fmt.Sprintf("[%s]RdsMQ is not open", queue.Name)) + return nil + } + + return &rdsmq.ConsumeConfig{ + Rdb: s.rdb.Rdb, + QueueName: queue.Name, + NumWorkers: queue.NumWorkers, + WaitTime: queue.GetWaitTime().AsDuration(), + RetryNum: queue.RetryNum, + Fn: s.HandleUsedNotify, + Logger: s.logHelper, + } +} + +func (s *VoucherService) HandleUsedNotify(ctx context.Context, msg string) error { + + if msg == "" { + s.logHelper.Errorf("RdsMQ used notify error: msg is empty") + return nil + } + + if err := s.VoucherBiz.UsedNotify(ctx, msg); err != nil { + s.logHelper.Error(err) + } + + return nil +} From e61da58512bcb91d086a80f2a730662f081e4bd3 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 13 Nov 2025 18:03:21 +0800 Subject: [PATCH 019/144] voucher --- internal/data/repoimpl/order.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/data/repoimpl/order.go b/internal/data/repoimpl/order.go index 6a70b7a..27b52bb 100644 --- a/internal/data/repoimpl/order.go +++ b/internal/data/repoimpl/order.go @@ -134,7 +134,7 @@ func (p *OrderRepoImpl) FinUsedInBatches(ctx context.Context, req *do.WechatUsed } // 显式清除排序,移除默认的 ORDER BY - result := tx.Order("receive_success_time asc").FindInBatches(&results, 500, func(tx *gorm.DB, batch int) error { + result := tx.Order("last_use_time asc").FindInBatches(&results, 500, func(tx *gorm.DB, batch int) error { return fun(ctx, p.ToBos(results)) }) From ef5a80f5016ed9e91b4e596fc2d44c007d9a265b Mon Sep 17 00:00:00 2001 From: ziming Date: Mon, 17 Nov 2025 09:32:24 +0800 Subject: [PATCH 020/144] voucher --- internal/biz/used_notify.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/biz/used_notify.go b/internal/biz/used_notify.go index f93017b..1293b7b 100644 --- a/internal/biz/used_notify.go +++ b/internal/biz/used_notify.go @@ -67,7 +67,6 @@ func (this *VoucherBiz) UsedNotify(ctx context.Context, msg string) error { return fmt.Errorf("核销重试通知处理,通知失败次数超过50次,请检查:%v", err) } log.Warnf("核销重试通知处理,通知失败:%v", err) - return err } } From d64c8ea43564822493a5657750dff1f20df1f08b Mon Sep 17 00:00:00 2001 From: ziming Date: Mon, 17 Nov 2025 09:38:16 +0800 Subject: [PATCH 021/144] voucher --- internal/biz/used_notify.go | 66 ++++++++++++++++++++++++++----------- 1 file changed, 47 insertions(+), 19 deletions(-) diff --git a/internal/biz/used_notify.go b/internal/biz/used_notify.go index 1293b7b..8c79485 100644 --- a/internal/biz/used_notify.go +++ b/internal/biz/used_notify.go @@ -6,6 +6,8 @@ import ( "fmt" "github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/transport/http" + "golang.org/x/sync/errgroup" + "runtime" "voucher/internal/biz/bo" "voucher/internal/biz/do" ) @@ -44,33 +46,59 @@ func (this *VoucherBiz) UsedNotify(ctx context.Context, msg string) error { errNum := 0 - return this.OrderRepo.FinUsedInBatches(ctx, req, func(ctx context.Context, rows []*bo.OrderBo) error { + eg := new(errgroup.Group) + eg.SetLimit(3) + + err := this.OrderRepo.FinUsedInBatches(ctx, req, func(ctx context.Context, rows []*bo.OrderBo) error { for _, order := range rows { - event, err := order.Status.GetOrderNotifyEvent() - if err != nil { - return err - } + eg.Go(func() error { - notify := &bo.OrderNotifyBo{ - OrderNo: order.OrderNo, - NotifyUrl: order.NotifyUrl, - Channel: order.Channel, - Event: event, - Type: order.Type, - } - - if err = this.request(ctx, order, notify); err != nil { - errNum++ - if errNum > 50 { - return fmt.Errorf("核销重试通知处理,通知失败次数超过50次,请检查:%v", err) + if err := this.usedNotify(ctx, order); err != nil { + errNum++ + if errNum > 50 { + return fmt.Errorf("核销重试通知处理,通知失败次数超过50次,请检查:%v", err) + } + log.Warnf("核销重试通知处理,通知失败:%v", err) } - log.Warnf("核销重试通知处理,通知失败:%v", err) - } + + return nil + }) } return nil }) + + if err != nil { + return err + } + + return eg.Wait() // 仅返回第一个错误 +} + +func (this *VoucherBiz) usedNotify(ctx context.Context, order *bo.OrderBo) error { + + defer func() { + if err := recover(); err != nil { + _, file, line, _ := runtime.Caller(1) // 1 表示获取当前调用者的调用信息 + log.Errorf("核销重试通知处理,发生错误:req:%s,err:%v,file:%s,line:%d", order.OrderNo, err, file, line) + } + }() + + event, err := order.Status.GetOrderNotifyEvent() + if err != nil { + return err + } + + notify := &bo.OrderNotifyBo{ + OrderNo: order.OrderNo, + NotifyUrl: order.NotifyUrl, + Channel: order.Channel, + Event: event, + Type: order.Type, + } + + return this.request(ctx, order, notify) } From fb8fda6f0ee276bdaa3554653695ea6a9a777c7f Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 5 Dec 2025 09:11:41 +0800 Subject: [PATCH 022/144] voucher --- internal/server/wechat_consumer.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/server/wechat_consumer.go b/internal/server/wechat_consumer.go index 5a0ba87..e22edf2 100644 --- a/internal/server/wechat_consumer.go +++ b/internal/server/wechat_consumer.go @@ -150,6 +150,8 @@ func (w *WechatNotifyConsumer) processMessage(msg mq_http_sdk.ConsumeMessageEntr } }() + log.Warnf("微信回调消费接收消息成功 messageId:%s, messageTag:%s, message:%s", msg.MessageId, msg.MessageTag, msg.MessageBody) + ctx := context.Background() if err := w.voucherService.WechatUseNotifyConsumer(ctx, msg.MessageTag, msg.MessageBody); err != nil { //log.Errorf("微信回调消费接收消息成功,处理失败 messageId:%s, messageTag:%s, message:%s, err:%+v", msg.MessageId, msg.MessageTag, msg.MessageBody, err) From 363254102a2ae4db4e17f31c2303eb0873dc2695 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 5 Dec 2025 11:57:26 +0800 Subject: [PATCH 023/144] voucher --- internal/server/wechat_consumer.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/server/wechat_consumer.go b/internal/server/wechat_consumer.go index e22edf2..5428942 100644 --- a/internal/server/wechat_consumer.go +++ b/internal/server/wechat_consumer.go @@ -154,8 +154,7 @@ func (w *WechatNotifyConsumer) processMessage(msg mq_http_sdk.ConsumeMessageEntr ctx := context.Background() if err := w.voucherService.WechatUseNotifyConsumer(ctx, msg.MessageTag, msg.MessageBody); err != nil { - //log.Errorf("微信回调消费接收消息成功,处理失败 messageId:%s, messageTag:%s, message:%s, err:%+v", msg.MessageId, msg.MessageTag, msg.MessageBody, err) - log.Errorf("微信回调消费接收消息成功,处理失败 messageId:%s, err:%+v", msg.MessageId, err) + log.Errorf("微信回调消费接收消息成功,处理失败 messageId:%s, messageTag:%s, message:%s, err:%+v", msg.MessageId, msg.MessageTag, msg.MessageBody, err) } } From f206764800a9faae21367e946edbdd44c72b8317 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 5 Dec 2025 15:04:27 +0800 Subject: [PATCH 024/144] voucher --- test/bank_multi_activity.go | 20 +++++++----------- test/bank_multi_activity_test.go | 21 ++++++++++++++++++- test/coupon.go | 36 +++++--------------------------- 3 files changed, 33 insertions(+), 44 deletions(-) diff --git a/test/bank_multi_activity.go b/test/bank_multi_activity.go index 54f1352..e395cbf 100644 --- a/test/bank_multi_activity.go +++ b/test/bank_multi_activity.go @@ -81,12 +81,12 @@ func marketingFJxw() *marketing2.Marketing { func MarketingSend() { - //openId := "oSNb4ftgnWC22Z0cWTjsQebdr2Yk" - //appId := "wx619991cc795028f5" + openId := "oSNb4ftgnWC22Z0cWTjsQebdr2Yk" + appId := "wx619991cc795028f5" //195516196845312409613 - openId := "ocuH-0Nymo4sJLRNabIBbg9H2XCo" - appId := "wx5d3e839568f24b2b" + //openId := "ocuH-0Nymo4sJLRNabIBbg9H2XCo" + //appId := "wx5d3e839568f24b2b" //respBody={"coupon_id":"116076813524"} //openId := "ocZ-njugTd_fgCJMHTG8PukPAVm4" @@ -99,9 +99,9 @@ func MarketingSend() { //19581007753265602565 //19581007753265602564 request := &marketing2.SendReq{ - ActivityId: utils.String("11941580000000005"), - StockId: utils.String("20847510"), - OutRequestNo: utils.String("195516196845312409613"), + ActivityId: utils.String("11941580000000012"), + StockId: utils.String("20964154"), + OutRequestNo: utils.String("196605139814767820814"), Appid: utils.String(appId), StockCreatorMchId: utils.String("1652465541"), } @@ -128,11 +128,7 @@ func MarketingSend() { fmt.Printf("请求成功: %s\n", *response.CouponId) } -func MarketingQuery() { - - appId := "wx619991cc795028f5" - openId := "oSNb4ftgnWC22Z0cWTjsQebdr2Yk" - couponId := "113831004454" +func MarketingQuery(appId, openId, couponId string) { response, err := marketing().Query(appId, openId, couponId) if err != nil { diff --git a/test/bank_multi_activity_test.go b/test/bank_multi_activity_test.go index 907c1bf..c5e1631 100644 --- a/test/bank_multi_activity_test.go +++ b/test/bank_multi_activity_test.go @@ -2,7 +2,7 @@ package test import "testing" -func TestMarketingSend(t *testing.T) { +func Test_MarketingSend(t *testing.T) { tests := []struct { name string }{ @@ -16,3 +16,22 @@ func TestMarketingSend(t *testing.T) { }) } } + +func Test_MarketingQuery(t *testing.T) { + tests := []struct { + name string + appId, openId, couponId string + }{ + { + name: "查询绑定多笔立减活动的代金券详情", // 查询的商户非创建方商户 查询商户要为创建方商户 + appId: "wx619991cc795028f5", + openId: "oSNb4ftgnWC22Z0cWTjsQebdr2Yk", + couponId: "122529292094", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + MarketingQuery(tt.appId, tt.openId, tt.couponId) + }) + } +} diff --git a/test/coupon.go b/test/coupon.go index d899b10..d2b107c 100644 --- a/test/coupon.go +++ b/test/coupon.go @@ -93,35 +93,9 @@ func QueryCoupon() { return } - //req := cashcoupons.QueryCouponRequest{ - // //CouponId: core.String("101270193400"), - // CouponId: core.String("101270193400"), - // Appid: core.String("wx619991cc795028f5"), - // Openid: core.String("oSNb4fmScVLmXILaolXVdBpJmYbQ"), - //} - - //req := cashcoupons.QueryCouponRequest{ - // CouponId: core.String("101274529925"), - // Appid: core.String("wx619991cc795028f5"), - // Openid: core.String("oSNb4fpnLNdkFPk-O43o6f2hxxRo"), - //} - - //appId := "wx619991cc795028f5" - //openId := "oSNb4ftgnWC22Z0cWTjsQebdr2Yk" - //couponId := "113831004454" - //req := cashcoupons.QueryCouponRequest{ - // CouponId: core.String(couponId), - // Appid: core.String(appId), - // Openid: core.String(openId), - //} - - //appId := "wx5d3e839568f24b2b" - //openId := "ocuH-0Nymo4sJLRNabIBbg9H2XCo" - //couponId := "116076813524" - - appId := "wxd27e255810842ba8" - openId := "o3dEt5cA8jt3Kz5wNzAO6-3YQHsE" - couponId := "129623470711" + appId := "wx619991cc795028f5" + openId := "oSNb4ftgnWC22Z0cWTjsQebdr2Yk" + couponId := "122529292094" req := cashcoupons.QueryCouponRequest{ CouponId: core.String(couponId), @@ -174,8 +148,8 @@ func QueryProduct() { } req := cashcoupons.QueryStockRequest{ - StockId: core.String("21104160"), - StockCreatorMchid: core.String("1715349578"), + StockId: core.String("20965520"), + StockCreatorMchid: core.String("1652465541"), } svc := cashcoupons.StockApiService{Client: client} From 7dccb67de3a78c2b0ee5219d5e085b337a5f2239 Mon Sep 17 00:00:00 2001 From: ziming Date: Wed, 10 Dec 2025 09:00:36 +0800 Subject: [PATCH 025/144] voucher --- test/bank_multi_activity.go | 31 +++---------------------------- test/bank_multi_activity_test.go | 16 ++++++++++++++-- test/coupon.go | 23 ++++++++++++++++------- 3 files changed, 33 insertions(+), 37 deletions(-) diff --git a/test/bank_multi_activity.go b/test/bank_multi_activity.go index e395cbf..8edd220 100644 --- a/test/bank_multi_activity.go +++ b/test/bank_multi_activity.go @@ -56,13 +56,9 @@ func marketingFJxw() *marketing2.Marketing { //openssl x509 -in xxx.pem -noout -serial mchId := "1652465541" - wechatPayPublicKeyId := "PUB_KEY_ID_0117109533612025031800326400002563" + wechatPayPublicKeyId := " PUB_KEY_ID_0116524655412025070900181741001803" certificateSerialNo := "1E3F2CE013203BA9C3DEFC5782FCD3329C3DAC1C" - //mchId := "1652465541" - //wechatPayPublicKeyId := "" - //certificateSerialNo := "1E3F2CE013203BA9C3DEFC5782FCD3329C3DAC1C" - filePath := fmt.Sprintf("%s/cert/wechat/%s", parentDir, mchId) c, err := utils.CreateMchConfig( @@ -84,20 +80,6 @@ func MarketingSend() { openId := "oSNb4ftgnWC22Z0cWTjsQebdr2Yk" appId := "wx619991cc795028f5" - //195516196845312409613 - //openId := "ocuH-0Nymo4sJLRNabIBbg9H2XCo" - //appId := "wx5d3e839568f24b2b" - //respBody={"coupon_id":"116076813524"} - - //openId := "ocZ-njugTd_fgCJMHTG8PukPAVm4" - //appId := "wxd9137161bc8f9ca9" - //respBody={"coupon_id":"117888124542"} - - //195516196845312409613 - //1958100775326560252 - //1958100775326560251 - //19581007753265602565 - //19581007753265602564 request := &marketing2.SendReq{ ActivityId: utils.String("11941580000000012"), StockId: utils.String("20964154"), @@ -128,13 +110,6 @@ func MarketingSend() { fmt.Printf("请求成功: %s\n", *response.CouponId) } -func MarketingQuery(appId, openId, couponId string) { - - response, err := marketing().Query(appId, openId, couponId) - if err != nil { - fmt.Print(err) - return - } - - fmt.Printf("请求成功: %+v\n", response) +func MarketingQuery(appId, openId, couponId string) (response *marketing2.SendResp, err error) { + return marketingFJxw().Query(appId, openId, couponId) } diff --git a/test/bank_multi_activity_test.go b/test/bank_multi_activity_test.go index c5e1631..e6963fc 100644 --- a/test/bank_multi_activity_test.go +++ b/test/bank_multi_activity_test.go @@ -26,12 +26,24 @@ func Test_MarketingQuery(t *testing.T) { name: "查询绑定多笔立减活动的代金券详情", // 查询的商户非创建方商户 查询商户要为创建方商户 appId: "wx619991cc795028f5", openId: "oSNb4ftgnWC22Z0cWTjsQebdr2Yk", - couponId: "122529292094", + couponId: "139923450432", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - MarketingQuery(tt.appId, tt.openId, tt.couponId) + resp, err := MarketingQuery(tt.appId, tt.openId, tt.couponId) + if err != nil { + t.Errorf("MarketingQuery() error = %v", err) + return + } + t.Logf("MarketingQuery() = %v", resp) }) } + + // 合单订单就是没有核销回调通知的 + // {"code":"APPID_MCHID_NOT_MATCH","message":"商户号与AppID不匹配"} + // APPID_MCHID_NOT_MATCH 商户号与AppID不匹配 调用接口的商户号需与接口传入的AppID有绑定关系 + + //openid不是自己的appid下的喔,这也能查询到吗” + //不行的,需要是在自己appid下的才能查到 } diff --git a/test/coupon.go b/test/coupon.go index d2b107c..9b8d40a 100644 --- a/test/coupon.go +++ b/test/coupon.go @@ -24,6 +24,15 @@ var bc = &conf.Bootstrap{ }, } +var bc2 = &conf.Bootstrap{ + Wechat: &conf.Wechat{ + MchID: "1652465541", // notifyUrl https://nsall.86698.cn/wechatPay/coupon_notify/fjxingwang + MchCertificateSerialNumber: "1E3F2CE013203BA9C3DEFC5782FCD3329C3DAC1C", + WechatPayPublicKeyID: "PUB_KEY_ID_0116524655412025070900181741001803", + Name: "福建兴旺", + }, +} + func SendCoupon() { ctx := context.Background() @@ -94,8 +103,8 @@ func QueryCoupon() { } appId := "wx619991cc795028f5" - openId := "oSNb4ftgnWC22Z0cWTjsQebdr2Yk" - couponId := "122529292094" + openId := "oSNb4fulXAleiammvWXnz1pRghAE" + couponId := "142388354994" req := cashcoupons.QueryCouponRequest{ CouponId: core.String(couponId), @@ -148,8 +157,8 @@ func QueryProduct() { } req := cashcoupons.QueryStockRequest{ - StockId: core.String("20965520"), - StockCreatorMchid: core.String("1652465541"), + StockId: core.String("21386505"), + StockCreatorMchid: core.String("1715349578"), } svc := cashcoupons.StockApiService{Client: client} @@ -226,9 +235,9 @@ func QueryCallback() { parentDir := filepath.Dir(dir) server := data.Server{ - MchID: bc.Wechat.MchID, - MchCertificateSerialNumber: bc.Wechat.MchCertificateSerialNumber, - WechatPayPublicKeyID: bc.Wechat.WechatPayPublicKeyID, + MchID: bc2.Wechat.MchID, + MchCertificateSerialNumber: bc2.Wechat.MchCertificateSerialNumber, + WechatPayPublicKeyID: bc2.Wechat.WechatPayPublicKeyID, Dir: parentDir, } client, err := data.GetClient(ctx, server) From 11e28adf6de04b1bbb2add078457398c7e84cec1 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 11 Dec 2025 11:13:04 +0800 Subject: [PATCH 026/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/config.yaml | 1 + gorm.sh | 2 +- ...chat_voucher_bo.go => wechat_notify_bo.go} | 29 +- internal/biz/multi.go | 258 ++++++++++++++++++ internal/biz/order.go | 4 + internal/biz/provider_set.go | 2 +- internal/biz/vo/cache.go | 6 + internal/biz/vo/multi_notify_log_status.go | 38 +++ internal/biz/voucher.go | 3 + internal/biz/wechat_notify.go | 4 + internal/conf/conf.pb.go | 220 ++++++++------- internal/conf/conf.proto | 1 + internal/data/repoimpl/provider_set.go | 2 + internal/pkg/helper/utils_test.go | 48 ++++ internal/server/http.go | 8 +- internal/service/provider_set.go | 1 + internal/service/qixing.go | 35 +++ 17 files changed, 547 insertions(+), 115 deletions(-) rename internal/biz/bo/{wechat_voucher_bo.go => wechat_notify_bo.go} (60%) create mode 100644 internal/biz/multi.go create mode 100644 internal/biz/vo/multi_notify_log_status.go create mode 100644 internal/service/qixing.go diff --git a/configs/config.yaml b/configs/config.yaml index ffd5222..984ed0f 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -66,6 +66,7 @@ cmb: cmbKeyAlias: "SM2_CMBLIFE" orgNo: "LANSEXIONGDI" # 发码机构号,固定值,掌上生活优惠券系统提供 notifyUrl: "https://sandbox.cdcc.cmbchina.com/AccessGateway/transIn/updateCodeStatus.json" # 招行测试回调地址 + multiNotifyUrl: "https://sandbox.cdcc.cmbchina.com/AccessGateway/transIn/updateCodeStatus.json" # 招行测试回调地址 noticeStartDays: 7 noticeEndDays: 1 diff --git a/gorm.sh b/gorm.sh index 52d91e3..5777d2c 100755 --- a/gorm.sh +++ b/gorm.sh @@ -6,7 +6,7 @@ # 3. 在指定目录下创建对应表的CRUD操作代码文件,定义针对该表的常见增删改查以及列表查询、按字段查询等操作方法。 # 设置数据库连接信息,注意密码部分如果包含特殊字符可能需要进行转义处理,这里示例中未做额外处理,需根据实际情况检查 -dsn="root:lansexiongdi6,@tcp(47.97.27.195:3306)/voucher?parseTime=True&loc=Local" +dsn="root:lsxddb123.@tcp(47.108.53.72:3306)/voucher?parseTime=True&loc=Local" # 获取当前脚本所在的绝对路径,即使脚本被在不同目录下调用也能正确定位相关文件和目录 SHELL_FOLDER=$(cd "$(dirname "\$0")" || exit; pwd) echo "$SHELL_FOLDER" diff --git a/internal/biz/bo/wechat_voucher_bo.go b/internal/biz/bo/wechat_notify_bo.go similarity index 60% rename from internal/biz/bo/wechat_voucher_bo.go rename to internal/biz/bo/wechat_notify_bo.go index 6c99585..b8238b8 100644 --- a/internal/biz/bo/wechat_voucher_bo.go +++ b/internal/biz/bo/wechat_notify_bo.go @@ -1,12 +1,18 @@ package bo -import "voucher/internal/biz/vo" +import ( + "encoding/json" + "fmt" + "time" + "voucher/internal/biz/vo" +) // ConsumeInformation 定义消费信息结构体 type ConsumeInformation struct { - ConsumeTime string `json:"consume_time"` - ConsumeMchid string `json:"consume_mchid"` - TransactionID string `json:"transaction_id"` + ConsumeTime time.Time `json:"consume_time"` // 核销时间 + ConsumeMchid string `json:"consume_mchid"` // 核销商户号 + TransactionID string `json:"transaction_id"` // 微信支付交易单号 + ConsumeAmount int `json:"consume_amount"` // 核销金额(单位:分) } // PlainText 定义明文数据结构体 @@ -17,11 +23,12 @@ type PlainText struct { CouponName string `json:"coupon_name"` Description string `json:"description"` Status vo.WechatVoucherStatus `json:"status"` - CreateTime string `json:"create_time"` + CreateTime time.Time `json:"create_time"` CouponType string `json:"coupon_type"` NoCash bool `json:"no_cash"` Singleitem bool `json:"singleitem"` - ConsumeInformation ConsumeInformation `json:"consume_information,omitempty"` + BusinessType string `json:"business_type"` // 业务类型 + ConsumeInformation *ConsumeInformation `json:"consume_information,omitempty"` } type WechatVoucherNotifyBo struct { @@ -34,3 +41,13 @@ type WechatVoucherNotifyBo struct { AssociatedData string `json:"associated_data"` PlainText PlainText `json:"plain_text"` } + +func (d *WechatVoucherNotifyBo) Str() (string, error) { + + b, err := json.Marshal(d) + if err != nil { + return "", fmt.Errorf("json marshal original_data error: %v", err) + } + + return string(b), nil +} diff --git a/internal/biz/multi.go b/internal/biz/multi.go new file mode 100644 index 0000000..7fef2f0 --- /dev/null +++ b/internal/biz/multi.go @@ -0,0 +1,258 @@ +package biz + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "github.com/go-kratos/kratos/v2/log" + "gorm.io/gorm" + v1 "voucher/api/v1" + "voucher/internal/biz/bo" + "voucher/internal/biz/cmb" + "voucher/internal/biz/mixrepos" + "voucher/internal/biz/repo" + "voucher/internal/biz/vo" + "voucher/internal/conf" + "voucher/internal/data" + "voucher/internal/pkg/lock" +) + +type MultiBiz struct { + bc *conf.Bootstrap + rdb *data.Rdb + Cmb *cmb.Cmb + ProductRepo repo.ProductRepo + OrderRepo repo.OrderRepo + MultiNotifyDataRepo repo.MultiNotifyDataRepo + MultiNotifyLogRepo repo.MultiNotifyLogRepo + CmbMixRepo mixrepos.CmbMixRepo +} + +func NewMultiBiz( + bc *conf.Bootstrap, + rdb *data.Rdb, + cmb *cmb.Cmb, + productRepo repo.ProductRepo, + orderRepo repo.OrderRepo, + multiNotifyDataRepo repo.MultiNotifyDataRepo, + multiNotifyLogRepo repo.MultiNotifyLogRepo, + cmbMixRepo mixrepos.CmbMixRepo, +) *MultiBiz { + return &MultiBiz{ + bc: bc, + rdb: rdb, + Cmb: cmb, + ProductRepo: productRepo, + OrderRepo: orderRepo, + MultiNotifyDataRepo: multiNotifyDataRepo, + MultiNotifyLogRepo: multiNotifyLogRepo, + CmbMixRepo: cmbMixRepo, + } +} + +func (biz *MultiBiz) Notify(ctx context.Context, source string, req *bo.WechatVoucherNotifyBo) error { + + cl := vo.MultiNotifyLockKey.BuildCache([]string{ + source, + req.PlainText.StockCreatorMchid, + req.PlainText.StockID, + req.PlainText.CouponID, + }) + + return lock.NewMutex(biz.rdb.Rdb, cl.TTL).Lock(ctx, cl.Key, func(ctx context.Context) error { + + order, err := biz.order(ctx, req) + if err != nil { + log.Errorf("[%s] multi notify error: %v,req:%+v", source, err, req) + return err + } + + if err = biz.Run(ctx, source, req, order); err != nil { + log.Errorf("[%s] multi notify error: %v,req:%+v", source, err, req) + return err + } + + return nil + }) + +} + +func (biz *MultiBiz) order(ctx context.Context, req *bo.WechatVoucherNotifyBo) (*bo.OrderBo, error) { + + order, err := biz.OrderRepo.GetByCouponId(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID, req.PlainText.CouponID) + if err != nil { + return nil, fmt.Errorf("订单查询错误 error: %v", err) + } + + return order, nil +} + +func (biz *MultiBiz) Run(ctx context.Context, source string, req *bo.WechatVoucherNotifyBo, order *bo.OrderBo) error { + + if order.ActivityId == "" { + return fmt.Errorf("批次活动ID为空,不是多笔立减金,请检查") + } + + mnd, err := biz.MultiNotifyDataRepo.GetByNotifyID(ctx, req.ID) + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return fmt.Errorf("查询通知数据错误 error: %v", err) + } + + if mnd != nil { + if mnd.NoticeNum > 0 { + log.Warnf("[%s] multi notify log already exists,req:%+v", source, req) + return nil + } + } else { + mnd, err = biz.mndCreate(ctx, source, req, order) + if err != nil { + return fmt.Errorf("创建通知数据错误 error: %v", err) + } + } + + nl, err := biz.nlCreate(ctx, req, mnd, order) + if err != nil { + return fmt.Errorf("创建通知日志错误 error: %v", err) + } + + return biz.Request(ctx, mnd, nl) +} + +func (biz *MultiBiz) mndCreate(ctx context.Context, source string, req *bo.WechatVoucherNotifyBo, order *bo.OrderBo) (*bo.MultiNotifyDataBo, error) { + + originalData, err := req.Str() + if err != nil { + return nil, fmt.Errorf("通知数据 json str 错误 error: %v", err) + } + + return biz.MultiNotifyDataRepo.Create(ctx, &bo.MultiNotifyDataBo{ + Source: source, + NotifyID: req.ID, + OrderNo: order.OrderNo, + OutBizNo: order.OutBizNo, + CouponID: req.PlainText.CouponID, + StockID: req.PlainText.StockID, + ConsumeAmount: int32(req.PlainText.ConsumeInformation.ConsumeAmount), + ConsumeTime: &req.PlainText.ConsumeInformation.ConsumeTime, + EventType: req.EventType, + OriginalData: originalData, + }) +} + +func (biz *MultiBiz) nlCreate(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) (*bo.MultiNotifyLogBo, error) { + + nl := &bo.MultiNotifyLogBo{ + MultiNotifyDataID: mnd.ID, + OrderNo: mnd.OrderNo, + OutBizNo: mnd.OutBizNo, + CouponID: mnd.CouponID, + ActivityNo: order.ProductNo, + StockID: mnd.StockID, + EventType: mnd.EventType, + Status: req.PlainText.Status.GetValue(), + ConsumeAmount: mnd.ConsumeAmount, + ConsumeTime: mnd.ConsumeTime, + TransactionID: req.PlainText.ConsumeInformation.TransactionID, + RequestURL: order.NotifyUrl, + RequestStatus: vo.MultiNotifyLogStatusWait.GetValue(), + OrderCreateTime: order.CreateTime, + CouponCreateTime: &req.PlainText.CreateTime, + } + request, err := biz.GetRequest(ctx, nl) + if err != nil { + return nil, err + } + b, _ := json.Marshal(request) + nl.Request = string(b) + + return biz.MultiNotifyLogRepo.Create(ctx, nl) +} + +func (biz *MultiBiz) bizContent(nl *bo.MultiNotifyLogBo) (string, error) { + + req := &v1.CmbNotifyRequest{ // 待确定 + Ticket: nl.OrderNo, + TransDate: "", // 格式yyyy-mm-dd hh:mm:ss.sss + OrgNo: biz.bc.Cmb.OrgNo, + //Attach: nl.Attach, + Ext: "", + } + + bizJsonBytes, err := json.Marshal(req) + if err != nil { + return "", err + } + + return string(bizJsonBytes), nil +} + +func (biz *MultiBiz) GetRequest(ctx context.Context, nl *bo.MultiNotifyLogBo) (*v1.CmbRequest, error) { + + bizContent, err := biz.bizContent(nl) + if err != nil { + return nil, err + } + + request, err := biz.CmbMixRepo.GetRequest(ctx, &bo.CmbRequestBo{ + FuncName: vo.CmbNotifyFuncName, // 待确定 + BizContent: bizContent, + }) + if err != nil { + return nil, err + } + + return request, nil +} + +func (biz *MultiBiz) Request(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl *bo.MultiNotifyLogBo) error { + + if nl.RequestURL == "" { + return biz.notifyFail(ctx, nl, "回调通知招行地址为空") + } + + request, err := biz.GetRequest(ctx, nl) + if err != nil { + return err + } + + reply, err := biz.CmbMixRepo.Request(ctx, request, nl.RequestURL) + if err != nil { + if err2 := biz.notifyFail(ctx, nl, err.Error()); err2 != nil { + return err2 + } + return err + } + + if err = biz.CmbMixRepo.VerifyResponse(ctx, reply); err != nil { + errMsg := fmt.Sprintf("回调通知招行返回验证结果发生错误,rep:%+v error:%s", reply, err.Error()) + if err2 := biz.notifyFail(ctx, nl, errMsg); err2 != nil { + return err2 + } + return err + } + + return biz.notifySuccess(ctx, mmd, nl, reply) +} + +func (biz *MultiBiz) notifyFail(ctx context.Context, nl *bo.MultiNotifyLogBo, remark string) error { + return biz.MultiNotifyLogRepo.Fail(ctx, nl.ID, remark) +} + +func (biz *MultiBiz) notifySuccess(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl *bo.MultiNotifyLogBo, reply *v1.CmbReply) error { + + response, err := json.Marshal(reply) + if err != nil { + return err + } + + if err = biz.MultiNotifyLogRepo.Success(ctx, nl.ID, string(response)); err != nil { + return err + } + + if err = biz.MultiNotifyDataRepo.AddNoticeNum(ctx, mmd.ID); err != nil { + return err + } + + return nil +} diff --git a/internal/biz/order.go b/internal/biz/order.go index 4ef70cc..c00dda2 100644 --- a/internal/biz/order.go +++ b/internal/biz/order.go @@ -105,6 +105,10 @@ func (this *VoucherBiz) create(ctx context.Context, req *bo.OrderCreateReqBo, pr ActivityId: product.ActivityId, // 多笔立减活动 } + if product.ActivityId != "" { + o.NotifyUrl = this.bc.Cmb.MultiNotifyUrl + } + return this.OrderRepo.Create(ctx, o) } diff --git a/internal/biz/provider_set.go b/internal/biz/provider_set.go index f9f9c14..1b2e794 100644 --- a/internal/biz/provider_set.go +++ b/internal/biz/provider_set.go @@ -5,4 +5,4 @@ import ( ) // ProviderSetBiz is biz providers. -var ProviderSetBiz = wire.NewSet(NewVoucherBiz) +var ProviderSetBiz = wire.NewSet(NewVoucherBiz, NewMultiBiz) diff --git a/internal/biz/vo/cache.go b/internal/biz/vo/cache.go index 0650a04..23f5ff7 100644 --- a/internal/biz/vo/cache.go +++ b/internal/biz/vo/cache.go @@ -29,6 +29,10 @@ const ( ProductQueryLockKey CacheKey = "product_query_lock" ) +const ( + MultiNotifyLockKey CacheKey = "multi_notify_lock_key" +) + var ( WarningBudgetCron CacheKey = "warning_budget_cron" WarningBudgetSendIncr CacheKey = "warning_budget_incr" @@ -51,6 +55,8 @@ var CacheKeyMap = map[CacheKey]time.Duration{ WarningBudgetSendIncr: 3 * time.Hour, WarningBudgetCron: 5 * time.Minute, + + MultiNotifyLockKey: 30 * time.Second, } type Cache struct { diff --git a/internal/biz/vo/multi_notify_log_status.go b/internal/biz/vo/multi_notify_log_status.go new file mode 100644 index 0000000..362998d --- /dev/null +++ b/internal/biz/vo/multi_notify_log_status.go @@ -0,0 +1,38 @@ +package vo + +type MultiNotifyLogStatus int32 + +const ( + MultiNotifyLogStatusWait MultiNotifyLogStatus = iota + 1 + MultiNotifyLogStatusSuccess + MultiNotifyLogStatusFail +) + +var MultiNotifyLogStatusMap = map[MultiNotifyLogStatus]string{ + MultiNotifyLogStatusWait: "待请求", + MultiNotifyLogStatusSuccess: "请求成功", + MultiNotifyLogStatusFail: "请求失败", +} + +func (s MultiNotifyLogStatus) GetText() string { + if t, ok := MultiNotifyLogStatusMap[s]; ok { + return t + } + return "未知请求状态" +} + +func (s MultiNotifyLogStatus) GetValue() int32 { + return int32(s) +} + +func (s MultiNotifyLogStatus) IsWait() bool { + return s == MultiNotifyLogStatusWait +} + +func (s MultiNotifyLogStatus) IsSuccess() bool { + return s == MultiNotifyLogStatusSuccess +} + +func (s MultiNotifyLogStatus) IsFail() bool { + return s == MultiNotifyLogStatusFail +} diff --git a/internal/biz/voucher.go b/internal/biz/voucher.go index e21e639..9e743be 100644 --- a/internal/biz/voucher.go +++ b/internal/biz/voucher.go @@ -26,6 +26,7 @@ type VoucherBiz struct { DingMixRepo mixrepos.DingMixRepo CmbMixRepo mixrepos.CmbMixRepo SmsMixRepo mixrepos.SmsMixRepo + MultiBiz *MultiBiz mu sync.RWMutex queryMap map[string]bool @@ -47,6 +48,7 @@ func NewVoucherBiz( DingMixRepo mixrepos.DingMixRepo, CmbMixRepo mixrepos.CmbMixRepo, SmsMixRepo mixrepos.SmsMixRepo, + MultiBiz *MultiBiz, ) *VoucherBiz { return &VoucherBiz{ bc: bc, @@ -64,6 +66,7 @@ func NewVoucherBiz( DingMixRepo: DingMixRepo, CmbMixRepo: CmbMixRepo, SmsMixRepo: SmsMixRepo, + MultiBiz: MultiBiz, queryMap: make(map[string]bool), } diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index 32d8bf4..c939be8 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -22,6 +22,10 @@ func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, tag string, re return err } + if order.ActivityId != "" { + return this.MultiBiz.Run(ctx, "lsxd_"+order.MerchantNo, req, order) + } + if req.PlainText.Status.IsSended() { return this.available(ctx, order) diff --git a/internal/conf/conf.pb.go b/internal/conf/conf.pb.go index 10176f4..5b9b766 100644 --- a/internal/conf/conf.pb.go +++ b/internal/conf/conf.pb.go @@ -486,6 +486,7 @@ type Cmb struct { CmbKeyAlias string `protobuf:"bytes,8,opt,name=cmbKeyAlias,proto3" json:"cmbKeyAlias,omitempty"` OrgNo string `protobuf:"bytes,9,opt,name=orgNo,proto3" json:"orgNo,omitempty"` NotifyUrl string `protobuf:"bytes,10,opt,name=notifyUrl,proto3" json:"notifyUrl,omitempty"` + MultiNotifyUrl string `protobuf:"bytes,13,opt,name=multiNotifyUrl,proto3" json:"multiNotifyUrl,omitempty"` NoticeStartDays int64 `protobuf:"varint,11,opt,name=noticeStartDays,proto3" json:"noticeStartDays,omitempty"` NoticeEndDays int64 `protobuf:"varint,12,opt,name=noticeEndDays,proto3" json:"noticeEndDays,omitempty"` } @@ -592,6 +593,13 @@ func (x *Cmb) GetNotifyUrl() string { return "" } +func (x *Cmb) GetMultiNotifyUrl() string { + if x != nil { + return x.MultiNotifyUrl + } + return "" +} + func (x *Cmb) GetNoticeStartDays() int64 { if x != nil { return x.NoticeStartDays @@ -1626,7 +1634,7 @@ var file_conf_conf_proto_rawDesc = []byte{ 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x50, 0x61, 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xd7, 0x02, 0x0a, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xff, 0x02, 0x0a, 0x03, 0x43, 0x6d, 0x62, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6d, 0x32, 0x50, @@ -1643,113 +1651,115 @@ var file_conf_conf_proto_rawDesc = []byte{ 0x61, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x72, 0x67, 0x4e, 0x6f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x4e, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x72, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x79, 0x55, 0x72, 0x6c, 0x12, 0x28, 0x0a, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, - 0x53, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x79, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x79, 0x73, - 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x64, 0x44, 0x61, 0x79, - 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x45, - 0x6e, 0x64, 0x44, 0x61, 0x79, 0x73, 0x22, 0xc6, 0x02, 0x0a, 0x0e, 0x57, 0x65, 0x63, 0x68, 0x61, - 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4d, 0x51, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x61, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1e, 0x0a, - 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x14, 0x0a, - 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, - 0x70, 0x69, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, - 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, - 0x26, 0x0a, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x55, 0x72, - 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x54, 0x61, 0x67, 0x55, 0x72, 0x6c, 0x12, 0x26, 0x0a, 0x0e, 0x69, 0x73, 0x4f, 0x70, 0x65, - 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0e, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x22, - 0x9b, 0x01, 0x0a, 0x05, 0x41, 0x6c, 0x61, 0x72, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x77, 0x65, 0x62, - 0x68, 0x6f, 0x6f, 0x6b, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, - 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x55, 0x52, 0x4c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x74, 0x41, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x05, 0x61, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x74, 0x4d, 0x6f, 0x62, - 0x69, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x74, 0x4d, 0x6f, - 0x62, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, - 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x77, - 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x84, 0x02, - 0x0a, 0x04, 0x43, 0x72, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x44, - 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x18, 0x02, 0x20, 0x03, - 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x2e, 0x43, 0x72, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, - 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x4d, 0x61, 0x70, 0x1a, 0x3e, 0x0a, 0x0a, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, - 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, - 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x5e, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, - 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x35, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, - 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x72, 0x6f, 0x6e, 0x2e, 0x43, - 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc4, 0x04, 0x0a, 0x05, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x12, 0x3d, - 0x0a, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, - 0x52, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x4f, 0x0a, - 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x6c, 0x69, 0x63, 0x65, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, + 0x69, 0x66, 0x79, 0x55, 0x72, 0x6c, 0x12, 0x26, 0x0a, 0x0e, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x4e, + 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x72, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, + 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x72, 0x6c, 0x12, 0x28, + 0x0a, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x79, + 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x53, + 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x79, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x6f, 0x74, 0x69, + 0x63, 0x65, 0x45, 0x6e, 0x64, 0x44, 0x61, 0x79, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, + 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x64, 0x44, 0x61, 0x79, 0x73, 0x22, 0xc6, + 0x02, 0x0a, 0x0e, 0x57, 0x65, 0x63, 0x68, 0x61, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4d, + 0x51, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, + 0x79, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x67, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x67, + 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, + 0x65, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, + 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x06, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x74, + 0x61, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x18, 0x0a, + 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, + 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, + 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x55, 0x72, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x55, 0x72, 0x6c, 0x12, + 0x26, 0x0a, 0x0e, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, + 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x43, + 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x22, 0x9b, 0x01, 0x0a, 0x05, 0x41, 0x6c, 0x61, 0x72, + 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x55, 0x52, 0x4c, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x55, 0x52, + 0x4c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x74, 0x41, + 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x74, 0x41, 0x6c, 0x6c, 0x12, + 0x1c, 0x0a, 0x09, 0x61, 0x74, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x09, 0x61, 0x74, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x26, 0x0a, + 0x0e, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x73, 0x18, + 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x6f, + 0x62, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x84, 0x02, 0x0a, 0x04, 0x43, 0x72, 0x6f, 0x6e, 0x12, 0x16, + 0x0a, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, + 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x44, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, + 0x64, 0x4d, 0x61, 0x70, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x76, 0x6f, 0x75, + 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x72, 0x6f, 0x6e, + 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x1a, 0x3e, 0x0a, 0x0a, + 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, + 0x4f, 0x70, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4f, 0x70, + 0x65, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x5e, 0x0a, 0x0f, + 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, + 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, + 0x79, 0x12, 0x35, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x1f, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x2e, 0x43, 0x72, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, + 0x70, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc4, 0x04, 0x0a, + 0x05, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x12, 0x3d, 0x0a, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, - 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, - 0x54, 0x69, 0x6d, 0x65, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x3d, - 0x0a, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x52, 0x65, 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, + 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, + 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x4f, 0x0a, 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x54, + 0x69, 0x6d, 0x65, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, - 0x52, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x3d, 0x0a, - 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, - 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, - 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x47, 0x0a, 0x10, - 0x6f, 0x72, 0x64, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x74, 0x72, 0x79, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, - 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, - 0x65, 0x75, 0x65, 0x52, 0x10, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, - 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x3b, 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x64, 0x4e, 0x6f, 0x74, - 0x69, 0x66, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, - 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, - 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x64, 0x4e, 0x6f, 0x74, 0x69, - 0x66, 0x79, 0x1a, 0xa6, 0x01, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x74, 0x72, - 0x79, 0x4e, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x72, 0x65, 0x74, 0x72, - 0x79, 0x4e, 0x75, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x75, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x65, - 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6e, 0x75, 0x6d, 0x57, 0x6f, 0x72, - 0x6b, 0x65, 0x72, 0x73, 0x12, 0x35, 0x0a, 0x08, 0x77, 0x61, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x08, 0x77, 0x61, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x22, 0xb9, 0x01, 0x0a, 0x09, - 0x41, 0x6c, 0x69, 0x59, 0x75, 0x6e, 0x53, 0x6d, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x61, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, - 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, - 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x22, 0x3a, 0x0a, 0x04, 0x4c, 0x6f, 0x67, 0x73, 0x12, - 0x1a, 0x0a, 0x08, 0x62, 0x75, 0x73, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x08, 0x62, 0x75, 0x73, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x63, - 0x65, 0x73, 0x73, 0x42, 0x17, 0x5a, 0x15, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2f, 0x63, - 0x70, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x3b, 0x63, 0x6f, 0x6e, 0x66, 0x62, 0x06, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x33, + 0x52, 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x6c, 0x69, 0x63, + 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x3d, 0x0a, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, + 0x52, 0x65, 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, + 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, + 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, + 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x3d, 0x0a, 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, + 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, + 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x12, 0x47, 0x0a, 0x10, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x4e, 0x6f, 0x74, + 0x69, 0x66, 0x79, 0x52, 0x65, 0x74, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, + 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, + 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x10, 0x6f, 0x72, 0x64, + 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x3b, 0x0a, + 0x0a, 0x75, 0x73, 0x65, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x0a, + 0x75, 0x73, 0x65, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x1a, 0xa6, 0x01, 0x0a, 0x05, 0x51, + 0x75, 0x65, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x4f, 0x70, + 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, + 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, + 0x28, 0x0d, 0x52, 0x08, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x75, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, + 0x6e, 0x75, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, + 0x52, 0x0a, 0x6e, 0x75, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x35, 0x0a, 0x08, + 0x77, 0x61, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, + 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x77, 0x61, 0x69, 0x74, 0x54, + 0x69, 0x6d, 0x65, 0x22, 0xb9, 0x01, 0x0a, 0x09, 0x41, 0x6c, 0x69, 0x59, 0x75, 0x6e, 0x53, 0x6d, + 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, + 0x79, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, + 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, + 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x69, 0x67, + 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x69, 0x67, + 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, + 0x65, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, + 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x22, + 0x3a, 0x0a, 0x04, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x75, 0x73, 0x69, 0x6e, + 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x75, 0x73, 0x69, 0x6e, + 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x17, 0x5a, 0x15, 0x76, + 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2f, 0x63, 0x70, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x3b, + 0x63, 0x6f, 0x6e, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/internal/conf/conf.proto b/internal/conf/conf.proto index 5787df9..f56e09f 100644 --- a/internal/conf/conf.proto +++ b/internal/conf/conf.proto @@ -88,6 +88,7 @@ message Cmb { string cmbKeyAlias = 8; string orgNo = 9; string notifyUrl = 10; + string multiNotifyUrl = 13; int64 noticeStartDays = 11; int64 noticeEndDays = 12; } diff --git a/internal/data/repoimpl/provider_set.go b/internal/data/repoimpl/provider_set.go index 9fd0991..dc58fc5 100644 --- a/internal/data/repoimpl/provider_set.go +++ b/internal/data/repoimpl/provider_set.go @@ -11,4 +11,6 @@ var ProviderRepoImplSet = wire.NewSet( NewOrderNotifyRepoImpl, NewWechatNotifyRegisterTagRepoImpl, NewOrderBakRepoImpl, + NewMultiNotifyDataRepoImpl, + NewMultiNotifyLogRepoImpl, ) diff --git a/internal/pkg/helper/utils_test.go b/internal/pkg/helper/utils_test.go index b8d2096..d09f8d6 100644 --- a/internal/pkg/helper/utils_test.go +++ b/internal/pkg/helper/utils_test.go @@ -1,9 +1,11 @@ package helper import ( + "encoding/json" "fmt" "testing" "time" + "voucher/internal/biz/do" ) func TestHashMod(t *testing.T) { @@ -46,3 +48,49 @@ func TestMd5(t *testing.T) { s := Md5(`{"product_no":"","start_time":"2025-04-20 09:00:00","end_time":"2025-05-01 00:00:00"}`) t.Log(s) } + +func TestLength(t *testing.T) { + + jsonStr := `{ + "id": "4ab2699d-e91d-5460-9810-25fd6d4c69a5", + "create_time": "2025-12-08T17:54:24+08:00", + "resource_type": "encrypt-resource", + "event_type": "COUPON.USE", + "summary": "代金券核销通知", + "original_type": "coupon", + "associated_data": "coupon", + "plain_text": { + "stock_creator_mchid": "1652465541", + "stock_id": "21386484", + "coupon_id": "142388354994", + "coupon_name": "银行卡多笔立减", + "description": "", + "status": "SENDED", + "create_time": "2025-12-08T17:50:48+08:00", + "coupon_type": "NORMAL", + "no_cash": false, + "singleitem": false, + "business_type": "", + "consume_information": { + "consume_time": "2025-12-08T17:54:24+08:00", + "consume_mchid": "1274938601", + "transaction_id": "4200002996202512083063051834", + "consume_amount": 16 + } + } +}` + s := len(jsonStr) + t.Log(s) + + var notify do.CouponNotification + err := json.Unmarshal([]byte(jsonStr), ¬ify) + if err != nil { + panic(err) + } + + // 输出验证 + fmt.Println("代金券ID:", notify.PlainText.CouponID) + fmt.Println("核销金额(分):", notify.PlainText.ConsumeInformation.ConsumeAmount) + fmt.Println("核销时间:", notify.PlainText.ConsumeInformation.ConsumeTime) + fmt.Println("核销时间:", notify.PlainText.ConsumeInformation.ConsumeTime.Format(time.DateTime)) +} diff --git a/internal/server/http.go b/internal/server/http.go index 55e33d6..2758dd4 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -11,7 +11,6 @@ import ( "github.com/go-kratos/kratos/v2/middleware/recovery" "github.com/go-kratos/kratos/v2/middleware/validate" "github.com/go-kratos/kratos/v2/transport/http" - "github.com/go-kratos/kratos/v2/transport/http/pprof" "github.com/gorilla/handlers" http2 "net/http" "time" @@ -27,15 +26,20 @@ func NewHTTPServer( log *log.Helper, accessLogger *log2.AccessLogger, cmb *service.CmbService, + tripartiteService *service.TripartiteService, ) *http.Server { //构建 server srv := buildHTTPServer(c, accessLogger, log) - srv.Handle("/voucher/debug/pprof/", pprof.NewHandler()) + + //srv.Handle("/voucher/debug/pprof/", pprof.NewHandler()) srv.Route("/voucher/").GET("ping", func(ctx http.Context) error { return ctx.String(http2.StatusOK, "pong") }) + // 启星 /voucher/qiXing/v1/notify + srv.Route("/voucher/").POST("qiXing/v1/notify", tripartiteService.QiXingNotify) + // 订单通知重试 -- 不健全 srv.Route("/voucher/").POST("orderNotifyRetry", cmb.OrderNotifyRetry) // 重试通知 diff --git a/internal/service/provider_set.go b/internal/service/provider_set.go index 984a7a0..fd9e579 100644 --- a/internal/service/provider_set.go +++ b/internal/service/provider_set.go @@ -8,4 +8,5 @@ import ( var ProviderSetService = wire.NewSet( NewVoucherService, NewCmbService, + NewTripartiteService, ) diff --git a/internal/service/qixing.go b/internal/service/qixing.go new file mode 100644 index 0000000..5c51940 --- /dev/null +++ b/internal/service/qixing.go @@ -0,0 +1,35 @@ +package service + +import ( + "encoding/json" + "fmt" + "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/transport/http" + "io" + "voucher/internal/biz" + "voucher/internal/biz/bo" +) + +type TripartiteService struct { + multiBiz *biz.MultiBiz +} + +func NewTripartiteService(multiBiz *biz.MultiBiz) *TripartiteService { + return &TripartiteService{multiBiz: multiBiz} +} + +func (srv *TripartiteService) QiXingNotify(ctx http.Context) error { + + bodyBytes, err := io.ReadAll(ctx.Request().Body) + if err != nil { + return fmt.Errorf("read body error: %v", err) + } + + var req *bo.WechatVoucherNotifyBo + if err = json.Unmarshal(bodyBytes, &req); err != nil { + log.Errorf("qixing notify error:%v,body:%s", err, string(bodyBytes)) + return fmt.Errorf("json unmarshal bodyBytes error: %v", err) + } + + return srv.multiBiz.Notify(ctx, "qixing_"+req.PlainText.StockCreatorMchid, req) +} From 2edd4fd8cb80420e55a6abab66369868a8c61c46 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 11 Dec 2025 11:13:13 +0800 Subject: [PATCH 027/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/bo/multi_notify_data_bo.go | 21 +++ internal/biz/bo/multi_notify_log_bo.go | 27 ++++ internal/biz/repo/multi_notify_data.go | 14 ++ internal/biz/repo/multi_notify_log.go | 13 ++ internal/data/model/multi_notify_data.gen.go | 34 +++++ internal/data/model/multi_notify_log.gen.go | 40 ++++++ internal/data/repoimpl/multi_notify_data.go | 129 +++++++++++++++++++ internal/data/repoimpl/multi_notify_log.go | 128 ++++++++++++++++++ 8 files changed, 406 insertions(+) create mode 100644 internal/biz/bo/multi_notify_data_bo.go create mode 100644 internal/biz/bo/multi_notify_log_bo.go create mode 100644 internal/biz/repo/multi_notify_data.go create mode 100644 internal/biz/repo/multi_notify_log.go create mode 100644 internal/data/model/multi_notify_data.gen.go create mode 100644 internal/data/model/multi_notify_log.gen.go create mode 100644 internal/data/repoimpl/multi_notify_data.go create mode 100644 internal/data/repoimpl/multi_notify_log.go diff --git a/internal/biz/bo/multi_notify_data_bo.go b/internal/biz/bo/multi_notify_data_bo.go new file mode 100644 index 0000000..8f541cf --- /dev/null +++ b/internal/biz/bo/multi_notify_data_bo.go @@ -0,0 +1,21 @@ +package bo + +import "time" + +// MultiNotifyDataBo 领域实体Bo结构,字段和模型字段保持一致 +type MultiNotifyDataBo struct { + ID int64 + Source string + NotifyID string + OrderNo string + OutBizNo string + CouponID string + StockID string + ConsumeAmount int32 + ConsumeTime *time.Time + EventType string + OriginalData string + NoticeNum int32 + CreateTime *time.Time + UpdateTime *time.Time +} diff --git a/internal/biz/bo/multi_notify_log_bo.go b/internal/biz/bo/multi_notify_log_bo.go new file mode 100644 index 0000000..487ab23 --- /dev/null +++ b/internal/biz/bo/multi_notify_log_bo.go @@ -0,0 +1,27 @@ +package bo + +import "time" + +// MultiNotifyLogBo 领域实体Bo结构,字段和模型字段保持一致 +type MultiNotifyLogBo struct { + ID int64 + MultiNotifyDataID int64 + OrderNo string + OutBizNo string + CouponID string + ActivityNo string + StockID string + EventType string + Status string + ConsumeAmount int32 + ConsumeTime *time.Time + TransactionID string + RequestURL string + RequestStatus int32 + Request string + Response string + OrderCreateTime *time.Time + CouponCreateTime *time.Time + CreateTime *time.Time + UpdateTime *time.Time +} diff --git a/internal/biz/repo/multi_notify_data.go b/internal/biz/repo/multi_notify_data.go new file mode 100644 index 0000000..b0b9cc5 --- /dev/null +++ b/internal/biz/repo/multi_notify_data.go @@ -0,0 +1,14 @@ +package repo + +import ( + "context" + "voucher/internal/biz/bo" +) + +type MultiNotifyDataRepo interface { + FindNoticeNumZero(ctx context.Context, fun func(ctx context.Context, rows []*bo.MultiNotifyDataBo) error) error + Create(ctx context.Context, req *bo.MultiNotifyDataBo) (*bo.MultiNotifyDataBo, error) + GetByID(ctx context.Context, id int64) (*bo.MultiNotifyDataBo, error) + GetByNotifyID(ctx context.Context, notifyId string) (*bo.MultiNotifyDataBo, error) + AddNoticeNum(ctx context.Context, id int64) error +} diff --git a/internal/biz/repo/multi_notify_log.go b/internal/biz/repo/multi_notify_log.go new file mode 100644 index 0000000..a43f049 --- /dev/null +++ b/internal/biz/repo/multi_notify_log.go @@ -0,0 +1,13 @@ +package repo + +import ( + "context" + "voucher/internal/biz/bo" +) + +type MultiNotifyLogRepo interface { + Create(ctx context.Context, req *bo.MultiNotifyLogBo) (*bo.MultiNotifyLogBo, error) + GetByID(ctx context.Context, id int64) (*bo.MultiNotifyLogBo, error) + Success(ctx context.Context, id int64, response string) error + Fail(ctx context.Context, id int64, remark string) error +} diff --git a/internal/data/model/multi_notify_data.gen.go b/internal/data/model/multi_notify_data.gen.go new file mode 100644 index 0000000..f66a190 --- /dev/null +++ b/internal/data/model/multi_notify_data.gen.go @@ -0,0 +1,34 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package model + +import ( + "time" +) + +const TableNameMultiNotifyDatum = "multi_notify_data" + +// MultiNotifyDatum mapped from table +type MultiNotifyDatum struct { + ID int64 `gorm:"column:id;primaryKey" json:"id"` + Source string `gorm:"column:source;not null;comment:来源" json:"source"` // 来源 + NotifyID string `gorm:"column:notify_id;not null;comment:回调通知id" json:"notify_id"` // 回调通知id + OrderNo string `gorm:"column:order_no;not null;comment:订单号" json:"order_no"` // 订单号 + OutBizNo string `gorm:"column:out_biz_no;not null;comment:外部业务号" json:"out_biz_no"` // 外部业务号 + CouponID string `gorm:"column:coupon_id;not null;comment:券id" json:"coupon_id"` // 券id + StockID string `gorm:"column:stock_id;not null;comment:微信批次号" json:"stock_id"` // 微信批次号 + ConsumeAmount int32 `gorm:"column:consume_amount;not null;comment:核销金额" json:"consume_amount"` // 核销金额 + ConsumeTime *time.Time `gorm:"column:consume_time;not null;comment:核销时间" json:"consume_time"` // 核销时间 + EventType string `gorm:"column:event_type;not null;comment:通知的类型" json:"event_type"` // 通知的类型 + OriginalData string `gorm:"column:original_data;not null;comment:微信回调通知原始数据" json:"original_data"` // 微信回调通知原始数据 + NoticeNum int32 `gorm:"column:notice_num;not null;comment:通知下游次数" json:"notice_num"` // 通知下游次数 + CreateTime *time.Time `gorm:"column:create_time;not null;comment:创建时间" json:"create_time"` // 创建时间 + UpdateTime *time.Time `gorm:"column:update_time;comment:修改时间" json:"update_time"` // 修改时间 +} + +// TableName MultiNotifyDatum's table name +func (*MultiNotifyDatum) TableName() string { + return TableNameMultiNotifyDatum +} diff --git a/internal/data/model/multi_notify_log.gen.go b/internal/data/model/multi_notify_log.gen.go new file mode 100644 index 0000000..8a459e2 --- /dev/null +++ b/internal/data/model/multi_notify_log.gen.go @@ -0,0 +1,40 @@ +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. +// Code generated by gorm.io/gen. DO NOT EDIT. + +package model + +import ( + "time" +) + +const TableNameMultiNotifyLog = "multi_notify_log" + +// MultiNotifyLog mapped from table +type MultiNotifyLog struct { + ID int64 `gorm:"column:id;primaryKey;autoIncrement:true" json:"id"` + MultiNotifyDataID int64 `gorm:"column:multi_notify_data_id;not null" json:"multi_notify_data_id"` + OrderNo string `gorm:"column:order_no;not null;comment:订单号" json:"order_no"` // 订单号 + OutBizNo string `gorm:"column:out_biz_no;not null;comment:外部请求号" json:"out_biz_no"` // 外部请求号 + CouponID string `gorm:"column:coupon_id;not null;comment:微信券id" json:"coupon_id"` // 微信券id + ActivityNo string `gorm:"column:activity_no;not null;comment:活动编号CMB开头" json:"activity_no"` // 活动编号CMB开头 + StockID string `gorm:"column:stock_id;not null;comment:微信批次号" json:"stock_id"` // 微信批次号 + EventType string `gorm:"column:event_type;not null;comment:通知类型 COUPON.USE" json:"event_type"` // 通知类型 COUPON.USE + Status string `gorm:"column:status;not null;comment:券状态" json:"status"` // 券状态 + ConsumeAmount int32 `gorm:"column:consume_amount;not null;comment:核销金额" json:"consume_amount"` // 核销金额 + ConsumeTime *time.Time `gorm:"column:consume_time;not null;comment:核销时间" json:"consume_time"` // 核销时间 + TransactionID string `gorm:"column:transaction_id;not null;comment:微信支付系统生成的订单号" json:"transaction_id"` // 微信支付系统生成的订单号 + RequestURL string `gorm:"column:request_url;not null;comment:请求地址" json:"request_url"` // 请求地址 + RequestStatus int32 `gorm:"column:request_status;not null;comment:请求状态" json:"request_status"` // 请求状态 + Request string `gorm:"column:request;not null;comment:请求数据" json:"request"` // 请求数据 + Response string `gorm:"column:response;not null;comment:响应结果" json:"response"` // 响应结果 + OrderCreateTime *time.Time `gorm:"column:order_create_time;not null;comment:券收单时间-蓝色兄弟" json:"order_create_time"` // 券收单时间-蓝色兄弟 + CouponCreateTime *time.Time `gorm:"column:coupon_create_time;not null;comment:券创建时间-微信侧" json:"coupon_create_time"` // 券创建时间-微信侧 + CreateTime *time.Time `gorm:"column:create_time;not null;comment:创建时间" json:"create_time"` // 创建时间 + UpdateTime *time.Time `gorm:"column:update_time;comment:修改时间" json:"update_time"` // 修改时间 +} + +// TableName MultiNotifyLog's table name +func (*MultiNotifyLog) TableName() string { + return TableNameMultiNotifyLog +} diff --git a/internal/data/repoimpl/multi_notify_data.go b/internal/data/repoimpl/multi_notify_data.go new file mode 100644 index 0000000..95ff99a --- /dev/null +++ b/internal/data/repoimpl/multi_notify_data.go @@ -0,0 +1,129 @@ +package repoimpl + +import ( + "context" + "gorm.io/gorm" + "time" + "voucher/internal/biz/bo" + "voucher/internal/biz/repo" + "voucher/internal/data" + "voucher/internal/data/model" +) + +// MultiNotifyDataRepoImpl . +type MultiNotifyDataRepoImpl struct { + Base[model.MultiNotifyDatum, bo.MultiNotifyDataBo] + db *data.Db +} + +// NewMultiNotifyDataRepoImpl . +func NewMultiNotifyDataRepoImpl(db *data.Db) repo.MultiNotifyDataRepo { + return &MultiNotifyDataRepoImpl{db: db} +} + +func (p *MultiNotifyDataRepoImpl) DB(ctx context.Context) *gorm.DB { + return p.db.DB(ctx).WithContext(ctx).Model(model.MultiNotifyDatum{}) +} + +func (p *MultiNotifyDataRepoImpl) FindNoticeNumZero(ctx context.Context, fun func(ctx context.Context, rows []*bo.MultiNotifyDataBo) error) error { + + tx := p.DB(ctx).Where("notice_num = 0") + tx.Order("id asc") // 显式清除排序,移除默认的 ORDER BY + tx.Limit(200) + + var results = make([]*model.MultiNotifyDatum, 0) + + result := tx.FindInBatches(&results, 50, func(tx *gorm.DB, batch int) error { + return fun(ctx, p.ToBos(results)) + }) + + if result.Error != nil { + return result.Error + } + + return nil +} + +func (p *MultiNotifyDataRepoImpl) Create(ctx context.Context, req *bo.MultiNotifyDataBo) (*bo.MultiNotifyDataBo, error) { + + now := time.Now() + + info := &model.MultiNotifyDatum{ + Source: req.Source, + NotifyID: req.NotifyID, + OrderNo: req.OrderNo, + OutBizNo: req.OutBizNo, + CouponID: req.CouponID, + StockID: req.StockID, + ConsumeAmount: req.ConsumeAmount, + ConsumeTime: req.ConsumeTime, + EventType: req.EventType, + OriginalData: req.OriginalData, + NoticeNum: 0, + CreateTime: &now, + } + + if err := p.DB(ctx).Create(info).Error; err != nil { + return nil, err + } + + return p.ToBo(info), nil +} + +func (p *MultiNotifyDataRepoImpl) GetByID(ctx context.Context, id int64) (*bo.MultiNotifyDataBo, error) { + + var item model.MultiNotifyDatum + + tx := p.DB(ctx).Where(model.MultiNotifyDatum{ID: id}).First(&item) + + if tx.Error != nil { + return nil, tx.Error + } + + if tx.RowsAffected == 0 { + return nil, gorm.ErrRecordNotFound + } + + return p.ToBo(&item), nil +} + +func (p *MultiNotifyDataRepoImpl) GetByNotifyID(ctx context.Context, notifyId string) (*bo.MultiNotifyDataBo, error) { + + var item model.MultiNotifyDatum + + tx := p.DB(ctx).Where(model.MultiNotifyDatum{NotifyID: notifyId}).First(&item) + + if tx.Error != nil { + return nil, tx.Error + } + + if tx.RowsAffected == 0 { + return nil, gorm.ErrRecordNotFound + } + + return p.ToBo(&item), nil +} + +func (p *MultiNotifyDataRepoImpl) AddNoticeNum(ctx context.Context, id int64) error { + + now := time.Now() + + u := map[string]interface{}{ + "notice_num": gorm.Expr("notice_num + ?", 1), + "update_time": &now, + } + + tx := p.DB(ctx). + Where("id = ?", id). + Updates(u) + + if tx.Error != nil { + return tx.Error + } + + if tx.RowsAffected == 0 { + return gorm.ErrRecordNotFound + } + + return nil +} diff --git a/internal/data/repoimpl/multi_notify_log.go b/internal/data/repoimpl/multi_notify_log.go new file mode 100644 index 0000000..4a4ccc9 --- /dev/null +++ b/internal/data/repoimpl/multi_notify_log.go @@ -0,0 +1,128 @@ +package repoimpl + +import ( + "context" + "fmt" + "gorm.io/gorm" + "time" + "unicode/utf8" + err2 "voucher/api/err" + "voucher/internal/biz/bo" + "voucher/internal/biz/repo" + "voucher/internal/biz/vo" + "voucher/internal/data" + "voucher/internal/data/model" +) + +// MultiNotifyLogRepoImpl . +type MultiNotifyLogRepoImpl struct { + Base[model.MultiNotifyLog, bo.MultiNotifyLogBo] + db *data.Db +} + +// NewMultiNotifyLogRepoImpl . +func NewMultiNotifyLogRepoImpl(db *data.Db) repo.MultiNotifyLogRepo { + return &MultiNotifyLogRepoImpl{db: db} +} + +func (p *MultiNotifyLogRepoImpl) DB(ctx context.Context) *gorm.DB { + return p.db.DB(ctx).WithContext(ctx).Model(model.MultiNotifyLog{}) +} + +func (p *MultiNotifyLogRepoImpl) Create(ctx context.Context, req *bo.MultiNotifyLogBo) (*bo.MultiNotifyLogBo, error) { + + now := time.Now() + + info := &model.MultiNotifyLog{ + MultiNotifyDataID: req.MultiNotifyDataID, + OrderNo: req.OrderNo, + OutBizNo: req.OutBizNo, + CouponID: req.CouponID, + ActivityNo: req.ActivityNo, + StockID: req.StockID, + EventType: req.EventType, + Status: req.Status, + ConsumeAmount: req.ConsumeAmount, + ConsumeTime: req.ConsumeTime, + TransactionID: req.TransactionID, + Request: req.Request, + RequestURL: req.RequestURL, + RequestStatus: vo.MultiNotifyLogStatusWait.GetValue(), + OrderCreateTime: req.OrderCreateTime, + CouponCreateTime: req.CouponCreateTime, + CreateTime: &now, + } + + if err := p.DB(ctx).Create(info).Error; err != nil { + return nil, err + } + + return p.ToBo(info), nil +} + +func (p *MultiNotifyLogRepoImpl) GetByID(ctx context.Context, id int64) (*bo.MultiNotifyLogBo, error) { + var item model.MultiNotifyLog + + tx := p.DB(ctx).Where(model.MultiNotifyLog{ID: id}).First(&item) + + if tx.Error != nil { + return nil, fmt.Errorf("b fail %w", tx.Error) + } + + if tx.RowsAffected == 0 { + return nil, err2.ErrorDbNotFound("数据不存在") + } + + return p.ToBo(&item), nil +} + +func (p *MultiNotifyLogRepoImpl) Success(ctx context.Context, id int64, response string) error { + + now := time.Now() + + res := p.DB(ctx). + Where(model.MultiNotifyLog{ + ID: id, + RequestStatus: vo.MultiNotifyLogStatusWait.GetValue(), + }). + Updates(model.MultiNotifyLog{ + RequestStatus: vo.MultiNotifyLogStatusSuccess.GetValue(), + Response: response, + UpdateTime: &now, + }) + + if res.Error != nil { + return res.Error + } + + return nil +} + +func (p *MultiNotifyLogRepoImpl) Fail(ctx context.Context, id int64, remark string) error { + + if utf8.RuneCountInString(remark) > 255 { + runes := []rune(remark) + if len(runes) > 255 { + remark = string(runes[:255]) + } + } + + now := time.Now() + + res := p.DB(ctx). + Where(model.MultiNotifyLog{ + ID: id, + RequestStatus: vo.MultiNotifyLogStatusWait.GetValue(), + }). + Updates(model.MultiNotifyLog{ + RequestStatus: vo.MultiNotifyLogStatusSuccess.GetValue(), + Response: remark, + UpdateTime: &now, + }) + + if res.Error != nil { + return res.Error + } + + return nil +} From 94936482720e32c1bd06a53d44a53043451fc000 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 11 Dec 2025 14:20:09 +0800 Subject: [PATCH 028/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 14 ++++++++++++-- internal/biz/wechat_notify.go | 1 - internal/data/repoimpl/multi_notify_data.go | 4 +--- internal/pkg/helper/utils_test.go | 4 ++-- internal/server/http.go | 2 +- 5 files changed, 16 insertions(+), 9 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 7fef2f0..f88489e 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/go-kratos/kratos/v2/log" "gorm.io/gorm" + "time" v1 "voucher/api/v1" "voucher/internal/biz/bo" "voucher/internal/biz/cmb" @@ -178,6 +179,11 @@ func (biz *MultiBiz) bizContent(nl *bo.MultiNotifyLogBo) (string, error) { //Attach: nl.Attach, Ext: "", } + if nl.ConsumeTime != nil { + req.TransDate = nl.ConsumeTime.Format("2006-01-02 15:04:05.000") + } else { + req.TransDate = time.Now().Format("2006-01-02 15:04:05.000") + } bizJsonBytes, err := json.Marshal(req) if err != nil { @@ -208,7 +214,11 @@ func (biz *MultiBiz) GetRequest(ctx context.Context, nl *bo.MultiNotifyLogBo) (* func (biz *MultiBiz) Request(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl *bo.MultiNotifyLogBo) error { if nl.RequestURL == "" { - return biz.notifyFail(ctx, nl, "回调通知招行地址为空") + if err := biz.notifyFail(ctx, nl, "回调通知招行地址为空"); err != nil { + return err + } + // 回调通知地址为空,不反回错误,不做再次通知处理 + return nil } request, err := biz.GetRequest(ctx, nl) @@ -225,7 +235,7 @@ func (biz *MultiBiz) Request(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl } if err = biz.CmbMixRepo.VerifyResponse(ctx, reply); err != nil { - errMsg := fmt.Sprintf("回调通知招行返回验证结果发生错误,rep:%+v error:%s", reply, err.Error()) + errMsg := fmt.Sprintf("回调通知招行返回验证结果发生错误,resp:%+v error:%s", reply, err.Error()) if err2 := biz.notifyFail(ctx, nl, errMsg); err2 != nil { return err2 } diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index c939be8..6fd7c74 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -37,7 +37,6 @@ func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, tag string, re } else if req.PlainText.Status.IsExpired() { return this.expired(ctx, order) - } return fmt.Errorf("未知通知类型:%s", req.PlainText.Status.GetText()) diff --git a/internal/data/repoimpl/multi_notify_data.go b/internal/data/repoimpl/multi_notify_data.go index 95ff99a..4cbe272 100644 --- a/internal/data/repoimpl/multi_notify_data.go +++ b/internal/data/repoimpl/multi_notify_data.go @@ -113,9 +113,7 @@ func (p *MultiNotifyDataRepoImpl) AddNoticeNum(ctx context.Context, id int64) er "update_time": &now, } - tx := p.DB(ctx). - Where("id = ?", id). - Updates(u) + tx := p.DB(ctx).Where("id = ?", id).Updates(u) if tx.Error != nil { return tx.Error diff --git a/internal/pkg/helper/utils_test.go b/internal/pkg/helper/utils_test.go index d09f8d6..04142a2 100644 --- a/internal/pkg/helper/utils_test.go +++ b/internal/pkg/helper/utils_test.go @@ -5,7 +5,7 @@ import ( "fmt" "testing" "time" - "voucher/internal/biz/do" + "voucher/internal/biz/bo" ) func TestHashMod(t *testing.T) { @@ -82,7 +82,7 @@ func TestLength(t *testing.T) { s := len(jsonStr) t.Log(s) - var notify do.CouponNotification + var notify bo.WechatVoucherNotifyBo err := json.Unmarshal([]byte(jsonStr), ¬ify) if err != nil { panic(err) diff --git a/internal/server/http.go b/internal/server/http.go index 2758dd4..b65e518 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -37,7 +37,7 @@ func NewHTTPServer( return ctx.String(http2.StatusOK, "pong") }) - // 启星 /voucher/qiXing/v1/notify + // 启星(启星-蓝色兄弟立减金代配) /voucher/qiXing/v1/notify srv.Route("/voucher/").POST("qiXing/v1/notify", tripartiteService.QiXingNotify) // 订单通知重试 -- 不健全 From b8fc6a765080da1c1d7cc4ef9e231adea67f414d Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 11 Dec 2025 14:28:34 +0800 Subject: [PATCH 029/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/pkg/helper/ip.go | 65 ++++++++++++++++++++++++++++++++++++++ internal/service/qixing.go | 8 ++++- 2 files changed, 72 insertions(+), 1 deletion(-) create mode 100644 internal/pkg/helper/ip.go diff --git a/internal/pkg/helper/ip.go b/internal/pkg/helper/ip.go new file mode 100644 index 0000000..6600bef --- /dev/null +++ b/internal/pkg/helper/ip.go @@ -0,0 +1,65 @@ +package helper + +import ( + "net" + "strings" + + "github.com/go-kratos/kratos/v2/transport/http" +) + +// GetClientIP 获取客户端真实 IP +func GetClientIP(ctx http.Context) string { + + // 检查 X-Forwarded-For 头(多个代理时格式为 "client, proxy1, proxy2") + if xff := ctx.Header().Get("X-Forwarded-For"); xff != "" { + ips := strings.Split(xff, ",") + for _, ip := range ips { + ip = strings.TrimSpace(ip) + if ip != "" { + // 验证是否为合法 IP + if isValidIP(ip) { + return ip + } + } + } + } + + // 检查 X-Real-IP 头 + if realIP := ctx.Header().Get("X-Real-IP"); realIP != "" { + if isValidIP(realIP) { + return realIP + } + } + + // 检查 X-Forwarded + if forwarded := ctx.Header().Get("X-Forwarded"); forwarded != "" { + // 格式可能为 "for=client-ip;host=example.com;proto=https" + parts := strings.Split(forwarded, ";") + for _, part := range parts { + part = strings.TrimSpace(part) + if strings.HasPrefix(part, "for=") { + ip := strings.TrimPrefix(part, "for=") + ip = strings.Trim(ip, `"`) // 可能被引号包围 + if isValidIP(ip) { + return ip + } + } + } + } + + // 直接从 RemoteAddr 获取 + remoteAddr := ctx.Request().RemoteAddr + if ip, _, err := net.SplitHostPort(remoteAddr); err == nil { + if isValidIP(ip) { + return ip + } + } + + return "" +} + +// 验证是否为合法 IP +func isValidIP(ip string) bool { + parsedIP := net.ParseIP(ip) + return parsedIP != nil +} diff --git a/internal/service/qixing.go b/internal/service/qixing.go index 5c51940..74af1e7 100644 --- a/internal/service/qixing.go +++ b/internal/service/qixing.go @@ -8,6 +8,7 @@ import ( "io" "voucher/internal/biz" "voucher/internal/biz/bo" + "voucher/internal/pkg/helper" ) type TripartiteService struct { @@ -20,6 +21,11 @@ func NewTripartiteService(multiBiz *biz.MultiBiz) *TripartiteService { func (srv *TripartiteService) QiXingNotify(ctx http.Context) error { + ip := helper.GetClientIP(ctx) + if ip == "" { + return fmt.Errorf("获取请求 IP 失败") + } + bodyBytes, err := io.ReadAll(ctx.Request().Body) if err != nil { return fmt.Errorf("read body error: %v", err) @@ -27,7 +33,7 @@ func (srv *TripartiteService) QiXingNotify(ctx http.Context) error { var req *bo.WechatVoucherNotifyBo if err = json.Unmarshal(bodyBytes, &req); err != nil { - log.Errorf("qixing notify error:%v,body:%s", err, string(bodyBytes)) + log.Errorf("qixing notify ip:%s,error:%v,body:%s", ip, err, string(bodyBytes)) return fmt.Errorf("json unmarshal bodyBytes error: %v", err) } From c0df2aa399f212228d6ac0364b30d7107635eb99 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 11 Dec 2025 14:32:45 +0800 Subject: [PATCH 030/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/config.yaml | 2 +- configs/config_test.yaml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/configs/config.yaml b/configs/config.yaml index 984ed0f..511cfa5 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -66,7 +66,7 @@ cmb: cmbKeyAlias: "SM2_CMBLIFE" orgNo: "LANSEXIONGDI" # 发码机构号,固定值,掌上生活优惠券系统提供 notifyUrl: "https://sandbox.cdcc.cmbchina.com/AccessGateway/transIn/updateCodeStatus.json" # 招行测试回调地址 - multiNotifyUrl: "https://sandbox.cdcc.cmbchina.com/AccessGateway/transIn/updateCodeStatus.json" # 招行测试回调地址 + multiNotifyUrl: "https://sandbox.cdcc.cmbchina.com/AccessGateway/transIn/updateCodeStatus.json" # 招行测试多笔立减金回调地址 noticeStartDays: 7 noticeEndDays: 1 diff --git a/configs/config_test.yaml b/configs/config_test.yaml index fb616a2..1a61af0 100644 --- a/configs/config_test.yaml +++ b/configs/config_test.yaml @@ -66,6 +66,7 @@ cmb: cmbKeyAlias: "SM2_CMBLIFE" orgNo: "LANSEXIONGDI" # 发码机构号,固定值,掌上生活优惠券系统提供 notifyUrl: "https://sandbox.cdcc.cmbchina.com/AccessGateway/transIn/updateCodeStatus.json" # 招行测试回调地址 + multiNotifyUrl: "https://sandbox.cdcc.cmbchina.com/AccessGateway/transIn/updateCodeStatus.json" # 招行测试多笔立减金回调地址 noticeStartDays: 7 noticeEndDays: 1 From dd5380c47deec581a9a4be08cc91c2314d5dc604 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 11 Dec 2025 14:34:30 +0800 Subject: [PATCH 031/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 6 +++--- internal/service/qixing.go | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index f88489e..4558bbf 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -52,7 +52,7 @@ func NewMultiBiz( } } -func (biz *MultiBiz) Notify(ctx context.Context, source string, req *bo.WechatVoucherNotifyBo) error { +func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.WechatVoucherNotifyBo) error { cl := vo.MultiNotifyLockKey.BuildCache([]string{ source, @@ -65,12 +65,12 @@ func (biz *MultiBiz) Notify(ctx context.Context, source string, req *bo.WechatVo order, err := biz.order(ctx, req) if err != nil { - log.Errorf("[%s] multi notify error: %v,req:%+v", source, err, req) + log.Errorf("[%s-%s] multi notify error: %v,req:%+v", source, ip, err, req) return err } if err = biz.Run(ctx, source, req, order); err != nil { - log.Errorf("[%s] multi notify error: %v,req:%+v", source, err, req) + log.Errorf("[%s-%s] multi notify error: %v,req:%+v", source, ip, err, req) return err } diff --git a/internal/service/qixing.go b/internal/service/qixing.go index 74af1e7..d74956b 100644 --- a/internal/service/qixing.go +++ b/internal/service/qixing.go @@ -37,5 +37,5 @@ func (srv *TripartiteService) QiXingNotify(ctx http.Context) error { return fmt.Errorf("json unmarshal bodyBytes error: %v", err) } - return srv.multiBiz.Notify(ctx, "qixing_"+req.PlainText.StockCreatorMchid, req) + return srv.multiBiz.Notify(ctx, ip, "qixing_"+req.PlainText.StockCreatorMchid, req) } From 6b9a5a06663ab902dfc8abf77e26d738bab133c2 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 11 Dec 2025 14:40:12 +0800 Subject: [PATCH 032/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/data/mixrepoimpl/cmb.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/data/mixrepoimpl/cmb.go b/internal/data/mixrepoimpl/cmb.go index a35b4f2..5ed0844 100644 --- a/internal/data/mixrepoimpl/cmb.go +++ b/internal/data/mixrepoimpl/cmb.go @@ -289,19 +289,19 @@ func (s *CmbMixRepoImpl) Request(ctx context.Context, req *v1.CmbRequest, uri st _, bodyBytes, err := request.Post(ctx, r, nil, request.WithHeaders(h), request.WithTimeout(time.Second*20)) if err != nil { - log.Errorf("请求掌上生活报错,url:%s,err:%v", r, err) - return nil, err + //log.Errorf("请求掌上生活报错,url:%s,err:%v", r, err) + return nil, fmt.Errorf("CMB请求失败:%v", err) } var response *v1.CmbReply if err = json.Unmarshal(bodyBytes, &response); err != nil { log.Errorf("请求掌上生活返回数据解析报错:%s,url:%s,bodyBytes:%s", err.Error(), r, string(bodyBytes)) - return nil, err + return nil, fmt.Errorf("CMB数据解析错误:%s", err.Error()) } if response.RespCode != vo.CmbResponseStatusSuccess.GetValue() { - log.Errorf("请求掌上生活返回报错:msg:%s,url:%s,bodyBytes:%s", response.RespMsg, r, string(bodyBytes)) - return nil, fmt.Errorf(response.RespMsg) + //log.Errorf("请求掌上生活返回报错:msg:%s,url:%s,bodyBytes:%s", response.RespMsg, r, string(bodyBytes)) + return nil, fmt.Errorf("CMB请求返回错误:%s", response.RespMsg) } return response, nil From 1a7df7cbab99cc4b85b35629d6851bc87060b150 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 11 Dec 2025 14:53:41 +0800 Subject: [PATCH 033/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/server/http.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/server/http.go b/internal/server/http.go index b65e518..ac4c16e 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -37,8 +37,8 @@ func NewHTTPServer( return ctx.String(http2.StatusOK, "pong") }) - // 启星(启星-蓝色兄弟立减金代配) /voucher/qiXing/v1/notify - srv.Route("/voucher/").POST("qiXing/v1/notify", tripartiteService.QiXingNotify) + // 启星(启星-蓝色兄弟立减金代配) /voucher/qixing/v1/notify + srv.Route("/voucher/").POST("qixing/v1/notify", tripartiteService.QiXingNotify) // 订单通知重试 -- 不健全 srv.Route("/voucher/").POST("orderNotifyRetry", cmb.OrderNotifyRetry) From 413b8100d81cf9069a2ae14ca54df5ea3841abc2 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 11 Dec 2025 17:20:11 +0800 Subject: [PATCH 034/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/config.yaml | 4 + internal/biz/bo/qixing.go | 12 + internal/biz/multi.go | 4 +- internal/conf/conf.pb.go | 687 ++++++++++++++++++++++--------------- internal/conf/conf.proto | 8 + internal/service/qixing.go | 56 ++- 6 files changed, 488 insertions(+), 283 deletions(-) create mode 100644 internal/biz/bo/qixing.go diff --git a/configs/config.yaml b/configs/config.yaml index 511cfa5..d412ef2 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -90,6 +90,10 @@ cron: isOpen: true #是否启动 true/false command: "0 */5 * * * ?" #cron表达式,每5分钟执行一次 +tripartite: + qiXing: + appKey: "DrY1zLkOH01q0sN66vrmkdpbWsyb41ho" + rdsMQ: wechatQuery: name: "wechatQuery" diff --git a/internal/biz/bo/qixing.go b/internal/biz/bo/qixing.go new file mode 100644 index 0000000..2d0dd61 --- /dev/null +++ b/internal/biz/bo/qixing.go @@ -0,0 +1,12 @@ +package bo + +type QiXingRequestBo struct { + Content string `json:"content"` + Timestamp int64 `json:"timestamp"` + Ciphertext string `json:"ciphertext"` +} + +// QiXingResponse 响应结构体 {"msg":"SUCCESS"} / {"msg":"操作成功"} +type QiXingResponse struct { + Msg string `json:"msg"` +} diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 4558bbf..c67a11e 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -52,7 +52,7 @@ func NewMultiBiz( } } -func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.WechatVoucherNotifyBo) error { +func (biz *MultiBiz) Notify(ctx context.Context, source string, req *bo.WechatVoucherNotifyBo) error { cl := vo.MultiNotifyLockKey.BuildCache([]string{ source, @@ -65,12 +65,10 @@ func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.Wech order, err := biz.order(ctx, req) if err != nil { - log.Errorf("[%s-%s] multi notify error: %v,req:%+v", source, ip, err, req) return err } if err = biz.Run(ctx, source, req, order); err != nil { - log.Errorf("[%s-%s] multi notify error: %v,req:%+v", source, ip, err, req) return err } diff --git a/internal/conf/conf.pb.go b/internal/conf/conf.pb.go index 5b9b766..7f53e1e 100644 --- a/internal/conf/conf.pb.go +++ b/internal/conf/conf.pb.go @@ -37,6 +37,7 @@ type Bootstrap struct { Cron *Cron `protobuf:"bytes,9,opt,name=cron,proto3" json:"cron,omitempty"` RdsMQ *RdsMQ `protobuf:"bytes,10,opt,name=rdsMQ,proto3" json:"rdsMQ,omitempty"` AliYunSms *AliYunSms `protobuf:"bytes,11,opt,name=aliYunSms,proto3" json:"aliYunSms,omitempty"` + Tripartite *Tripartite `protobuf:"bytes,12,opt,name=tripartite,proto3" json:"tripartite,omitempty"` } func (x *Bootstrap) Reset() { @@ -148,6 +149,13 @@ func (x *Bootstrap) GetAliYunSms() *AliYunSms { return nil } +func (x *Bootstrap) GetTripartite() *Tripartite { + if x != nil { + return x.Tripartite + } + return nil +} + type Server struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1033,6 +1041,53 @@ func (x *AliYunSms) GetTemplateWarning() string { return "" } +type Tripartite struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + QiXing *Tripartite_QiXing `protobuf:"bytes,1,opt,name=qiXing,proto3" json:"qiXing,omitempty"` +} + +func (x *Tripartite) Reset() { + *x = Tripartite{} + if protoimpl.UnsafeEnabled { + mi := &file_conf_conf_proto_msgTypes[12] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Tripartite) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Tripartite) ProtoMessage() {} + +func (x *Tripartite) ProtoReflect() protoreflect.Message { + mi := &file_conf_conf_proto_msgTypes[12] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Tripartite.ProtoReflect.Descriptor instead. +func (*Tripartite) Descriptor() ([]byte, []int) { + return file_conf_conf_proto_rawDescGZIP(), []int{12} +} + +func (x *Tripartite) GetQiXing() *Tripartite_QiXing { + if x != nil { + return x.QiXing + } + return nil +} + type Logs struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1045,7 +1100,7 @@ type Logs struct { func (x *Logs) Reset() { *x = Logs{} if protoimpl.UnsafeEnabled { - mi := &file_conf_conf_proto_msgTypes[12] + mi := &file_conf_conf_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1058,7 +1113,7 @@ func (x *Logs) String() string { func (*Logs) ProtoMessage() {} func (x *Logs) ProtoReflect() protoreflect.Message { - mi := &file_conf_conf_proto_msgTypes[12] + mi := &file_conf_conf_proto_msgTypes[13] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1071,7 +1126,7 @@ func (x *Logs) ProtoReflect() protoreflect.Message { // Deprecated: Use Logs.ProtoReflect.Descriptor instead. func (*Logs) Descriptor() ([]byte, []int) { - return file_conf_conf_proto_rawDescGZIP(), []int{12} + return file_conf_conf_proto_rawDescGZIP(), []int{13} } func (x *Logs) GetBusiness() string { @@ -1103,7 +1158,7 @@ type Server_HTTP struct { func (x *Server_HTTP) Reset() { *x = Server_HTTP{} if protoimpl.UnsafeEnabled { - mi := &file_conf_conf_proto_msgTypes[13] + mi := &file_conf_conf_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1116,7 +1171,7 @@ func (x *Server_HTTP) String() string { func (*Server_HTTP) ProtoMessage() {} func (x *Server_HTTP) ProtoReflect() protoreflect.Message { - mi := &file_conf_conf_proto_msgTypes[13] + mi := &file_conf_conf_proto_msgTypes[14] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1183,7 +1238,7 @@ type Data_Database struct { func (x *Data_Database) Reset() { *x = Data_Database{} if protoimpl.UnsafeEnabled { - mi := &file_conf_conf_proto_msgTypes[14] + mi := &file_conf_conf_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1196,7 +1251,7 @@ func (x *Data_Database) String() string { func (*Data_Database) ProtoMessage() {} func (x *Data_Database) ProtoReflect() protoreflect.Message { - mi := &file_conf_conf_proto_msgTypes[14] + mi := &file_conf_conf_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1273,7 +1328,7 @@ type Data_Redis struct { func (x *Data_Redis) Reset() { *x = Data_Redis{} if protoimpl.UnsafeEnabled { - mi := &file_conf_conf_proto_msgTypes[15] + mi := &file_conf_conf_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1286,7 +1341,7 @@ func (x *Data_Redis) String() string { func (*Data_Redis) ProtoMessage() {} func (x *Data_Redis) ProtoReflect() protoreflect.Message { - mi := &file_conf_conf_proto_msgTypes[15] + mi := &file_conf_conf_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1377,7 +1432,7 @@ type Cron_CommandMap struct { func (x *Cron_CommandMap) Reset() { *x = Cron_CommandMap{} if protoimpl.UnsafeEnabled { - mi := &file_conf_conf_proto_msgTypes[17] + mi := &file_conf_conf_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1390,7 +1445,7 @@ func (x *Cron_CommandMap) String() string { func (*Cron_CommandMap) ProtoMessage() {} func (x *Cron_CommandMap) ProtoReflect() protoreflect.Message { - mi := &file_conf_conf_proto_msgTypes[17] + mi := &file_conf_conf_proto_msgTypes[18] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1435,7 +1490,7 @@ type RdsMQ_Queue struct { func (x *RdsMQ_Queue) Reset() { *x = RdsMQ_Queue{} if protoimpl.UnsafeEnabled { - mi := &file_conf_conf_proto_msgTypes[19] + mi := &file_conf_conf_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1448,7 +1503,7 @@ func (x *RdsMQ_Queue) String() string { func (*RdsMQ_Queue) ProtoMessage() {} func (x *RdsMQ_Queue) ProtoReflect() protoreflect.Message { - mi := &file_conf_conf_proto_msgTypes[19] + mi := &file_conf_conf_proto_msgTypes[20] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1499,6 +1554,53 @@ func (x *RdsMQ_Queue) GetWaitTime() *durationpb.Duration { return nil } +type Tripartite_QiXing struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + AppKey string `protobuf:"bytes,1,opt,name=appKey,proto3" json:"appKey,omitempty"` +} + +func (x *Tripartite_QiXing) Reset() { + *x = Tripartite_QiXing{} + if protoimpl.UnsafeEnabled { + mi := &file_conf_conf_proto_msgTypes[21] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Tripartite_QiXing) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Tripartite_QiXing) ProtoMessage() {} + +func (x *Tripartite_QiXing) ProtoReflect() protoreflect.Message { + mi := &file_conf_conf_proto_msgTypes[21] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Tripartite_QiXing.ProtoReflect.Descriptor instead. +func (*Tripartite_QiXing) Descriptor() ([]byte, []int) { + return file_conf_conf_proto_rawDescGZIP(), []int{12, 0} +} + +func (x *Tripartite_QiXing) GetAppKey() string { + if x != nil { + return x.AppKey + } + return "" +} + var File_conf_conf_proto protoreflect.FileDescriptor var file_conf_conf_proto_rawDesc = []byte{ @@ -1506,7 +1608,7 @@ var file_conf_conf_proto_rawDesc = []byte{ 0x6f, 0x12, 0x0e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x22, 0xa1, 0x04, 0x0a, 0x09, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x12, + 0x6f, 0x22, 0xdd, 0x04, 0x0a, 0x09, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, @@ -1540,226 +1642,237 @@ var file_conf_conf_proto_rawDesc = []byte{ 0x61, 0x6c, 0x69, 0x59, 0x75, 0x6e, 0x53, 0x6d, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x41, 0x6c, 0x69, 0x59, 0x75, 0x6e, 0x53, 0x6d, 0x73, 0x52, 0x09, 0x61, 0x6c, 0x69, 0x59, - 0x75, 0x6e, 0x53, 0x6d, 0x73, 0x22, 0xff, 0x01, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x12, 0x2f, 0x0a, 0x04, 0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, - 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, - 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x04, 0x68, 0x74, 0x74, - 0x70, 0x1a, 0xc3, 0x01, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, - 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, - 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, - 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, - 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, - 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x24, 0x0a, - 0x0d, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x77, 0x61, 0x67, - 0x67, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x14, 0x69, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x52, 0x65, 0x71, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, - 0x08, 0x52, 0x14, 0x69, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x65, 0x71, - 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x22, 0x94, 0x05, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, - 0x12, 0x2d, 0x0a, 0x02, 0x64, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x76, - 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x61, - 0x74, 0x61, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x02, 0x64, 0x62, 0x12, - 0x30, 0x0a, 0x05, 0x72, 0x65, 0x64, 0x69, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, - 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, - 0x44, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x64, 0x69, 0x73, 0x52, 0x05, 0x72, 0x65, 0x64, 0x69, - 0x73, 0x1a, 0xc5, 0x01, 0x0a, 0x08, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x16, - 0x0a, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x18, - 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x49, 0x64, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, - 0x07, 0x6d, 0x61, 0x78, 0x49, 0x64, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x4f, - 0x70, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x4f, 0x70, - 0x65, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x4c, 0x69, 0x66, 0x65, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x4c, 0x69, 0x66, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x12, - 0x18, 0x0a, 0x07, 0x69, 0x73, 0x44, 0x65, 0x62, 0x75, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x07, 0x69, 0x73, 0x44, 0x65, 0x62, 0x75, 0x67, 0x1a, 0xe2, 0x02, 0x0a, 0x05, 0x52, 0x65, - 0x64, 0x69, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, - 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, - 0x72, 0x12, 0x3b, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x3d, - 0x0a, 0x0c, 0x77, 0x72, 0x69, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x04, - 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, - 0x0c, 0x77, 0x72, 0x69, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x6f, - 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6f, 0x6f, - 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x6d, 0x69, 0x6e, 0x49, 0x64, 0x6c, 0x65, - 0x43, 0x6f, 0x6e, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x69, 0x6e, - 0x49, 0x64, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, - 0x6e, 0x4d, 0x61, 0x78, 0x49, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, + 0x75, 0x6e, 0x53, 0x6d, 0x73, 0x12, 0x3a, 0x0a, 0x0a, 0x74, 0x72, 0x69, 0x70, 0x61, 0x72, 0x74, + 0x69, 0x74, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x76, 0x6f, 0x75, 0x63, + 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54, 0x72, 0x69, 0x70, 0x61, + 0x72, 0x74, 0x69, 0x74, 0x65, 0x52, 0x0a, 0x74, 0x72, 0x69, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, + 0x65, 0x22, 0xff, 0x01, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x2f, 0x0a, 0x04, + 0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, + 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x53, 0x65, 0x72, 0x76, + 0x65, 0x72, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x1a, 0xc3, 0x01, + 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, + 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, + 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x61, 0x64, 0x64, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, + 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x69, 0x73, 0x4f, + 0x70, 0x65, 0x6e, 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, + 0x52, 0x0d, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x12, + 0x32, 0x0a, 0x14, 0x69, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x65, 0x71, + 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x69, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x65, 0x71, 0x48, 0x65, 0x61, 0x64, + 0x65, 0x72, 0x73, 0x22, 0x94, 0x05, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2d, 0x0a, 0x02, + 0x64, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, + 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x44, + 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x02, 0x64, 0x62, 0x12, 0x30, 0x0a, 0x05, 0x72, + 0x65, 0x64, 0x69, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x76, 0x6f, 0x75, + 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x61, 0x74, 0x61, + 0x2e, 0x52, 0x65, 0x64, 0x69, 0x73, 0x52, 0x05, 0x72, 0x65, 0x64, 0x69, 0x73, 0x1a, 0xc5, 0x01, + 0x0a, 0x08, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x72, + 0x69, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x72, 0x69, 0x76, + 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, + 0x78, 0x49, 0x64, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x6d, 0x61, 0x78, + 0x49, 0x64, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x4f, 0x70, 0x65, 0x6e, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x3b, + 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x4c, 0x69, 0x66, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, + 0x6d, 0x61, 0x78, 0x4c, 0x69, 0x66, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x69, + 0x73, 0x44, 0x65, 0x62, 0x75, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, + 0x44, 0x65, 0x62, 0x75, 0x67, 0x1a, 0xe2, 0x02, 0x0a, 0x05, 0x52, 0x65, 0x64, 0x69, 0x73, 0x12, + 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x3b, 0x0a, + 0x0b, 0x72, 0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x63, - 0x6f, 0x6e, 0x6e, 0x4d, 0x61, 0x78, 0x49, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x0e, - 0x0a, 0x02, 0x64, 0x62, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x64, 0x62, 0x22, 0x97, - 0x02, 0x0a, 0x08, 0x52, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x4d, 0x51, 0x12, 0x12, 0x0a, 0x04, 0x61, - 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, - 0x1c, 0x0a, 0x09, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, - 0x09, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x73, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x0b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x42, 0x0a, - 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x26, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x2e, 0x52, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x4d, 0x51, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, - 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x61, - 0x70, 0x1a, 0x55, 0x0a, 0x0d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x88, 0x01, 0x0a, 0x08, 0x45, 0x76, 0x65, - 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x74, - 0x6f, 0x70, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x70, 0x69, - 0x63, 0x12, 0x28, 0x0a, 0x0f, 0x70, 0x65, 0x72, 0x43, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, - 0x65, 0x43, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x70, 0x65, 0x72, 0x43, - 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x43, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x69, - 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x18, 0x04, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x75, - 0x6d, 0x65, 0x72, 0x22, 0xa6, 0x01, 0x0a, 0x06, 0x57, 0x65, 0x63, 0x68, 0x61, 0x74, 0x12, 0x14, - 0x0a, 0x05, 0x6d, 0x63, 0x68, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, - 0x63, 0x68, 0x49, 0x44, 0x12, 0x3e, 0x0a, 0x1a, 0x6d, 0x63, 0x68, 0x43, 0x65, 0x72, 0x74, 0x69, - 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, - 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x6d, 0x63, 0x68, 0x43, 0x65, 0x72, - 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, - 0x6d, 0x62, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x50, 0x61, - 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x50, 0x61, 0x79, 0x50, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xff, 0x02, 0x0a, - 0x03, 0x43, 0x6d, 0x62, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x03, 0x6d, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x69, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6d, 0x32, 0x50, - 0x72, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6d, 0x32, 0x50, 0x72, 0x6b, - 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6d, 0x32, 0x50, 0x75, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x73, 0x6d, 0x32, 0x50, 0x75, 0x6b, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6d, 0x62, 0x53, - 0x6d, 0x32, 0x50, 0x69, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6d, 0x62, - 0x53, 0x6d, 0x32, 0x50, 0x69, 0x6b, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6d, 0x62, 0x53, 0x6d, 0x32, - 0x50, 0x75, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6d, 0x62, 0x53, 0x6d, - 0x32, 0x50, 0x75, 0x6b, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x69, 0x61, 0x73, - 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x69, 0x61, 0x73, - 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6d, 0x62, 0x4b, 0x65, 0x79, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x18, - 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6d, 0x62, 0x4b, 0x65, 0x79, 0x41, 0x6c, 0x69, - 0x61, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x72, 0x67, 0x4e, 0x6f, 0x18, 0x09, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x4e, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x69, - 0x66, 0x79, 0x55, 0x72, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, 0x74, - 0x69, 0x66, 0x79, 0x55, 0x72, 0x6c, 0x12, 0x26, 0x0a, 0x0e, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x4e, - 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x72, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, - 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x72, 0x6c, 0x12, 0x28, - 0x0a, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x79, - 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x53, - 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x79, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x6f, 0x74, 0x69, - 0x63, 0x65, 0x45, 0x6e, 0x64, 0x44, 0x61, 0x79, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, - 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x64, 0x44, 0x61, 0x79, 0x73, 0x22, 0xc6, - 0x02, 0x0a, 0x0e, 0x57, 0x65, 0x63, 0x68, 0x61, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4d, - 0x51, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, - 0x79, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, - 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x67, - 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x67, - 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, - 0x65, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, - 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x74, - 0x61, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x18, 0x0a, - 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, - 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x55, 0x72, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x55, 0x72, 0x6c, 0x12, - 0x26, 0x0a, 0x0e, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, - 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x43, - 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x22, 0x9b, 0x01, 0x0a, 0x05, 0x41, 0x6c, 0x61, 0x72, - 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x55, 0x52, 0x4c, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x55, 0x52, - 0x4c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x74, 0x41, - 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x74, 0x41, 0x6c, 0x6c, 0x12, - 0x1c, 0x0a, 0x09, 0x61, 0x74, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x09, 0x61, 0x74, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x26, 0x0a, - 0x0e, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x73, 0x18, - 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x6f, - 0x62, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x84, 0x02, 0x0a, 0x04, 0x43, 0x72, 0x6f, 0x6e, 0x12, 0x16, - 0x0a, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, - 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x44, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, - 0x64, 0x4d, 0x61, 0x70, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x76, 0x6f, 0x75, - 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x72, 0x6f, 0x6e, - 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x1a, 0x3e, 0x0a, 0x0a, - 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, - 0x4f, 0x70, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4f, 0x70, - 0x65, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x5e, 0x0a, 0x0f, - 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x35, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x1f, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, - 0x67, 0x2e, 0x43, 0x72, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, - 0x70, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc4, 0x04, 0x0a, - 0x05, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x12, 0x3d, 0x0a, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, - 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, - 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, - 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x4f, 0x0a, 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x54, - 0x69, 0x6d, 0x65, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, - 0x52, 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x6c, 0x69, 0x63, - 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x3d, 0x0a, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, - 0x52, 0x65, 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, - 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, - 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, - 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x3d, 0x0a, 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, - 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, - 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x79, 0x12, 0x47, 0x0a, 0x10, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x4e, 0x6f, 0x74, - 0x69, 0x66, 0x79, 0x52, 0x65, 0x74, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, - 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, - 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x10, 0x6f, 0x72, 0x64, - 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x3b, 0x0a, - 0x0a, 0x75, 0x73, 0x65, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, - 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x0a, - 0x75, 0x73, 0x65, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x1a, 0xa6, 0x01, 0x0a, 0x05, 0x51, - 0x75, 0x65, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x4f, 0x70, - 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, - 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0d, 0x52, 0x08, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x75, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, - 0x6e, 0x75, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, - 0x52, 0x0a, 0x6e, 0x75, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x35, 0x0a, 0x08, - 0x77, 0x61, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, + 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x72, + 0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x3d, 0x0a, 0x0c, 0x77, 0x72, + 0x69, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, + 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, + 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x77, 0x72, 0x69, + 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, + 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x6f, 0x6c, 0x53, 0x69, 0x7a, + 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6f, 0x6f, 0x6c, 0x53, 0x69, 0x7a, + 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x6d, 0x69, 0x6e, 0x49, 0x64, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x6e, + 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x49, 0x64, 0x6c, 0x65, + 0x43, 0x6f, 0x6e, 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x4d, 0x61, 0x78, + 0x49, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x77, 0x61, 0x69, 0x74, 0x54, - 0x69, 0x6d, 0x65, 0x22, 0xb9, 0x01, 0x0a, 0x09, 0x41, 0x6c, 0x69, 0x59, 0x75, 0x6e, 0x53, 0x6d, - 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, - 0x79, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, - 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, - 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x69, 0x67, - 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x69, 0x67, - 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, - 0x65, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, - 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x22, - 0x3a, 0x0a, 0x04, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x75, 0x73, 0x69, 0x6e, - 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x75, 0x73, 0x69, 0x6e, - 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x42, 0x17, 0x5a, 0x15, 0x76, - 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2f, 0x63, 0x70, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x3b, - 0x63, 0x6f, 0x6e, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x4d, + 0x61, 0x78, 0x49, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x64, 0x62, + 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x64, 0x62, 0x22, 0x97, 0x02, 0x0a, 0x08, 0x52, + 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x4d, 0x51, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x63, + 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, + 0x63, 0x72, 0x65, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x42, 0x0a, 0x08, 0x65, 0x76, 0x65, + 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x76, 0x6f, + 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x6f, 0x63, + 0x6b, 0x65, 0x74, 0x4d, 0x51, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x1a, 0x55, 0x0a, + 0x0d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, + 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, + 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x18, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, + 0x3a, 0x02, 0x38, 0x01, 0x22, 0x88, 0x01, 0x0a, 0x08, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x61, + 0x70, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x28, 0x0a, + 0x0f, 0x70, 0x65, 0x72, 0x43, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x43, 0x6e, 0x74, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x70, 0x65, 0x72, 0x43, 0x6f, 0x72, 0x6f, 0x75, + 0x74, 0x69, 0x6e, 0x65, 0x43, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x69, 0x73, 0x4f, 0x70, 0x65, + 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x0e, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x22, + 0xa6, 0x01, 0x0a, 0x06, 0x57, 0x65, 0x63, 0x68, 0x61, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x63, + 0x68, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x63, 0x68, 0x49, 0x44, + 0x12, 0x3e, 0x0a, 0x1a, 0x6d, 0x63, 0x68, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, + 0x74, 0x65, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x6d, 0x63, 0x68, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, + 0x63, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, + 0x12, 0x32, 0x0a, 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x50, 0x61, 0x79, 0x50, 0x75, 0x62, + 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, + 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x50, 0x61, 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, + 0x65, 0x79, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xff, 0x02, 0x0a, 0x03, 0x43, 0x6d, 0x62, + 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, + 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x61, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6d, 0x32, 0x50, 0x72, 0x6b, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6d, 0x32, 0x50, 0x72, 0x6b, 0x12, 0x16, 0x0a, 0x06, + 0x73, 0x6d, 0x32, 0x50, 0x75, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6d, + 0x32, 0x50, 0x75, 0x6b, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6d, 0x62, 0x53, 0x6d, 0x32, 0x50, 0x69, + 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6d, 0x62, 0x53, 0x6d, 0x32, 0x50, + 0x69, 0x6b, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6d, 0x62, 0x53, 0x6d, 0x32, 0x50, 0x75, 0x6b, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6d, 0x62, 0x53, 0x6d, 0x32, 0x50, 0x75, 0x6b, + 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x07, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x20, 0x0a, 0x0b, + 0x63, 0x6d, 0x62, 0x4b, 0x65, 0x79, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x63, 0x6d, 0x62, 0x4b, 0x65, 0x79, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x14, + 0x0a, 0x05, 0x6f, 0x72, 0x67, 0x4e, 0x6f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, + 0x72, 0x67, 0x4e, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x72, + 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, + 0x72, 0x6c, 0x12, 0x26, 0x0a, 0x0e, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x4e, 0x6f, 0x74, 0x69, 0x66, + 0x79, 0x55, 0x72, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x75, 0x6c, 0x74, + 0x69, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x72, 0x6c, 0x12, 0x28, 0x0a, 0x0f, 0x6e, 0x6f, + 0x74, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x79, 0x73, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x03, 0x52, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, + 0x44, 0x61, 0x79, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x45, 0x6e, + 0x64, 0x44, 0x61, 0x79, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6e, 0x6f, 0x74, + 0x69, 0x63, 0x65, 0x45, 0x6e, 0x64, 0x44, 0x61, 0x79, 0x73, 0x22, 0xc6, 0x02, 0x0a, 0x0e, 0x57, + 0x65, 0x63, 0x68, 0x61, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4d, 0x51, 0x12, 0x20, 0x0a, + 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, + 0x28, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, + 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, + 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x49, + 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x49, + 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x18, + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, + 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x07, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x72, 0x6f, + 0x75, 0x70, 0x49, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, + 0x70, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, + 0x61, 0x67, 0x55, 0x72, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x55, 0x72, 0x6c, 0x12, 0x26, 0x0a, 0x0e, 0x69, + 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x18, 0x0b, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x75, + 0x6d, 0x65, 0x72, 0x22, 0x9b, 0x01, 0x0a, 0x05, 0x41, 0x6c, 0x61, 0x72, 0x6d, 0x12, 0x1e, 0x0a, + 0x0a, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0a, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x55, 0x52, 0x4c, 0x12, 0x16, 0x0a, + 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, + 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x74, 0x41, 0x6c, 0x6c, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x61, + 0x74, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, + 0x61, 0x74, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x77, 0x61, 0x72, + 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, + 0x09, 0x52, 0x0e, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, + 0x73, 0x22, 0x84, 0x02, 0x0a, 0x04, 0x43, 0x72, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, + 0x4f, 0x70, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4f, 0x70, + 0x65, 0x6e, 0x12, 0x44, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, + 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x72, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6d, + 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x63, 0x6f, + 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x1a, 0x3e, 0x0a, 0x0a, 0x43, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x18, + 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x5e, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x6d, + 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, + 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x35, 0x0a, + 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x76, + 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x72, + 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc4, 0x04, 0x0a, 0x05, 0x52, 0x64, 0x73, + 0x4d, 0x51, 0x12, 0x3d, 0x0a, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, + 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, + 0x75, 0x65, 0x75, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x51, 0x75, 0x65, 0x72, + 0x79, 0x12, 0x4f, 0x0a, 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x53, + 0x6c, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x14, 0x77, 0x65, + 0x63, 0x68, 0x61, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, + 0x72, 0x79, 0x12, 0x3d, 0x0a, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x52, 0x65, 0x74, 0x72, + 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, + 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, + 0x75, 0x65, 0x75, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x52, 0x65, 0x74, 0x72, + 0x79, 0x12, 0x3d, 0x0a, 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, + 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, + 0x65, 0x75, 0x65, 0x52, 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, + 0x12, 0x47, 0x0a, 0x10, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, + 0x65, 0x74, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, + 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, + 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x10, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x4e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x3b, 0x0a, 0x0a, 0x75, 0x73, 0x65, + 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, + 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, + 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x64, + 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x1a, 0xa6, 0x01, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x75, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, + 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, + 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x75, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x75, 0x6d, 0x57, + 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6e, 0x75, + 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x35, 0x0a, 0x08, 0x77, 0x61, 0x69, 0x74, + 0x54, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, + 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x77, 0x61, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x22, + 0xb9, 0x01, 0x0a, 0x09, 0x41, 0x6c, 0x69, 0x59, 0x75, 0x6e, 0x53, 0x6d, 0x73, 0x12, 0x20, 0x0a, + 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, + 0x28, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, + 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, + 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, + 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x4e, 0x61, 0x6d, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x4e, 0x61, 0x6d, + 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x57, 0x61, 0x72, + 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, + 0x6c, 0x61, 0x74, 0x65, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x22, 0x69, 0x0a, 0x0a, 0x54, + 0x72, 0x69, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x65, 0x12, 0x39, 0x0a, 0x06, 0x71, 0x69, 0x58, + 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x6f, 0x75, 0x63, + 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54, 0x72, 0x69, 0x70, 0x61, + 0x72, 0x74, 0x69, 0x74, 0x65, 0x2e, 0x51, 0x69, 0x58, 0x69, 0x6e, 0x67, 0x52, 0x06, 0x71, 0x69, + 0x58, 0x69, 0x6e, 0x67, 0x1a, 0x20, 0x0a, 0x06, 0x51, 0x69, 0x58, 0x69, 0x6e, 0x67, 0x12, 0x16, + 0x0a, 0x06, 0x61, 0x70, 0x70, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, + 0x61, 0x70, 0x70, 0x4b, 0x65, 0x79, 0x22, 0x3a, 0x0a, 0x04, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x1a, + 0x0a, 0x08, 0x62, 0x75, 0x73, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x62, 0x75, 0x73, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x63, 0x65, + 0x73, 0x73, 0x42, 0x17, 0x5a, 0x15, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2f, 0x63, 0x70, + 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x3b, 0x63, 0x6f, 0x6e, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, } var ( @@ -1774,7 +1887,7 @@ func file_conf_conf_proto_rawDescGZIP() []byte { return file_conf_conf_proto_rawDescData } -var file_conf_conf_proto_msgTypes = make([]protoimpl.MessageInfo, 20) +var file_conf_conf_proto_msgTypes = make([]protoimpl.MessageInfo, 22) var file_conf_conf_proto_goTypes = []any{ (*Bootstrap)(nil), // 0: voucher.config.Bootstrap (*Server)(nil), // 1: voucher.config.Server @@ -1788,19 +1901,21 @@ var file_conf_conf_proto_goTypes = []any{ (*Cron)(nil), // 9: voucher.config.Cron (*RdsMQ)(nil), // 10: voucher.config.RdsMQ (*AliYunSms)(nil), // 11: voucher.config.AliYunSms - (*Logs)(nil), // 12: voucher.config.Logs - (*Server_HTTP)(nil), // 13: voucher.config.Server.HTTP - (*Data_Database)(nil), // 14: voucher.config.Data.Database - (*Data_Redis)(nil), // 15: voucher.config.Data.Redis - nil, // 16: voucher.config.RocketMQ.EventMapEntry - (*Cron_CommandMap)(nil), // 17: voucher.config.Cron.CommandMap - nil, // 18: voucher.config.Cron.CommandMapEntry - (*RdsMQ_Queue)(nil), // 19: voucher.config.RdsMQ.Queue - (*durationpb.Duration)(nil), // 20: google.protobuf.Duration + (*Tripartite)(nil), // 12: voucher.config.Tripartite + (*Logs)(nil), // 13: voucher.config.Logs + (*Server_HTTP)(nil), // 14: voucher.config.Server.HTTP + (*Data_Database)(nil), // 15: voucher.config.Data.Database + (*Data_Redis)(nil), // 16: voucher.config.Data.Redis + nil, // 17: voucher.config.RocketMQ.EventMapEntry + (*Cron_CommandMap)(nil), // 18: voucher.config.Cron.CommandMap + nil, // 19: voucher.config.Cron.CommandMapEntry + (*RdsMQ_Queue)(nil), // 20: voucher.config.RdsMQ.Queue + (*Tripartite_QiXing)(nil), // 21: voucher.config.Tripartite.QiXing + (*durationpb.Duration)(nil), // 22: google.protobuf.Duration } var file_conf_conf_proto_depIdxs = []int32{ 1, // 0: voucher.config.Bootstrap.server:type_name -> voucher.config.Server - 12, // 1: voucher.config.Bootstrap.logs:type_name -> voucher.config.Logs + 13, // 1: voucher.config.Bootstrap.logs:type_name -> voucher.config.Logs 2, // 2: voucher.config.Bootstrap.data:type_name -> voucher.config.Data 3, // 3: voucher.config.Bootstrap.rocketMQ:type_name -> voucher.config.RocketMQ 5, // 4: voucher.config.Bootstrap.wechat:type_name -> voucher.config.Wechat @@ -1810,30 +1925,32 @@ var file_conf_conf_proto_depIdxs = []int32{ 9, // 8: voucher.config.Bootstrap.cron:type_name -> voucher.config.Cron 10, // 9: voucher.config.Bootstrap.rdsMQ:type_name -> voucher.config.RdsMQ 11, // 10: voucher.config.Bootstrap.aliYunSms:type_name -> voucher.config.AliYunSms - 13, // 11: voucher.config.Server.http:type_name -> voucher.config.Server.HTTP - 14, // 12: voucher.config.Data.db:type_name -> voucher.config.Data.Database - 15, // 13: voucher.config.Data.redis:type_name -> voucher.config.Data.Redis - 16, // 14: voucher.config.RocketMQ.eventMap:type_name -> voucher.config.RocketMQ.EventMapEntry - 18, // 15: voucher.config.Cron.commandMap:type_name -> voucher.config.Cron.CommandMapEntry - 19, // 16: voucher.config.RdsMQ.wechatQuery:type_name -> voucher.config.RdsMQ.Queue - 19, // 17: voucher.config.RdsMQ.wechatTimeSliceQuery:type_name -> voucher.config.RdsMQ.Queue - 19, // 18: voucher.config.RdsMQ.wechatRetry:type_name -> voucher.config.RdsMQ.Queue - 19, // 19: voucher.config.RdsMQ.retryNotify:type_name -> voucher.config.RdsMQ.Queue - 19, // 20: voucher.config.RdsMQ.orderNotifyRetry:type_name -> voucher.config.RdsMQ.Queue - 19, // 21: voucher.config.RdsMQ.usedNotify:type_name -> voucher.config.RdsMQ.Queue - 20, // 22: voucher.config.Server.HTTP.timeout:type_name -> google.protobuf.Duration - 20, // 23: voucher.config.Data.Database.maxLifetime:type_name -> google.protobuf.Duration - 20, // 24: voucher.config.Data.Redis.readTimeout:type_name -> google.protobuf.Duration - 20, // 25: voucher.config.Data.Redis.writeTimeout:type_name -> google.protobuf.Duration - 20, // 26: voucher.config.Data.Redis.connMaxIdleTime:type_name -> google.protobuf.Duration - 4, // 27: voucher.config.RocketMQ.EventMapEntry.value:type_name -> voucher.config.EventMap - 17, // 28: voucher.config.Cron.CommandMapEntry.value:type_name -> voucher.config.Cron.CommandMap - 20, // 29: voucher.config.RdsMQ.Queue.waitTime:type_name -> google.protobuf.Duration - 30, // [30:30] is the sub-list for method output_type - 30, // [30:30] is the sub-list for method input_type - 30, // [30:30] is the sub-list for extension type_name - 30, // [30:30] is the sub-list for extension extendee - 0, // [0:30] is the sub-list for field type_name + 12, // 11: voucher.config.Bootstrap.tripartite:type_name -> voucher.config.Tripartite + 14, // 12: voucher.config.Server.http:type_name -> voucher.config.Server.HTTP + 15, // 13: voucher.config.Data.db:type_name -> voucher.config.Data.Database + 16, // 14: voucher.config.Data.redis:type_name -> voucher.config.Data.Redis + 17, // 15: voucher.config.RocketMQ.eventMap:type_name -> voucher.config.RocketMQ.EventMapEntry + 19, // 16: voucher.config.Cron.commandMap:type_name -> voucher.config.Cron.CommandMapEntry + 20, // 17: voucher.config.RdsMQ.wechatQuery:type_name -> voucher.config.RdsMQ.Queue + 20, // 18: voucher.config.RdsMQ.wechatTimeSliceQuery:type_name -> voucher.config.RdsMQ.Queue + 20, // 19: voucher.config.RdsMQ.wechatRetry:type_name -> voucher.config.RdsMQ.Queue + 20, // 20: voucher.config.RdsMQ.retryNotify:type_name -> voucher.config.RdsMQ.Queue + 20, // 21: voucher.config.RdsMQ.orderNotifyRetry:type_name -> voucher.config.RdsMQ.Queue + 20, // 22: voucher.config.RdsMQ.usedNotify:type_name -> voucher.config.RdsMQ.Queue + 21, // 23: voucher.config.Tripartite.qiXing:type_name -> voucher.config.Tripartite.QiXing + 22, // 24: voucher.config.Server.HTTP.timeout:type_name -> google.protobuf.Duration + 22, // 25: voucher.config.Data.Database.maxLifetime:type_name -> google.protobuf.Duration + 22, // 26: voucher.config.Data.Redis.readTimeout:type_name -> google.protobuf.Duration + 22, // 27: voucher.config.Data.Redis.writeTimeout:type_name -> google.protobuf.Duration + 22, // 28: voucher.config.Data.Redis.connMaxIdleTime:type_name -> google.protobuf.Duration + 4, // 29: voucher.config.RocketMQ.EventMapEntry.value:type_name -> voucher.config.EventMap + 18, // 30: voucher.config.Cron.CommandMapEntry.value:type_name -> voucher.config.Cron.CommandMap + 22, // 31: voucher.config.RdsMQ.Queue.waitTime:type_name -> google.protobuf.Duration + 32, // [32:32] is the sub-list for method output_type + 32, // [32:32] is the sub-list for method input_type + 32, // [32:32] is the sub-list for extension type_name + 32, // [32:32] is the sub-list for extension extendee + 0, // [0:32] is the sub-list for field type_name } func init() { file_conf_conf_proto_init() } @@ -1987,7 +2104,7 @@ func file_conf_conf_proto_init() { } } file_conf_conf_proto_msgTypes[12].Exporter = func(v any, i int) any { - switch v := v.(*Logs); i { + switch v := v.(*Tripartite); i { case 0: return &v.state case 1: @@ -1999,7 +2116,7 @@ func file_conf_conf_proto_init() { } } file_conf_conf_proto_msgTypes[13].Exporter = func(v any, i int) any { - switch v := v.(*Server_HTTP); i { + switch v := v.(*Logs); i { case 0: return &v.state case 1: @@ -2011,7 +2128,7 @@ func file_conf_conf_proto_init() { } } file_conf_conf_proto_msgTypes[14].Exporter = func(v any, i int) any { - switch v := v.(*Data_Database); i { + switch v := v.(*Server_HTTP); i { case 0: return &v.state case 1: @@ -2023,6 +2140,18 @@ func file_conf_conf_proto_init() { } } file_conf_conf_proto_msgTypes[15].Exporter = func(v any, i int) any { + switch v := v.(*Data_Database); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_conf_conf_proto_msgTypes[16].Exporter = func(v any, i int) any { switch v := v.(*Data_Redis); i { case 0: return &v.state @@ -2034,7 +2163,7 @@ func file_conf_conf_proto_init() { return nil } } - file_conf_conf_proto_msgTypes[17].Exporter = func(v any, i int) any { + file_conf_conf_proto_msgTypes[18].Exporter = func(v any, i int) any { switch v := v.(*Cron_CommandMap); i { case 0: return &v.state @@ -2046,7 +2175,7 @@ func file_conf_conf_proto_init() { return nil } } - file_conf_conf_proto_msgTypes[19].Exporter = func(v any, i int) any { + file_conf_conf_proto_msgTypes[20].Exporter = func(v any, i int) any { switch v := v.(*RdsMQ_Queue); i { case 0: return &v.state @@ -2058,6 +2187,18 @@ func file_conf_conf_proto_init() { return nil } } + file_conf_conf_proto_msgTypes[21].Exporter = func(v any, i int) any { + switch v := v.(*Tripartite_QiXing); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -2065,7 +2206,7 @@ func file_conf_conf_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_conf_conf_proto_rawDesc, NumEnums: 0, - NumMessages: 20, + NumMessages: 22, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/conf/conf.proto b/internal/conf/conf.proto index f56e09f..1bc014f 100644 --- a/internal/conf/conf.proto +++ b/internal/conf/conf.proto @@ -17,6 +17,7 @@ message Bootstrap { Cron cron = 9; RdsMQ rdsMQ = 10; AliYunSms aliYunSms = 11; + Tripartite tripartite = 12; } message Server { @@ -147,6 +148,13 @@ message AliYunSms { string templateWarning = 5; } +message Tripartite { + message QiXing { + string appKey = 1; + } + QiXing qiXing = 1; +} + message Logs { string business = 1; string access = 2; diff --git a/internal/service/qixing.go b/internal/service/qixing.go index d74956b..b1d14a2 100644 --- a/internal/service/qixing.go +++ b/internal/service/qixing.go @@ -6,17 +6,20 @@ import ( "github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/transport/http" "io" + http2 "net/http" "voucher/internal/biz" "voucher/internal/biz/bo" + "voucher/internal/conf" "voucher/internal/pkg/helper" ) type TripartiteService struct { + bc *conf.Bootstrap multiBiz *biz.MultiBiz } -func NewTripartiteService(multiBiz *biz.MultiBiz) *TripartiteService { - return &TripartiteService{multiBiz: multiBiz} +func NewTripartiteService(bc *conf.Bootstrap, multiBiz *biz.MultiBiz) *TripartiteService { + return &TripartiteService{bc: bc, multiBiz: multiBiz} } func (srv *TripartiteService) QiXingNotify(ctx http.Context) error { @@ -28,14 +31,53 @@ func (srv *TripartiteService) QiXingNotify(ctx http.Context) error { bodyBytes, err := io.ReadAll(ctx.Request().Body) if err != nil { - return fmt.Errorf("read body error: %v", err) + return srv.ResponseErr(ctx, fmt.Sprintf("read body error: %v", err)) } - var req *bo.WechatVoucherNotifyBo + var req *bo.QiXingRequestBo if err = json.Unmarshal(bodyBytes, &req); err != nil { - log.Errorf("qixing notify ip:%s,error:%v,body:%s", ip, err, string(bodyBytes)) - return fmt.Errorf("json unmarshal bodyBytes error: %v", err) + 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.multiBiz.Notify(ctx, ip, "qixing_"+req.PlainText.StockCreatorMchid, req) + // 加密校验串,(生成规则:MD5(content+公钥)) + if srv.bc.Tripartite.QiXing.AppKey == "" { + return srv.ResponseErr(ctx, "qixing appKey is empty") + } + + sign := helper.Md5(req.Content + srv.bc.Tripartite.QiXing.AppKey) + if sign != req.Ciphertext { + log.Errorf("qixing notify sign err ip:%s,error:%v,body:%s", ip, err, string(bodyBytes)) + return srv.ResponseErr(ctx, "sign error") + } + + var wxNotifyData *bo.WechatVoucherNotifyBo + if err = json.Unmarshal([]byte(req.Content), &wxNotifyData); err != nil { + log.Errorf("qixing notify wxNotifyData err ip:%s,error:%v,body:%s", ip, err, string(bodyBytes)) + return srv.ResponseErr(ctx, fmt.Sprintf("json unmarshal wxNotifyData error: %v", err)) + } + + err = srv.multiBiz.Notify(ctx, "qixing_"+wxNotifyData.PlainText.StockCreatorMchid, wxNotifyData) + if err != nil { + log.Errorf("qixing notify run err ip:%s,error:%v,body:%s", ip, err, string(bodyBytes)) + return srv.ResponseErr(ctx, err.Error()) + } + + return srv.ResponseOK(ctx) +} + +type Response struct { + Msg string `json:"msg"` +} + +func (this *TripartiteService) ResponseOK(ctx http.Context) error { + return ctx.JSON(http2.StatusOK, &bo.QiXingResponse{ + Msg: "SUCCESS", + }) +} + +func (this *TripartiteService) ResponseErr(ctx http.Context, msg string) error { + return ctx.JSON(http2.StatusOK, &bo.QiXingResponse{ + Msg: msg, + }) } From d01900b519585879e7a5666d6c5bc75ddef0d41e Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 11 Dec 2025 18:13:32 +0800 Subject: [PATCH 035/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/config_test.yaml | 4 +++ internal/biz/bo/qixing.go | 19 +++++++++-- internal/pkg/helper/utils_test.go | 34 +++++++++++++++++-- internal/service/qixing.go | 14 +++++++- test/bank_multi_activity_test.go | 56 ++++++++++++++++++++++++++++++- 5 files changed, 120 insertions(+), 7 deletions(-) diff --git a/configs/config_test.yaml b/configs/config_test.yaml index 1a61af0..dbc15ab 100644 --- a/configs/config_test.yaml +++ b/configs/config_test.yaml @@ -118,6 +118,10 @@ aliYunSms: signName: 蓝色兄弟 templateWarning: "SMS_489660721" +tripartite: + qiXing: + appKey: "DrY1zLkOH01q0sN66vrmkdpbWsyb41ho" + #配置日志 logs: business: business.log #业务日志路径:如果不写日志,则不配置或配置为空 diff --git a/internal/biz/bo/qixing.go b/internal/biz/bo/qixing.go index 2d0dd61..6eaf2da 100644 --- a/internal/biz/bo/qixing.go +++ b/internal/biz/bo/qixing.go @@ -1,12 +1,25 @@ package bo +import "github.com/go-playground/validator/v10" + type QiXingRequestBo struct { - Content string `json:"content"` - Timestamp int64 `json:"timestamp"` - Ciphertext string `json:"ciphertext"` + Content string `json:"content" validate:"required"` + Timestamp int64 `json:"timestamp" validate:"required"` + Ciphertext string `json:"ciphertext" validate:"required"` } // QiXingResponse 响应结构体 {"msg":"SUCCESS"} / {"msg":"操作成功"} type QiXingResponse struct { Msg string `json:"msg"` } + +func (c *QiXingRequestBo) Validate() error { + + if err := validator.New().Struct(c); err != nil { + for _, err = range err.(validator.ValidationErrors) { + return err + } + } + + return nil +} diff --git a/internal/pkg/helper/utils_test.go b/internal/pkg/helper/utils_test.go index 04142a2..647fe9f 100644 --- a/internal/pkg/helper/utils_test.go +++ b/internal/pkg/helper/utils_test.go @@ -45,8 +45,38 @@ func queryUsed(useNum *int) { } func TestMd5(t *testing.T) { - s := Md5(`{"product_no":"","start_time":"2025-04-20 09:00:00","end_time":"2025-05-01 00:00:00"}`) - t.Log(s) + + jsonStr := `{ + "id": "4ab2699d-e91d-5460-9810-25fd6d4c69a5", + "create_time": "2025-12-08T17:54:24+08:00", + "resource_type": "encrypt-resource", + "event_type": "COUPON.USE", + "summary": "代金券核销通知", + "original_type": "coupon", + "associated_data": "coupon", + "plain_text": { + "stock_creator_mchid": "1652465541", + "stock_id": "21386484", + "coupon_id": "142388354994", + "coupon_name": "银行卡多笔立减", + "description": "", + "status": "SENDED", + "create_time": "2025-12-08T17:50:48+08:00", + "coupon_type": "NORMAL", + "no_cash": false, + "singleitem": false, + "business_type": "", + "consume_information": { + "consume_time": "2025-12-08T17:54:24+08:00", + "consume_mchid": "1274938601", + "transaction_id": "4200002996202512083063051834", + "consume_amount": 16 + } + } +}` + + ciphertext := Md5(jsonStr) + t.Log(ciphertext) } func TestLength(t *testing.T) { diff --git a/internal/service/qixing.go b/internal/service/qixing.go index b1d14a2..4518be1 100644 --- a/internal/service/qixing.go +++ b/internal/service/qixing.go @@ -1,6 +1,7 @@ package service import ( + "encoding/base64" "encoding/json" "fmt" "github.com/go-kratos/kratos/v2/log" @@ -40,6 +41,11 @@ func (srv *TripartiteService) QiXingNotify(ctx http.Context) error { return srv.ResponseErr(ctx, fmt.Sprintf("json unmarshal bodyBytes error: %v", err)) } + if err = req.Validate(); err != nil { + log.Errorf("qixing notify req validate err ip:%s,error:%v,body:%s", ip, err, string(bodyBytes)) + return srv.ResponseErr(ctx, fmt.Sprintf("validate req error: %v", err)) + } + // 加密校验串,(生成规则:MD5(content+公钥)) if srv.bc.Tripartite.QiXing.AppKey == "" { return srv.ResponseErr(ctx, "qixing appKey is empty") @@ -51,8 +57,14 @@ func (srv *TripartiteService) QiXingNotify(ctx http.Context) error { return srv.ResponseErr(ctx, "sign error") } + wxNotifyDataStr, err := base64.StdEncoding.DecodeString(req.Content) + if err != nil { + log.Errorf("qixing notify wxNotifyDataStr err ip:%s,error:%v,body:%s", ip, err, string(bodyBytes)) + return srv.ResponseErr(ctx, fmt.Sprintf("base64 decode req content error: %v", err)) + } + var wxNotifyData *bo.WechatVoucherNotifyBo - if err = json.Unmarshal([]byte(req.Content), &wxNotifyData); err != nil { + if err = json.Unmarshal(wxNotifyDataStr, &wxNotifyData); err != nil { log.Errorf("qixing notify wxNotifyData err ip:%s,error:%v,body:%s", ip, err, string(bodyBytes)) return srv.ResponseErr(ctx, fmt.Sprintf("json unmarshal wxNotifyData error: %v", err)) } diff --git a/test/bank_multi_activity_test.go b/test/bank_multi_activity_test.go index e6963fc..2e3f446 100644 --- a/test/bank_multi_activity_test.go +++ b/test/bank_multi_activity_test.go @@ -1,6 +1,13 @@ package test -import "testing" +import ( + "encoding/base64" + "encoding/json" + "testing" + "time" + "voucher/internal/biz/bo" + "voucher/internal/pkg/helper" +) func Test_MarketingSend(t *testing.T) { tests := []struct { @@ -47,3 +54,50 @@ func Test_MarketingQuery(t *testing.T) { //openid不是自己的appid下的喔,这也能查询到吗” //不行的,需要是在自己appid下的才能查到 } + +func Test_QixingNotifyData(t *testing.T) { + + wxBody := `{ + "id": "4ab2699d-e91d-5460-9810-25fd6d4c69a5", + "create_time": "2025-12-08T17:54:24+08:00", + "resource_type": "encrypt-resource", + "event_type": "COUPON.USE", + "summary": "代金券核销通知", + "original_type": "coupon", + "associated_data": "coupon", + "plain_text": { + "stock_creator_mchid": "1652465541", + "stock_id": "21386484", + "coupon_id": "142388354994", + "coupon_name": "银行卡多笔立减", + "description": "", + "status": "SENDED", + "create_time": "2025-12-08T17:50:48+08:00", + "coupon_type": "NORMAL", + "no_cash": false, + "singleitem": false, + "business_type": "", + "consume_information": { + "consume_time": "2025-12-08T17:54:24+08:00", + "consume_mchid": "1274938601", + "transaction_id": "4200002996202512083063051834", + "consume_amount": 16 + } + } +}` + content := base64.StdEncoding.EncodeToString([]byte(wxBody)) + + ciphertext := helper.Md5(content + "DrY1zLkOH01q0sN66vrmkdpbWsyb41ho") + + req := bo.QiXingRequestBo{ + Content: content, + Timestamp: time.Now().UnixMilli(), + Ciphertext: ciphertext, + } + + b, _ := json.Marshal(req) + + t.Log(string(b)) + // {"content":"base64(微信通知json对象数据)","timestamp":1765447477945,"ciphertext":"md5(base64(微信通知json对象数据)+key)"} + //t.Logf(`{"content":"%s","timestamp":122345677890,"ciphertext":"%s"}`, content, ciphertext) +} From 43bf445f8b6dddb44116be5d29fa35e04c153a32 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 11 Dec 2025 18:29:23 +0800 Subject: [PATCH 036/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/bo/wechat_notify_bo.go | 28 ++++++++++++++++++++-------- internal/service/qixing.go | 5 +++++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/internal/biz/bo/wechat_notify_bo.go b/internal/biz/bo/wechat_notify_bo.go index b8238b8..56364d9 100644 --- a/internal/biz/bo/wechat_notify_bo.go +++ b/internal/biz/bo/wechat_notify_bo.go @@ -3,21 +3,22 @@ package bo import ( "encoding/json" "fmt" + "github.com/go-playground/validator/v10" "time" "voucher/internal/biz/vo" ) // ConsumeInformation 定义消费信息结构体 type ConsumeInformation struct { - ConsumeTime time.Time `json:"consume_time"` // 核销时间 - ConsumeMchid string `json:"consume_mchid"` // 核销商户号 - TransactionID string `json:"transaction_id"` // 微信支付交易单号 - ConsumeAmount int `json:"consume_amount"` // 核销金额(单位:分) + ConsumeTime time.Time `json:"consume_time" validate:"required"` // 核销时间 + ConsumeMchid string `json:"consume_mchid"` // 核销商户号 + TransactionID string `json:"transaction_id" validate:"required"` // 微信支付交易单号 + ConsumeAmount int `json:"consume_amount" validate:"required"` // 核销金额(单位:分) } // PlainText 定义明文数据结构体 type PlainText struct { - StockCreatorMchid string `json:"stock_creator_mchid"` + StockCreatorMchid string `json:"stock_creator_mchid" validate:"required"` StockID string `json:"stock_id"` CouponID string `json:"coupon_id"` CouponName string `json:"coupon_name"` @@ -32,14 +33,14 @@ type PlainText struct { } type WechatVoucherNotifyBo struct { - ID string `json:"id"` + ID string `json:"id" validate:"required"` CreateTime string `json:"create_time"` ResourceType string `json:"resource_type"` - EventType string `json:"event_type"` + EventType string `json:"event_type" validate:"required"` Summary string `json:"summary"` OriginalType string `json:"original_type"` AssociatedData string `json:"associated_data"` - PlainText PlainText `json:"plain_text"` + PlainText PlainText `json:"plain_text" validate:"required"` } func (d *WechatVoucherNotifyBo) Str() (string, error) { @@ -51,3 +52,14 @@ func (d *WechatVoucherNotifyBo) Str() (string, error) { return string(b), nil } + +func (c *WechatVoucherNotifyBo) Validate() error { + + if err := validator.New().Struct(c); err != nil { + for _, err = range err.(validator.ValidationErrors) { + return err + } + } + + return nil +} diff --git a/internal/service/qixing.go b/internal/service/qixing.go index 4518be1..a302e53 100644 --- a/internal/service/qixing.go +++ b/internal/service/qixing.go @@ -69,6 +69,11 @@ func (srv *TripartiteService) QiXingNotify(ctx http.Context) error { return srv.ResponseErr(ctx, fmt.Sprintf("json unmarshal wxNotifyData error: %v", err)) } + if err = wxNotifyData.Validate(); err != nil { + log.Errorf("qixing notify wxNotifyData validate err ip:%s,error:%v,body:%s", ip, err, string(bodyBytes)) + return srv.ResponseErr(ctx, fmt.Sprintf("validate wxNotifyData error: %v", err)) + } + err = srv.multiBiz.Notify(ctx, "qixing_"+wxNotifyData.PlainText.StockCreatorMchid, wxNotifyData) if err != nil { log.Errorf("qixing notify run err ip:%s,error:%v,body:%s", ip, err, string(bodyBytes)) From a1b59c97a93f6e7a384fb0666d9bcfd3a5f32755 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 11 Dec 2025 18:30:04 +0800 Subject: [PATCH 037/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/qixing.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/internal/service/qixing.go b/internal/service/qixing.go index a302e53..edd4c9f 100644 --- a/internal/service/qixing.go +++ b/internal/service/qixing.go @@ -83,10 +83,6 @@ func (srv *TripartiteService) QiXingNotify(ctx http.Context) error { return srv.ResponseOK(ctx) } -type Response struct { - Msg string `json:"msg"` -} - func (this *TripartiteService) ResponseOK(ctx http.Context) error { return ctx.JSON(http2.StatusOK, &bo.QiXingResponse{ Msg: "SUCCESS", From 947c4e5e5cee98091ef1a08ecfae179ad4545ba9 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 12 Dec 2025 11:11:29 +0800 Subject: [PATCH 038/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 29 +++++++++++++++++++-- internal/biz/repo/multi_notify_data.go | 2 +- internal/data/repoimpl/multi_notify_data.go | 4 +-- internal/service/qixing.go | 2 +- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index c67a11e..f0b5dd3 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -93,7 +93,7 @@ func (biz *MultiBiz) Run(ctx context.Context, source string, req *bo.WechatVouch return fmt.Errorf("批次活动ID为空,不是多笔立减金,请检查") } - mnd, err := biz.MultiNotifyDataRepo.GetByNotifyID(ctx, req.ID) + mnd, err := biz.MultiNotifyDataRepo.GetByNotifyID(ctx, source, req.ID) if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { return fmt.Errorf("查询通知数据错误 error: %v", err) } @@ -118,6 +118,31 @@ func (biz *MultiBiz) Run(ctx context.Context, source string, req *bo.WechatVouch return biz.Request(ctx, mnd, nl) } +func (biz *MultiBiz) RetryRunByMultiNotifyDataId(ctx context.Context, multiNotifyDataId int64) error { + + mnd, err := biz.MultiNotifyDataRepo.GetByID(ctx, multiNotifyDataId) + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return fmt.Errorf("查询通知数据错误 error: %v", err) + } + + order, err := biz.OrderRepo.GetByOrderNo(ctx, mnd.OrderNo) + if err != nil { + return fmt.Errorf("订单查询错误 error: %v", err) + } + + var req *bo.WechatVoucherNotifyBo + if err = json.Unmarshal([]byte(mnd.OriginalData), &req); err != nil { + return fmt.Errorf("通知数据 json unmarshal 错误 error: %v", err) + } + + nl, err := biz.nlCreate(ctx, req, mnd, order) + if err != nil { + return fmt.Errorf("创建通知日志错误 error: %v", err) + } + + return biz.Request(ctx, mnd, nl) +} + func (biz *MultiBiz) mndCreate(ctx context.Context, source string, req *bo.WechatVoucherNotifyBo, order *bo.OrderBo) (*bo.MultiNotifyDataBo, error) { originalData, err := req.Str() @@ -185,7 +210,7 @@ func (biz *MultiBiz) bizContent(nl *bo.MultiNotifyLogBo) (string, error) { bizJsonBytes, err := json.Marshal(req) if err != nil { - return "", err + return "", fmt.Errorf("json.Marshal CmbNotifyRequest error: %v", err) } return string(bizJsonBytes), nil diff --git a/internal/biz/repo/multi_notify_data.go b/internal/biz/repo/multi_notify_data.go index b0b9cc5..b77adfe 100644 --- a/internal/biz/repo/multi_notify_data.go +++ b/internal/biz/repo/multi_notify_data.go @@ -9,6 +9,6 @@ type MultiNotifyDataRepo interface { FindNoticeNumZero(ctx context.Context, fun func(ctx context.Context, rows []*bo.MultiNotifyDataBo) error) error Create(ctx context.Context, req *bo.MultiNotifyDataBo) (*bo.MultiNotifyDataBo, error) GetByID(ctx context.Context, id int64) (*bo.MultiNotifyDataBo, error) - GetByNotifyID(ctx context.Context, notifyId string) (*bo.MultiNotifyDataBo, error) + GetByNotifyID(ctx context.Context, source, notifyId string) (*bo.MultiNotifyDataBo, error) AddNoticeNum(ctx context.Context, id int64) error } diff --git a/internal/data/repoimpl/multi_notify_data.go b/internal/data/repoimpl/multi_notify_data.go index 4cbe272..e152592 100644 --- a/internal/data/repoimpl/multi_notify_data.go +++ b/internal/data/repoimpl/multi_notify_data.go @@ -87,11 +87,11 @@ func (p *MultiNotifyDataRepoImpl) GetByID(ctx context.Context, id int64) (*bo.Mu return p.ToBo(&item), nil } -func (p *MultiNotifyDataRepoImpl) GetByNotifyID(ctx context.Context, notifyId string) (*bo.MultiNotifyDataBo, error) { +func (p *MultiNotifyDataRepoImpl) GetByNotifyID(ctx context.Context, source, notifyId string) (*bo.MultiNotifyDataBo, error) { var item model.MultiNotifyDatum - tx := p.DB(ctx).Where(model.MultiNotifyDatum{NotifyID: notifyId}).First(&item) + tx := p.DB(ctx).Where(model.MultiNotifyDatum{Source: source, NotifyID: notifyId}).First(&item) if tx.Error != nil { return nil, tx.Error diff --git a/internal/service/qixing.go b/internal/service/qixing.go index edd4c9f..a4377ea 100644 --- a/internal/service/qixing.go +++ b/internal/service/qixing.go @@ -54,7 +54,7 @@ func (srv *TripartiteService) QiXingNotify(ctx http.Context) error { sign := helper.Md5(req.Content + srv.bc.Tripartite.QiXing.AppKey) if sign != req.Ciphertext { log.Errorf("qixing notify sign err ip:%s,error:%v,body:%s", ip, err, string(bodyBytes)) - return srv.ResponseErr(ctx, "sign error") + return srv.ResponseErr(ctx, "verify sign error") } wxNotifyDataStr, err := base64.StdEncoding.DecodeString(req.Content) From d3584edec57feed48b030cf7b066e4129e556d51 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 12 Dec 2025 11:30:16 +0800 Subject: [PATCH 039/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 26 ++++++++++++++++----- internal/biz/repo/order.go | 3 +++ internal/data/repoimpl/order.go | 41 +++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index f0b5dd3..26422a8 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -110,12 +110,7 @@ func (biz *MultiBiz) Run(ctx context.Context, source string, req *bo.WechatVouch } } - nl, err := biz.nlCreate(ctx, req, mnd, order) - if err != nil { - return fmt.Errorf("创建通知日志错误 error: %v", err) - } - - return biz.Request(ctx, mnd, nl) + return biz.run(ctx, req, mnd, order) } func (biz *MultiBiz) RetryRunByMultiNotifyDataId(ctx context.Context, multiNotifyDataId int64) error { @@ -135,6 +130,25 @@ func (biz *MultiBiz) RetryRunByMultiNotifyDataId(ctx context.Context, multiNotif return fmt.Errorf("通知数据 json unmarshal 错误 error: %v", err) } + return biz.run(ctx, req, mnd, order) +} + +func (biz *MultiBiz) run(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) error { + + if req.PlainText.Status.IsUsed() { + if order.Status.IsUse() && req.PlainText.ConsumeInformation.ConsumeTime != *order.LastUseTime { + if err := biz.OrderRepo.OverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime); err != nil { + return fmt.Errorf("订单使用完成修改发生错误 error: %v", err) + } + } + } else { + if req.PlainText.ConsumeInformation.ConsumeTime != *order.LastUseTime { + if err := biz.OrderRepo.LastUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime); err != nil { + return fmt.Errorf("订单使用修改发生错误 error: %v", err) + } + } + } + nl, err := biz.nlCreate(ctx, req, mnd, order) if err != nil { return fmt.Errorf("创建通知日志错误 error: %v", err) diff --git a/internal/biz/repo/order.go b/internal/biz/repo/order.go index 3cc8bbc..08f920e 100644 --- a/internal/biz/repo/order.go +++ b/internal/biz/repo/order.go @@ -2,6 +2,7 @@ package repo import ( "context" + "time" "voucher/internal/biz/bo" "voucher/internal/biz/do" "voucher/internal/biz/vo" @@ -25,6 +26,8 @@ type OrderRepo interface { Fail(ctx context.Context, id uint64, remark string) error Used(ctx context.Context, id uint64) error NotifyUsed(ctx context.Context, id uint64, transactionId string) error + LastUsed(ctx context.Context, id uint64, lastUseTime time.Time) error + OverUsed(ctx context.Context, id uint64, lastUseTime time.Time) error Available(ctx context.Context, id uint64) error Expired(ctx context.Context, id uint64) error } diff --git a/internal/data/repoimpl/order.go b/internal/data/repoimpl/order.go index 27b52bb..e1d53c6 100644 --- a/internal/data/repoimpl/order.go +++ b/internal/data/repoimpl/order.go @@ -453,6 +453,47 @@ func (p *OrderRepoImpl) Used(ctx context.Context, id uint64) error { return nil } +func (p *OrderRepoImpl) LastUsed(ctx context.Context, id uint64, lastUseTime time.Time) error { + now := time.Now() + + tx := p.DB(ctx). + Where(model.Order{ + ID: id, + }). + Updates(model.Order{ + Remark: "核销", + LastUseTime: &lastUseTime, + UpdateTime: &now, + }) + + if tx.Error != nil { + return fmt.Errorf("update db fail %w", tx.Error) + } + + return nil +} + +func (p *OrderRepoImpl) OverUsed(ctx context.Context, id uint64, lastUseTime time.Time) error { + now := time.Now() + + tx := p.DB(ctx). + Where(model.Order{ + ID: id, + }). + Updates(model.Order{ + Status: vo.OrderStatusUse.GetValue(), + Remark: "核销完成", + LastUseTime: &lastUseTime, + UpdateTime: &now, + }) + + if tx.Error != nil { + return fmt.Errorf("update db fail %w", tx.Error) + } + + return nil +} + func (p *OrderRepoImpl) NotifyUsed(ctx context.Context, id uint64, transactionId string) error { now := time.Now() From 75b5b9af287feb6671e772aa24b5bd2d1b19f453 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 12 Dec 2025 11:38:21 +0800 Subject: [PATCH 040/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 26422a8..2236e3f 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -136,16 +136,12 @@ func (biz *MultiBiz) RetryRunByMultiNotifyDataId(ctx context.Context, multiNotif func (biz *MultiBiz) run(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) error { if req.PlainText.Status.IsUsed() { - if order.Status.IsUse() && req.PlainText.ConsumeInformation.ConsumeTime != *order.LastUseTime { - if err := biz.OrderRepo.OverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime); err != nil { - return fmt.Errorf("订单使用完成修改发生错误 error: %v", err) - } + if err := biz.OrderRepo.OverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime); err != nil { + return fmt.Errorf("订单使用完成修改发生错误 error: %v", err) } } else { - if req.PlainText.ConsumeInformation.ConsumeTime != *order.LastUseTime { - if err := biz.OrderRepo.LastUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime); err != nil { - return fmt.Errorf("订单使用修改发生错误 error: %v", err) - } + if err := biz.OrderRepo.LastUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime); err != nil { + return fmt.Errorf("订单使用修改发生错误 error: %v", err) } } From 49e70928394c6da6de9b89ca5a97bb8c56ca0172 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 12 Dec 2025 11:44:45 +0800 Subject: [PATCH 041/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/bo/multi_notify_data_bo.go | 1 + internal/biz/multi.go | 1 + internal/data/model/multi_notify_data.gen.go | 27 ++++++++++---------- internal/data/repoimpl/multi_notify_data.go | 1 + 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/internal/biz/bo/multi_notify_data_bo.go b/internal/biz/bo/multi_notify_data_bo.go index 8f541cf..6779bde 100644 --- a/internal/biz/bo/multi_notify_data_bo.go +++ b/internal/biz/bo/multi_notify_data_bo.go @@ -13,6 +13,7 @@ type MultiNotifyDataBo struct { StockID string ConsumeAmount int32 ConsumeTime *time.Time + TransactionID string EventType string OriginalData string NoticeNum int32 diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 2236e3f..7fe2b3f 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -169,6 +169,7 @@ func (biz *MultiBiz) mndCreate(ctx context.Context, source string, req *bo.Wecha StockID: req.PlainText.StockID, ConsumeAmount: int32(req.PlainText.ConsumeInformation.ConsumeAmount), ConsumeTime: &req.PlainText.ConsumeInformation.ConsumeTime, + TransactionID: req.PlainText.ConsumeInformation.TransactionID, EventType: req.EventType, OriginalData: originalData, }) diff --git a/internal/data/model/multi_notify_data.gen.go b/internal/data/model/multi_notify_data.gen.go index f66a190..84ba3f2 100644 --- a/internal/data/model/multi_notify_data.gen.go +++ b/internal/data/model/multi_notify_data.gen.go @@ -13,19 +13,20 @@ const TableNameMultiNotifyDatum = "multi_notify_data" // MultiNotifyDatum mapped from table type MultiNotifyDatum struct { ID int64 `gorm:"column:id;primaryKey" json:"id"` - Source string `gorm:"column:source;not null;comment:来源" json:"source"` // 来源 - NotifyID string `gorm:"column:notify_id;not null;comment:回调通知id" json:"notify_id"` // 回调通知id - OrderNo string `gorm:"column:order_no;not null;comment:订单号" json:"order_no"` // 订单号 - OutBizNo string `gorm:"column:out_biz_no;not null;comment:外部业务号" json:"out_biz_no"` // 外部业务号 - CouponID string `gorm:"column:coupon_id;not null;comment:券id" json:"coupon_id"` // 券id - StockID string `gorm:"column:stock_id;not null;comment:微信批次号" json:"stock_id"` // 微信批次号 - ConsumeAmount int32 `gorm:"column:consume_amount;not null;comment:核销金额" json:"consume_amount"` // 核销金额 - ConsumeTime *time.Time `gorm:"column:consume_time;not null;comment:核销时间" json:"consume_time"` // 核销时间 - EventType string `gorm:"column:event_type;not null;comment:通知的类型" json:"event_type"` // 通知的类型 - OriginalData string `gorm:"column:original_data;not null;comment:微信回调通知原始数据" json:"original_data"` // 微信回调通知原始数据 - NoticeNum int32 `gorm:"column:notice_num;not null;comment:通知下游次数" json:"notice_num"` // 通知下游次数 - CreateTime *time.Time `gorm:"column:create_time;not null;comment:创建时间" json:"create_time"` // 创建时间 - UpdateTime *time.Time `gorm:"column:update_time;comment:修改时间" json:"update_time"` // 修改时间 + Source string `gorm:"column:source;not null;comment:来源" json:"source"` // 来源 + NotifyID string `gorm:"column:notify_id;not null;comment:回调通知id" json:"notify_id"` // 回调通知id + OrderNo string `gorm:"column:order_no;not null;comment:订单号" json:"order_no"` // 订单号 + OutBizNo string `gorm:"column:out_biz_no;not null;comment:外部业务号" json:"out_biz_no"` // 外部业务号 + CouponID string `gorm:"column:coupon_id;not null;comment:券id" json:"coupon_id"` // 券id + StockID string `gorm:"column:stock_id;not null;comment:微信批次号" json:"stock_id"` // 微信批次号 + ConsumeAmount int32 `gorm:"column:consume_amount;not null;comment:核销金额" json:"consume_amount"` // 核销金额 + ConsumeTime *time.Time `gorm:"column:consume_time;not null;comment:核销时间" json:"consume_time"` // 核销时间 + TransactionID string `gorm:"column:transaction_id;not null;comment:微信支付系统生成的订单号" json:"transaction_id"` // 微信支付系统生成的订单号 + EventType string `gorm:"column:event_type;not null;comment:通知的类型" json:"event_type"` // 通知的类型 + OriginalData string `gorm:"column:original_data;not null;comment:微信回调通知原始数据" json:"original_data"` // 微信回调通知原始数据 + NoticeNum int32 `gorm:"column:notice_num;not null;comment:通知下游次数" json:"notice_num"` // 通知下游次数 + CreateTime *time.Time `gorm:"column:create_time;not null;comment:创建时间" json:"create_time"` // 创建时间 + UpdateTime *time.Time `gorm:"column:update_time;comment:修改时间" json:"update_time"` // 修改时间 } // TableName MultiNotifyDatum's table name diff --git a/internal/data/repoimpl/multi_notify_data.go b/internal/data/repoimpl/multi_notify_data.go index e152592..2febea8 100644 --- a/internal/data/repoimpl/multi_notify_data.go +++ b/internal/data/repoimpl/multi_notify_data.go @@ -57,6 +57,7 @@ func (p *MultiNotifyDataRepoImpl) Create(ctx context.Context, req *bo.MultiNotif StockID: req.StockID, ConsumeAmount: req.ConsumeAmount, ConsumeTime: req.ConsumeTime, + TransactionID: req.TransactionID, EventType: req.EventType, OriginalData: req.OriginalData, NoticeNum: 0, From 5dfec0d509f8fb57df74c9cb08e3c75cb00e6005 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 12 Dec 2025 17:11:23 +0800 Subject: [PATCH 042/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 7fe2b3f..a87c1aa 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -56,7 +56,6 @@ func (biz *MultiBiz) Notify(ctx context.Context, source string, req *bo.WechatVo cl := vo.MultiNotifyLockKey.BuildCache([]string{ source, - req.PlainText.StockCreatorMchid, req.PlainText.StockID, req.PlainText.CouponID, }) From 349fe2621cc555e164aeeae139b64929dedd6e93 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 12 Dec 2025 17:18:03 +0800 Subject: [PATCH 043/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index a87c1aa..21ed30e 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -247,10 +247,10 @@ func (biz *MultiBiz) GetRequest(ctx context.Context, nl *bo.MultiNotifyLogBo) (* func (biz *MultiBiz) Request(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl *bo.MultiNotifyLogBo) error { if nl.RequestURL == "" { - if err := biz.notifyFail(ctx, nl, "回调通知招行地址为空"); err != nil { + if err := biz.notifyFail(ctx, nl, "回调通知招行地址为空,不做通知"); err != nil { return err } - // 回调通知地址为空,不反回错误,不做再次通知处理 + // 回调通知地址为空,不返回错误,不做再次通知处理 return nil } @@ -268,7 +268,9 @@ func (biz *MultiBiz) Request(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl } if err = biz.CmbMixRepo.VerifyResponse(ctx, reply); err != nil { + errMsg := fmt.Sprintf("回调通知招行返回验证结果发生错误,resp:%+v error:%s", reply, err.Error()) + if err2 := biz.notifyFail(ctx, nl, errMsg); err2 != nil { return err2 } @@ -279,22 +281,27 @@ func (biz *MultiBiz) Request(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl } func (biz *MultiBiz) notifyFail(ctx context.Context, nl *bo.MultiNotifyLogBo, remark string) error { - return biz.MultiNotifyLogRepo.Fail(ctx, nl.ID, remark) + + if err := biz.MultiNotifyLogRepo.Fail(ctx, nl.ID, remark); err != nil { + return fmt.Errorf("更新通知日志失败状态发生错误 error: %v", err) + } + + return nil } func (biz *MultiBiz) notifySuccess(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl *bo.MultiNotifyLogBo, reply *v1.CmbReply) error { response, err := json.Marshal(reply) if err != nil { - return err + return fmt.Errorf("json.Marshal CmbReply error: %v", err) } if err = biz.MultiNotifyLogRepo.Success(ctx, nl.ID, string(response)); err != nil { - return err + return fmt.Errorf("更新通知日志成功状态发生错误 error: %v", err) } if err = biz.MultiNotifyDataRepo.AddNoticeNum(ctx, mmd.ID); err != nil { - return err + return fmt.Errorf("更新通知数据通知次数发生错误 error: %v", err) } return nil From 25424eec72067cfd348ba5e905d8abe8688ddea6 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 12 Dec 2025 17:39:52 +0800 Subject: [PATCH 044/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/qixing.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/internal/service/qixing.go b/internal/service/qixing.go index a4377ea..cbaa499 100644 --- a/internal/service/qixing.go +++ b/internal/service/qixing.go @@ -23,6 +23,11 @@ func NewTripartiteService(bc *conf.Bootstrap, multiBiz *biz.MultiBiz) *Tripartit return &TripartiteService{bc: bc, multiBiz: multiBiz} } +// QiXingNotify +// 重试规则 +// 1.重试间隔:每次推送失败后,间隔5分钟重新推送; +// 2.最大重试次数:累计推送5 次(首次 + 4 次重试; +// 3.停止条件:返回成功响应 或 达到 4 次重试上限,停止推送。 func (srv *TripartiteService) QiXingNotify(ctx http.Context) error { ip := helper.GetClientIP(ctx) From 10eda78e58b980de3df28505362f73e833b70c62 Mon Sep 17 00:00:00 2001 From: ziming Date: Mon, 15 Dec 2025 09:58:12 +0800 Subject: [PATCH 045/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 21ed30e..c094b97 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -7,7 +7,6 @@ import ( "fmt" "github.com/go-kratos/kratos/v2/log" "gorm.io/gorm" - "time" v1 "voucher/api/v1" "voucher/internal/biz/bo" "voucher/internal/biz/cmb" @@ -149,7 +148,7 @@ func (biz *MultiBiz) run(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd return fmt.Errorf("创建通知日志错误 error: %v", err) } - return biz.Request(ctx, mnd, nl) + return biz.Request(ctx, mnd, nl, order) } func (biz *MultiBiz) mndCreate(ctx context.Context, source string, req *bo.WechatVoucherNotifyBo, order *bo.OrderBo) (*bo.MultiNotifyDataBo, error) { @@ -193,7 +192,7 @@ func (biz *MultiBiz) nlCreate(ctx context.Context, req *bo.WechatVoucherNotifyBo OrderCreateTime: order.CreateTime, CouponCreateTime: &req.PlainText.CreateTime, } - request, err := biz.GetRequest(ctx, nl) + request, err := biz.GetRequest(ctx, nl, order) if err != nil { return nil, err } @@ -203,19 +202,20 @@ func (biz *MultiBiz) nlCreate(ctx context.Context, req *bo.WechatVoucherNotifyBo return biz.MultiNotifyLogRepo.Create(ctx, nl) } -func (biz *MultiBiz) bizContent(nl *bo.MultiNotifyLogBo) (string, error) { +func (biz *MultiBiz) bizContent(nl *bo.MultiNotifyLogBo, order *bo.OrderBo) (string, error) { req := &v1.CmbNotifyRequest{ // 待确定 - Ticket: nl.OrderNo, - TransDate: "", // 格式yyyy-mm-dd hh:mm:ss.sss - OrgNo: biz.bc.Cmb.OrgNo, - //Attach: nl.Attach, - Ext: "", + Ticket: nl.OrderNo, // 券订单号,lsxd订单号 + TransDate: "", // 核销时间,验券时间,格式yyyy-mm-dd hh:mm:ss.sss + Status: "1", // 状态:1-核销成功 + OrgNo: biz.bc.Cmb.OrgNo, // cmb固定值 + Attach: order.Attach, // cmb拓展参数 + Ext: "", } if nl.ConsumeTime != nil { req.TransDate = nl.ConsumeTime.Format("2006-01-02 15:04:05.000") } else { - req.TransDate = time.Now().Format("2006-01-02 15:04:05.000") + req.TransDate = nl.CreateTime.Format("2006-01-02 15:04:05.000") } bizJsonBytes, err := json.Marshal(req) @@ -226,9 +226,9 @@ func (biz *MultiBiz) bizContent(nl *bo.MultiNotifyLogBo) (string, error) { return string(bizJsonBytes), nil } -func (biz *MultiBiz) GetRequest(ctx context.Context, nl *bo.MultiNotifyLogBo) (*v1.CmbRequest, error) { +func (biz *MultiBiz) GetRequest(ctx context.Context, nl *bo.MultiNotifyLogBo, order *bo.OrderBo) (*v1.CmbRequest, error) { - bizContent, err := biz.bizContent(nl) + bizContent, err := biz.bizContent(nl, order) if err != nil { return nil, err } @@ -244,7 +244,7 @@ func (biz *MultiBiz) GetRequest(ctx context.Context, nl *bo.MultiNotifyLogBo) (* return request, nil } -func (biz *MultiBiz) Request(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl *bo.MultiNotifyLogBo) error { +func (biz *MultiBiz) Request(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl *bo.MultiNotifyLogBo, order *bo.OrderBo) error { if nl.RequestURL == "" { if err := biz.notifyFail(ctx, nl, "回调通知招行地址为空,不做通知"); err != nil { @@ -254,7 +254,7 @@ func (biz *MultiBiz) Request(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl return nil } - request, err := biz.GetRequest(ctx, nl) + request, err := biz.GetRequest(ctx, nl, order) if err != nil { return err } From 408a929b33b50da6a1962abdb5f55f5259310e26 Mon Sep 17 00:00:00 2001 From: ziming Date: Mon, 15 Dec 2025 10:42:42 +0800 Subject: [PATCH 046/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/bo/multi_notify_data_bo.go | 1 + internal/biz/multi.go | 11 ++++++----- internal/biz/wechat_notify.go | 2 +- internal/data/model/multi_notify_data.gen.go | 1 + internal/data/repoimpl/multi_notify_data.go | 1 + internal/service/qixing.go | 2 +- 6 files changed, 11 insertions(+), 7 deletions(-) diff --git a/internal/biz/bo/multi_notify_data_bo.go b/internal/biz/bo/multi_notify_data_bo.go index 6779bde..d558e4e 100644 --- a/internal/biz/bo/multi_notify_data_bo.go +++ b/internal/biz/bo/multi_notify_data_bo.go @@ -6,6 +6,7 @@ import "time" type MultiNotifyDataBo struct { ID int64 Source string + IP string NotifyID string OrderNo string OutBizNo string diff --git a/internal/biz/multi.go b/internal/biz/multi.go index c094b97..6e71181 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -51,7 +51,7 @@ func NewMultiBiz( } } -func (biz *MultiBiz) Notify(ctx context.Context, source string, req *bo.WechatVoucherNotifyBo) error { +func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.WechatVoucherNotifyBo) error { cl := vo.MultiNotifyLockKey.BuildCache([]string{ source, @@ -66,7 +66,7 @@ func (biz *MultiBiz) Notify(ctx context.Context, source string, req *bo.WechatVo return err } - if err = biz.Run(ctx, source, req, order); err != nil { + if err = biz.Run(ctx, ip, source, req, order); err != nil { return err } @@ -85,7 +85,7 @@ func (biz *MultiBiz) order(ctx context.Context, req *bo.WechatVoucherNotifyBo) ( return order, nil } -func (biz *MultiBiz) Run(ctx context.Context, source string, req *bo.WechatVoucherNotifyBo, order *bo.OrderBo) error { +func (biz *MultiBiz) Run(ctx context.Context, ip, source string, req *bo.WechatVoucherNotifyBo, order *bo.OrderBo) error { if order.ActivityId == "" { return fmt.Errorf("批次活动ID为空,不是多笔立减金,请检查") @@ -102,7 +102,7 @@ func (biz *MultiBiz) Run(ctx context.Context, source string, req *bo.WechatVouch return nil } } else { - mnd, err = biz.mndCreate(ctx, source, req, order) + mnd, err = biz.mndCreate(ctx, ip, source, req, order) if err != nil { return fmt.Errorf("创建通知数据错误 error: %v", err) } @@ -151,7 +151,7 @@ func (biz *MultiBiz) run(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd return biz.Request(ctx, mnd, nl, order) } -func (biz *MultiBiz) mndCreate(ctx context.Context, source string, req *bo.WechatVoucherNotifyBo, order *bo.OrderBo) (*bo.MultiNotifyDataBo, error) { +func (biz *MultiBiz) mndCreate(ctx context.Context, ip, source string, req *bo.WechatVoucherNotifyBo, order *bo.OrderBo) (*bo.MultiNotifyDataBo, error) { originalData, err := req.Str() if err != nil { @@ -160,6 +160,7 @@ func (biz *MultiBiz) mndCreate(ctx context.Context, source string, req *bo.Wecha return biz.MultiNotifyDataRepo.Create(ctx, &bo.MultiNotifyDataBo{ Source: source, + IP: ip, NotifyID: req.ID, OrderNo: order.OrderNo, OutBizNo: order.OutBizNo, diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index 6fd7c74..be88e76 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -23,7 +23,7 @@ func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, tag string, re } if order.ActivityId != "" { - return this.MultiBiz.Run(ctx, "lsxd_"+order.MerchantNo, req, order) + return this.MultiBiz.Run(ctx, "127.0.0.1", "lsxd_"+req.PlainText.StockCreatorMchid, req, order) } if req.PlainText.Status.IsSended() { diff --git a/internal/data/model/multi_notify_data.gen.go b/internal/data/model/multi_notify_data.gen.go index 84ba3f2..bffbf9b 100644 --- a/internal/data/model/multi_notify_data.gen.go +++ b/internal/data/model/multi_notify_data.gen.go @@ -13,6 +13,7 @@ const TableNameMultiNotifyDatum = "multi_notify_data" // MultiNotifyDatum mapped from table type MultiNotifyDatum struct { ID int64 `gorm:"column:id;primaryKey" json:"id"` + IP string `gorm:"column:ip;not null;comment:ip" json:"ip"` // ip Source string `gorm:"column:source;not null;comment:来源" json:"source"` // 来源 NotifyID string `gorm:"column:notify_id;not null;comment:回调通知id" json:"notify_id"` // 回调通知id OrderNo string `gorm:"column:order_no;not null;comment:订单号" json:"order_no"` // 订单号 diff --git a/internal/data/repoimpl/multi_notify_data.go b/internal/data/repoimpl/multi_notify_data.go index 2febea8..83a5acd 100644 --- a/internal/data/repoimpl/multi_notify_data.go +++ b/internal/data/repoimpl/multi_notify_data.go @@ -50,6 +50,7 @@ func (p *MultiNotifyDataRepoImpl) Create(ctx context.Context, req *bo.MultiNotif info := &model.MultiNotifyDatum{ Source: req.Source, + IP: req.IP, NotifyID: req.NotifyID, OrderNo: req.OrderNo, OutBizNo: req.OutBizNo, diff --git a/internal/service/qixing.go b/internal/service/qixing.go index cbaa499..134c9c5 100644 --- a/internal/service/qixing.go +++ b/internal/service/qixing.go @@ -79,7 +79,7 @@ func (srv *TripartiteService) QiXingNotify(ctx http.Context) error { return srv.ResponseErr(ctx, fmt.Sprintf("validate wxNotifyData error: %v", err)) } - err = srv.multiBiz.Notify(ctx, "qixing_"+wxNotifyData.PlainText.StockCreatorMchid, wxNotifyData) + err = srv.multiBiz.Notify(ctx, ip, "qixing_"+wxNotifyData.PlainText.StockCreatorMchid, wxNotifyData) if err != nil { log.Errorf("qixing notify run err ip:%s,error:%v,body:%s", ip, err, string(bodyBytes)) return srv.ResponseErr(ctx, err.Error()) From 654540ba65c370f35a425c2cfb64e8affec2008d Mon Sep 17 00:00:00 2001 From: ziming Date: Mon, 15 Dec 2025 15:03:31 +0800 Subject: [PATCH 047/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/v1/cmb_cpn.proto | 27 ++++++++++++++++++ internal/biz/bo/multi_notify_log_bo.go | 7 +++-- internal/biz/multi.go | 33 +++++++++++++--------- internal/biz/vo/cmb.go | 3 +- internal/data/repoimpl/multi_notify_log.go | 2 +- internal/pkg/cmb/sm2_test.go | 2 +- 6 files changed, 56 insertions(+), 18 deletions(-) diff --git a/api/v1/cmb_cpn.proto b/api/v1/cmb_cpn.proto index ae03540..db0e675 100644 --- a/api/v1/cmb_cpn.proto +++ b/api/v1/cmb_cpn.proto @@ -210,6 +210,7 @@ message CmbNotifyRequest { string ext = 13 [json_name = "ext"]; string attach = 14 [json_name = "attach"]; } + message CmbNotifyReply { // 接口调用返回码,1000 成功,1001 失败 string respCode = 1 [json_name = "respCode"]; @@ -217,6 +218,32 @@ message CmbNotifyReply { string respMsg = 2 [json_name = "respMsg"]; } +message CmbMultiNotifyRequest { + // cmb业务号 + string transactionId = 1 [json_name = "transactionId"]; + // 活动编号 + string activityId = 2 [json_name = "activityId"]; + // 微信券id + string couponId = 3 [json_name = "couponId"]; + // 券领取时间 + string acquiredDate = 4 [json_name = "acquiredDate"]; + // 券状态0:可使用,1:已使用 + string status = 5 [json_name = "status"]; + // 券核销时间 格式:yyyy-MM-dd HH:mm:ss.sss + string transDate = 6 [json_name = "transDate"]; + // 券核销金额,单位-分 + string transAmount = 7 [json_name = "transAmount"]; + // 券核销支付单号,微信支付系统生成的订单号 + string orderId = 8 [json_name = "orderId"]; + // 优惠券券码 codeNo + string ticket = 9 [json_name = "ticket"]; + // 发码机构号,固定值,掌上生活优惠券系统提供 + string orgNo = 10 [json_name = "orgNo"]; + // 500 标识位 + string attach = 12 [json_name = "attach"]; + // 扩展字段 + string ext = 11 [json_name = "ext"]; +} message EncryptBodyRequest { string encryptBody = 1 [json_name = "encryptBody"]; diff --git a/internal/biz/bo/multi_notify_log_bo.go b/internal/biz/bo/multi_notify_log_bo.go index 487ab23..1fb27c9 100644 --- a/internal/biz/bo/multi_notify_log_bo.go +++ b/internal/biz/bo/multi_notify_log_bo.go @@ -1,6 +1,9 @@ package bo -import "time" +import ( + "time" + "voucher/internal/biz/vo" +) // MultiNotifyLogBo 领域实体Bo结构,字段和模型字段保持一致 type MultiNotifyLogBo struct { @@ -12,7 +15,7 @@ type MultiNotifyLogBo struct { ActivityNo string StockID string EventType string - Status string + Status vo.WechatVoucherStatus ConsumeAmount int32 ConsumeTime *time.Time TransactionID string diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 6e71181..20332ea 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -184,7 +184,7 @@ func (biz *MultiBiz) nlCreate(ctx context.Context, req *bo.WechatVoucherNotifyBo ActivityNo: order.ProductNo, StockID: mnd.StockID, EventType: mnd.EventType, - Status: req.PlainText.Status.GetValue(), + Status: req.PlainText.Status, ConsumeAmount: mnd.ConsumeAmount, ConsumeTime: mnd.ConsumeTime, TransactionID: req.PlainText.ConsumeInformation.TransactionID, @@ -193,10 +193,12 @@ func (biz *MultiBiz) nlCreate(ctx context.Context, req *bo.WechatVoucherNotifyBo OrderCreateTime: order.CreateTime, CouponCreateTime: &req.PlainText.CreateTime, } + request, err := biz.GetRequest(ctx, nl, order) if err != nil { return nil, err } + b, _ := json.Marshal(request) nl.Request = string(b) @@ -205,18 +207,23 @@ func (biz *MultiBiz) nlCreate(ctx context.Context, req *bo.WechatVoucherNotifyBo func (biz *MultiBiz) bizContent(nl *bo.MultiNotifyLogBo, order *bo.OrderBo) (string, error) { - req := &v1.CmbNotifyRequest{ // 待确定 - Ticket: nl.OrderNo, // 券订单号,lsxd订单号 - TransDate: "", // 核销时间,验券时间,格式yyyy-mm-dd hh:mm:ss.sss - Status: "1", // 状态:1-核销成功 - OrgNo: biz.bc.Cmb.OrgNo, // cmb固定值 - Attach: order.Attach, // cmb拓展参数 - Ext: "", + req := &v1.CmbMultiNotifyRequest{ + TransactionId: nl.OutBizNo, // cmb业务号 + ActivityId: nl.ActivityNo, // 批次活动号 + CouponId: nl.CouponID, // 微信券券号 + AcquiredDate: order.ReceiveSuccessTime.Format("2006-01-02 15:04:05.000"), // 券领取时间 + Status: "0", // 券状态 0:可使用,1:已使用 + TransDate: nl.ConsumeTime.Format("2006-01-02 15:04:05.000"), // 核销时间,验券时间,格式yyyy-mm-dd hh:mm:ss.sss + TransAmount: fmt.Sprintf("%d", nl.ConsumeAmount), + OrderId: nl.TransactionID, // 券核销支付单号 + Ticket: nl.OrderNo, // 券订单号,lsxd订单号 + OrgNo: biz.bc.Cmb.OrgNo, // cmb固定值 + Attach: order.Attach, // cmb拓展参数 + Ext: "", } - if nl.ConsumeTime != nil { - req.TransDate = nl.ConsumeTime.Format("2006-01-02 15:04:05.000") - } else { - req.TransDate = nl.CreateTime.Format("2006-01-02 15:04:05.000") + + if nl.Status.IsUsed() { + req.Status = "1" } bizJsonBytes, err := json.Marshal(req) @@ -235,7 +242,7 @@ func (biz *MultiBiz) GetRequest(ctx context.Context, nl *bo.MultiNotifyLogBo, or } request, err := biz.CmbMixRepo.GetRequest(ctx, &bo.CmbRequestBo{ - FuncName: vo.CmbNotifyFuncName, // 待确定 + FuncName: vo.CmbNotifyFuncNameUpdateCodeStatusForMulti, BizContent: bizContent, }) if err != nil { diff --git a/internal/biz/vo/cmb.go b/internal/biz/vo/cmb.go index d89d0b1..3b22bbf 100644 --- a/internal/biz/vo/cmb.go +++ b/internal/biz/vo/cmb.go @@ -5,7 +5,8 @@ type CmbFuncName string const ( // CmbNotifyFuncName . 券状态回调通知方法 - CmbNotifyFuncName CmbFuncName = "updateCodeStatus.json" + CmbNotifyFuncName CmbFuncName = "updateCodeStatus.json" + CmbNotifyFuncNameUpdateCodeStatusForMulti CmbFuncName = "updateCodeStatusForMulti.json" ) func (s CmbFuncName) GetValue() string { diff --git a/internal/data/repoimpl/multi_notify_log.go b/internal/data/repoimpl/multi_notify_log.go index 4a4ccc9..a1d08ea 100644 --- a/internal/data/repoimpl/multi_notify_log.go +++ b/internal/data/repoimpl/multi_notify_log.go @@ -41,7 +41,7 @@ func (p *MultiNotifyLogRepoImpl) Create(ctx context.Context, req *bo.MultiNotify ActivityNo: req.ActivityNo, StockID: req.StockID, EventType: req.EventType, - Status: req.Status, + Status: req.Status.GetValue(), ConsumeAmount: req.ConsumeAmount, ConsumeTime: req.ConsumeTime, TransactionID: req.TransactionID, diff --git a/internal/pkg/cmb/sm2_test.go b/internal/pkg/cmb/sm2_test.go index 511443f..6ba2dc8 100644 --- a/internal/pkg/cmb/sm2_test.go +++ b/internal/pkg/cmb/sm2_test.go @@ -186,7 +186,7 @@ func TestVerifyBody(t *testing.T) { } func TestDecryptBody(t *testing.T) { - //priKey := "f6a8d2f412e289686aba6a0f33cad1a64367d0ba012046ee0fbbefd3ffd675bd" + //priKey := "f6a8d2f412e289686aba6a0f33cad1a64367d0ba012046ee0fbbefd3ffd675bd" // 测试 priKey := "8d39ff3d2559258c163f4510f082727f51531e1953ab203d5ab1ea4a6d94fd73" //content := "BAdcIauIjNx3LsrplpJiZoljE4hCiGHra6ulhgG1qL0tKcAeenX+Z9VaHfXLSdkji1fYBpdZiiI35R0vFtnXPXJCJdHsGbfbae+PzNznYQS3KM8/90Y/FIWzSoszfUiF6fAuv8I6v9kQuqHUTidHeHyICDoyvJ0nhbNyUyg85bAKd6TmkVX1MgXLQ81m|5KfR/5UkpVBEQv1dx+iJbojOykNRuDV8Gsy3QOIlRI+cZvafRRPUUG6eeixnPMumhOvyZwsSG/OBeg0U/lSlAepg12tXWcQ601wjgyLaKN1iMvb1DCtfnJFAm8EWAc2SLH3NQuyhxGe/jgCXvj0wGphh4vBUzm8la8i8Aij0BI5lfgU5OzglkKDln6zHN3vBHDqOurEh18eU6z1bfvNnDpzdwEcygcEIH/6lGiqVnGH+C2+QpcKeCnj5qKGFiuSC" From 6a5198287ed0ef4340880a0a9d4de24234854096 Mon Sep 17 00:00:00 2001 From: ziming Date: Mon, 15 Dec 2025 15:15:22 +0800 Subject: [PATCH 048/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 7 ++++--- internal/pkg/cmb/sm2_test.go | 9 +++------ 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 20332ea..9369c7a 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -217,9 +217,10 @@ func (biz *MultiBiz) bizContent(nl *bo.MultiNotifyLogBo, order *bo.OrderBo) (str TransAmount: fmt.Sprintf("%d", nl.ConsumeAmount), OrderId: nl.TransactionID, // 券核销支付单号 Ticket: nl.OrderNo, // 券订单号,lsxd订单号 - OrgNo: biz.bc.Cmb.OrgNo, // cmb固定值 - Attach: order.Attach, // cmb拓展参数 - Ext: "", + //OrgNo: biz.bc.Cmb.OrgNo, // cmb固定值 + OrgNo: "LANSEXIONGDIMULTI", // cmb固定值 + Attach: order.Attach, // cmb拓展参数 + Ext: "", } if nl.Status.IsUsed() { diff --git a/internal/pkg/cmb/sm2_test.go b/internal/pkg/cmb/sm2_test.go index 6ba2dc8..fb166d9 100644 --- a/internal/pkg/cmb/sm2_test.go +++ b/internal/pkg/cmb/sm2_test.go @@ -186,14 +186,11 @@ func TestVerifyBody(t *testing.T) { } func TestDecryptBody(t *testing.T) { - //priKey := "f6a8d2f412e289686aba6a0f33cad1a64367d0ba012046ee0fbbefd3ffd675bd" // 测试 - priKey := "8d39ff3d2559258c163f4510f082727f51531e1953ab203d5ab1ea4a6d94fd73" + priKey := "f6a8d2f412e289686aba6a0f33cad1a64367d0ba012046ee0fbbefd3ffd675bd" // 测试 + //priKey := "8d39ff3d2559258c163f4510f082727f51531e1953ab203d5ab1ea4a6d94fd73" - //content := "BAdcIauIjNx3LsrplpJiZoljE4hCiGHra6ulhgG1qL0tKcAeenX+Z9VaHfXLSdkji1fYBpdZiiI35R0vFtnXPXJCJdHsGbfbae+PzNznYQS3KM8/90Y/FIWzSoszfUiF6fAuv8I6v9kQuqHUTidHeHyICDoyvJ0nhbNyUyg85bAKd6TmkVX1MgXLQ81m|5KfR/5UkpVBEQv1dx+iJbojOykNRuDV8Gsy3QOIlRI+cZvafRRPUUG6eeixnPMumhOvyZwsSG/OBeg0U/lSlAepg12tXWcQ601wjgyLaKN1iMvb1DCtfnJFAm8EWAc2SLH3NQuyhxGe/jgCXvj0wGphh4vBUzm8la8i8Aij0BI5lfgU5OzglkKDln6zHN3vBHDqOurEh18eU6z1bfvNnDpzdwEcygcEIH/6lGiqVnGH+C2+QpcKeCnj5qKGFiuSC" + content := "BH666zulhEdX6Axd3+LUaPo0a6WU1ze2qKFrtu+pxq4EZAy1TPY1NQ0+53+WpscLsoksfjlpCOPVpSLQDKe1IHARFTgJSNgsOfvdBGIEoyDEDdMOc6JzONUuijKsfUTnQI+UsecoJOmRo4xtSloR0sql9FPqhxTn9quMrsJQVL/DNnol+vs8r6RsyLPL|ArPZsEB4BoGL5UX0BTb9vdyOuSt+cMf7msRcEIUUX6O/oOoCdQmxYIVsaLCgq9IkK6mP4O1oAiu6R8gfdsyHgIQSDtKiv7DRAwrI/b4UD4ka+BVVhJXkX0uYlqmA8yCrcmdM2zwp7c/v0qeEr0LnJhrm2tPwAEhLdWEDJskNsH/dQsVkWxf77Vj1OtLQjl+6Ir0RYzdEXlBxNGBv6Y0x2khQcbcTbVfcMFJ3zoGcCEU75UrDLS/ph73t/BM2dICakqL+ymlKiqMm/+K77B4pdYbJ2SpiUdydv4wUzNLPsx2mhuh8KhuyEo1DI8Fzjqd3TiuXiIZISyxaXJ3aV9NdxdYVkZ4j5iLpYdL7GqAxBtdLLK/J6KtqzW+3rwM3L7alxbXHVdcgoxVZanunisaNWPsn4V5BUoL8halT3/smwZM=" //content := "BDUuuPClIlUKDJdpHtNBIU6u5JTetrVG53AfKDSrhah9Q0QZWAj3K5pZF8G/HFtzj/KvbrHfP/gnHri0L7L91HaKYYc1vqy/q8Z69v7MEiIWL2LeLOsc2b0cHmnt7Qey5aZzYVbZJJNhus2gvhahGwOPpPL50JFC8IqAlU4+E/kUBgx+RgzAIkLLs8Se|k1WOt2Eb+xkxSobw/1DaLblguznhnsk1ga87sfqrrPATRuTyszzdVrRtCUuUHRKJ+vQwlZAR1ypkBC1vPMdt8zccI/CeNof+4Ap23enbQwTJQ4KRvij4kbJd6ycY97B+vHnI+oSnwfmjK0EWNUeRrCr2Uau7yxmzlFwJHprbgPZpHVuUjAzMIXNixOfxSTdc9dL+j3/tR7+yookvd9W3hNm96XZFPTheI0FqUKkbKWUUcVs4lC1/yEgjwXFcD4NM" - //content := "BDqANzEC9vq82FTu9wR89Ou/8o2J2vD7yeoExb1Uxl2PSA9EsvI7YQNGSkSdxoloqRBn4vIrA8Uh+FmWtJ9wbFEEtbC+Epj8qHWN8S+lH9JEnvJhPhEtMgmHeuGDtvzHMACvc7KoBmJ/6XZIoEyLeRgkJnCTVVxnD78KMJ4eXzj7C2ErkISJ5r3qcL9t|cPw/uy87PW74aeU1RwGwUsYXZlXYkG0m+BoO/3qT4rLSIWsHvBN76yQJlTk89aa9VALdtwcU7H0q7ivCLZ9uflB6YFkg5QRQoj1b2AFFU4TPkpgAdCBAKs5+6z9cwDn+QQyuamKAnp1wJfMj7Ksa3HxLwCxTh/w2dF6LuruLFWZstMK1g9ID7tVTPqc05VNmktmNhc97BYuIH66xa0ZFgr+0i/3hFMx0hNTOau5UWFM6v4Lb1iM/v9ggM5ZvAheUspfgvRUhpD2TZvgyneWxqg==" - //content := "BOk+pytaf5faPeISKp/rzspV2ngCr/RuHe3lTTGv+0csaKh3y/x/e6njEbpYpQXCcYTI4eN9pjKMyU9ByA1hsfmiCSU4ziUCu5+ltxDFt/FHuhspBwIYRkVCQM9WefydXgGEuXBd0S+yB6BhBMqJgSUuyjxyF5AecdFoB9AoRyslbqSflqn2cctw5pIx|yvTLMRUfjO+9XV4ilxNBMD9jLwA1PYdthta5uhOOSzaJVOdNDhC+aAltyrLrZkBIUOrPZX7U3RtJVLnsk0y+t8/di+XxriZtnZZ0vTJC0q5tAONN9IE7s9clGGclX5dXYGV4jhP70tFzSeTlWsviLAs9sCvsuIloKEGLJdDvJ53vxiYskX3+pOF0uvOkAlJb70pfOHXsIzSAEhFC88O6o9AR4BRa18ex3V+CqN0CkFUqVazvxoyPFlhzxRXZyBkt" - content := "BOk+pytaf5faPeISKp/rzspV2ngCr/RuHe3lTTGv+0csaKh3y/x/e6njEbpYpQXCcYTI4eN9pjKMyU9ByA1hsfmiCSU4ziUCu5+ltxDFt/FHuhspBwIYRkVCQM9WefydXgGEuXBd0S+yB6BhBMqJgSUuyjxyF5AecdFoB9AoRyslbqSflqn2cctw5pIx|yvTLMRUfjO+9XV4ilxNBMD9jLwA1PYdthta5uhOOSzaJVOdNDhC+aAltyrLrZkBIUOrPZX7U3RtJVLnsk0y+t8/di+XxriZtnZZ0vTJC0q5tAONN9IE7s9clGGclX5dXYGV4jhP70tFzSeTlWsviLAs9sCvsuIloKEGLJdDvJ53vxiYskX3+pOF0uvOkAlJb70pfOHXsIzSAEhFC88O6o9AR4BRa18ex3V+CqN0CkFUqVazvxoyPFlhzxRXZyBkt" rs, err := DecryptBody(&Decrypts{content, priKey}) if err != nil { From 0b3a16c72a016de8ba5400a4c1928b933019f583 Mon Sep 17 00:00:00 2001 From: ziming Date: Mon, 15 Dec 2025 15:20:09 +0800 Subject: [PATCH 049/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 9369c7a..12b4e0b 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -215,12 +215,11 @@ func (biz *MultiBiz) bizContent(nl *bo.MultiNotifyLogBo, order *bo.OrderBo) (str Status: "0", // 券状态 0:可使用,1:已使用 TransDate: nl.ConsumeTime.Format("2006-01-02 15:04:05.000"), // 核销时间,验券时间,格式yyyy-mm-dd hh:mm:ss.sss TransAmount: fmt.Sprintf("%d", nl.ConsumeAmount), - OrderId: nl.TransactionID, // 券核销支付单号 - Ticket: nl.OrderNo, // 券订单号,lsxd订单号 - //OrgNo: biz.bc.Cmb.OrgNo, // cmb固定值 - OrgNo: "LANSEXIONGDIMULTI", // cmb固定值 - Attach: order.Attach, // cmb拓展参数 - Ext: "", + OrderId: nl.TransactionID, // 券核销支付单号 + Ticket: nl.OrderNo, // 券订单号,lsxd订单号 + OrgNo: "LANSEXIONGDIMULTI", // cmb固定值 + Attach: order.Attach, // cmb拓展参数 + Ext: "", } if nl.Status.IsUsed() { From f0f4ce123587262389ac2c6790fe0835e7d43310 Mon Sep 17 00:00:00 2001 From: ziming Date: Mon, 15 Dec 2025 15:30:33 +0800 Subject: [PATCH 050/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configs/config.yaml b/configs/config.yaml index d412ef2..6c00b25 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -66,7 +66,7 @@ cmb: cmbKeyAlias: "SM2_CMBLIFE" orgNo: "LANSEXIONGDI" # 发码机构号,固定值,掌上生活优惠券系统提供 notifyUrl: "https://sandbox.cdcc.cmbchina.com/AccessGateway/transIn/updateCodeStatus.json" # 招行测试回调地址 - multiNotifyUrl: "https://sandbox.cdcc.cmbchina.com/AccessGateway/transIn/updateCodeStatus.json" # 招行测试多笔立减金回调地址 + multiNotifyUrl: "https://sandbox.cdcc.cmbchina.com/AccessGateway/transIn/updateCodeStatusForMulti.json" # 招行测试多笔立减金回调地址 noticeStartDays: 7 noticeEndDays: 1 From 24994ec0d7707ccff29e12167b6a7af16b90d7c4 Mon Sep 17 00:00:00 2001 From: ziming Date: Tue, 16 Dec 2025 14:38:30 +0800 Subject: [PATCH 051/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/pkg/helper/utils_test.go | 47 ++----------------------------- 1 file changed, 2 insertions(+), 45 deletions(-) diff --git a/internal/pkg/helper/utils_test.go b/internal/pkg/helper/utils_test.go index 647fe9f..9d198f4 100644 --- a/internal/pkg/helper/utils_test.go +++ b/internal/pkg/helper/utils_test.go @@ -1,11 +1,9 @@ package helper import ( - "encoding/json" "fmt" "testing" "time" - "voucher/internal/biz/bo" ) func TestHashMod(t *testing.T) { @@ -45,37 +43,8 @@ func queryUsed(useNum *int) { } func TestMd5(t *testing.T) { - - jsonStr := `{ - "id": "4ab2699d-e91d-5460-9810-25fd6d4c69a5", - "create_time": "2025-12-08T17:54:24+08:00", - "resource_type": "encrypt-resource", - "event_type": "COUPON.USE", - "summary": "代金券核销通知", - "original_type": "coupon", - "associated_data": "coupon", - "plain_text": { - "stock_creator_mchid": "1652465541", - "stock_id": "21386484", - "coupon_id": "142388354994", - "coupon_name": "银行卡多笔立减", - "description": "", - "status": "SENDED", - "create_time": "2025-12-08T17:50:48+08:00", - "coupon_type": "NORMAL", - "no_cash": false, - "singleitem": false, - "business_type": "", - "consume_information": { - "consume_time": "2025-12-08T17:54:24+08:00", - "consume_mchid": "1274938601", - "transaction_id": "4200002996202512083063051834", - "consume_amount": 16 - } - } -}` - - ciphertext := Md5(jsonStr) + //jsonStr := `{"content":"1","timestamp":1765447477945,"ciphertext":"77CAC2FCFDEBAC6665025A1B81E3BBF9"}` + ciphertext := Md5("1" + "DrY1zLkOH01q0sN66vrmkdpbWsyb41ho") t.Log(ciphertext) } @@ -111,16 +80,4 @@ func TestLength(t *testing.T) { }` s := len(jsonStr) t.Log(s) - - var notify bo.WechatVoucherNotifyBo - err := json.Unmarshal([]byte(jsonStr), ¬ify) - if err != nil { - panic(err) - } - - // 输出验证 - fmt.Println("代金券ID:", notify.PlainText.CouponID) - fmt.Println("核销金额(分):", notify.PlainText.ConsumeInformation.ConsumeAmount) - fmt.Println("核销时间:", notify.PlainText.ConsumeInformation.ConsumeTime) - fmt.Println("核销时间:", notify.PlainText.ConsumeInformation.ConsumeTime.Format(time.DateTime)) } From 76033bc88aa8b067b325192ed541188dd0b4b173 Mon Sep 17 00:00:00 2001 From: ziming Date: Tue, 16 Dec 2025 14:38:49 +0800 Subject: [PATCH 052/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/pkg/helper/utils_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/internal/pkg/helper/utils_test.go b/internal/pkg/helper/utils_test.go index 9d198f4..9051c14 100644 --- a/internal/pkg/helper/utils_test.go +++ b/internal/pkg/helper/utils_test.go @@ -43,8 +43,10 @@ func queryUsed(useNum *int) { } func TestMd5(t *testing.T) { - //jsonStr := `{"content":"1","timestamp":1765447477945,"ciphertext":"77CAC2FCFDEBAC6665025A1B81E3BBF9"}` - ciphertext := Md5("1" + "DrY1zLkOH01q0sN66vrmkdpbWsyb41ho") + + jsonStr := `{"content":"1","timestamp":1765447477945,"ciphertext":"77CAC2FCFDEBAC6665025A1B81E3BBF9"}` + + ciphertext := Md5(jsonStr) t.Log(ciphertext) } @@ -80,4 +82,5 @@ func TestLength(t *testing.T) { }` s := len(jsonStr) t.Log(s) + } From c9c0a6004a31699f4241459a5664fde39f84851e Mon Sep 17 00:00:00 2001 From: ziming Date: Tue, 16 Dec 2025 18:27:53 +0800 Subject: [PATCH 053/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/bo/wechat_notify_bo.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/biz/bo/wechat_notify_bo.go b/internal/biz/bo/wechat_notify_bo.go index 56364d9..e26d5ef 100644 --- a/internal/biz/bo/wechat_notify_bo.go +++ b/internal/biz/bo/wechat_notify_bo.go @@ -24,7 +24,7 @@ type PlainText struct { CouponName string `json:"coupon_name"` Description string `json:"description"` Status vo.WechatVoucherStatus `json:"status"` - CreateTime time.Time `json:"create_time"` + CreateTime time.Time `json:"create_time" validate:"required"` CouponType string `json:"coupon_type"` NoCash bool `json:"no_cash"` Singleitem bool `json:"singleitem"` From d1fb805dfff24d2f11ff850928cc593931cfcf02 Mon Sep 17 00:00:00 2001 From: ziming Date: Wed, 17 Dec 2025 14:44:18 +0800 Subject: [PATCH 054/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 12 ++++++++++++ internal/pkg/helper/utils_test.go | 6 ++++++ 2 files changed, 18 insertions(+) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 12b4e0b..eddf098 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/go-kratos/kratos/v2/log" "gorm.io/gorm" + err2 "voucher/api/err" v1 "voucher/api/v1" "voucher/internal/biz/bo" "voucher/internal/biz/cmb" @@ -61,6 +62,17 @@ func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.Wech return lock.NewMutex(biz.rdb.Rdb, cl.TTL).Lock(ctx, cl.Key, func(ctx context.Context) error { + _, err := biz.ProductRepo.GetByBatchNo(ctx, req.PlainText.StockID) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil + } + if err2.IsDbNotFound(err) { + return nil + } + return err + } + order, err := biz.order(ctx, req) if err != nil { return err diff --git a/internal/pkg/helper/utils_test.go b/internal/pkg/helper/utils_test.go index 9051c14..82d4a7d 100644 --- a/internal/pkg/helper/utils_test.go +++ b/internal/pkg/helper/utils_test.go @@ -1,6 +1,7 @@ package helper import ( + "encoding/base64" "fmt" "testing" "time" @@ -50,6 +51,11 @@ func TestMd5(t *testing.T) { t.Log(ciphertext) } +func Test_DecodeString(t *testing.T) { + wxNotifyData, _ := base64.StdEncoding.DecodeString("eyJzdW1tYXJ5Ijoi5Luj6YeR5Yi45qC46ZSA6YCa55+lIiwiYXNzb2NpYXRlZF9kYXRhIjoiY291cG9uIiwiZXZlbnRfdHlwZSI6IkNPVVBPTi5VU0UiLCJjcmVhdGVfdGltZSI6IjIwMjUtMTItMTdUMTQ6MjQ6MDQrMDg6MDAiLCJwbGFpbl90ZXh0Ijp7InN0b2NrX2NyZWF0b3JfbWNoaWQiOiIxNzE1MzQ5NTc4Iiwic3RvY2tfaWQiOiIyMTM1NTI5OSIsImNvdXBvbl9pZCI6IjE0MzIxODQzNzA3NSIsInNpbmdsZWl0ZW1fZGlzY291bnRfb2ZmIjpudWxsLCJkaXNjb3VudF90byI6bnVsbCwiY291cG9uX25hbWUiOiLpk7booYzljaHlpJrnrJTnq4vlh48iLCJzdGF0dXMiOiJTRU5ERUQiLCJkZXNjcmlwdGlvbiI6IiIsImNyZWF0ZV90aW1lIjoiMjAyNS0xMi0xMlQyMTo0OTozMiswODowMCIsImNvdXBvbl90eXBlIjoiTk9STUFMIiwibm9fY2FzaCI6ZmFsc2UsImF2YWlsYWJsZV9iZWdpbl90aW1lIjoiMjAyNS0xMi0xMlQwMDowMDowMCswODowMCIsImF2YWlsYWJsZV9lbmRfdGltZSI6IjIwMjUtMTItMThUMjM6NTk6NTkrMDg6MDAiLCJzaW5nbGVpdGVtIjpmYWxzZSwibm9ybWFsX2NvdXBvbl9pbmZvcm1hdGlvbiI6eyJjb3Vwb25fYW1vdW50Ijo4NCwidHJhbnNhY3Rpb25fbWluaW11bSI6MX0sImNvbnN1bWVfaW5mb3JtYXRpb24iOnsiY29uc3VtZV90aW1lIjoiMjAyNS0xMi0xN1QxNDoyNDowNCswODowMCIsImNvbnN1bWVfbWNoaWQiOiIxNDA1MzEyNzAyIiwidHJhbnNhY3Rpb25faWQiOiI0MjAwMDAyOTk4MjAyNTEyMTcxMDI0MDI4OTQzIiwiY29uc3VtZV9hbW91bnQiOjE4LCJnb29kc19kZXRhaWwiOm51bGx9fSwicmVzb3VyY2VfdHlwZSI6ImVuY3J5cHQtcmVzb3VyY2UiLCJvcmlnaW5hbF90eXBlIjoiY291cG9uIiwiaWQiOiIyNmZhMGQ2ZC02NjM0LTU5ZGYtOWU0Yi00M2JjYmFiY2I4OGIifQ==") + t.Log(string(wxNotifyData)) +} + func TestLength(t *testing.T) { jsonStr := `{ From 4508dfe00e4a3f0ec338735cacfbf7b76c2191ad Mon Sep 17 00:00:00 2001 From: ziming Date: Wed, 17 Dec 2025 14:59:09 +0800 Subject: [PATCH 055/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index eddf098..64f8b03 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -7,6 +7,7 @@ import ( "fmt" "github.com/go-kratos/kratos/v2/log" "gorm.io/gorm" + "sync" err2 "voucher/api/err" v1 "voucher/api/v1" "voucher/internal/biz/bo" @@ -28,6 +29,9 @@ type MultiBiz struct { MultiNotifyDataRepo repo.MultiNotifyDataRepo MultiNotifyLogRepo repo.MultiNotifyLogRepo CmbMixRepo mixrepos.CmbMixRepo + + mu sync.RWMutex + NonExistentBatchData map[string]bool } func NewMultiBiz( @@ -52,8 +56,29 @@ func NewMultiBiz( } } +func (this *MultiBiz) Get(uid string) bool { + + if _, ok := this.NonExistentBatchData[uid]; ok { + return ok + } + + return false +} + +func (this *MultiBiz) Add(uid string) { + + this.mu.Lock() + defer this.mu.Unlock() + + this.NonExistentBatchData[uid] = true +} + func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.WechatVoucherNotifyBo) error { + if !biz.Get(req.PlainText.StockID) { + return nil + } + cl := vo.MultiNotifyLockKey.BuildCache([]string{ source, req.PlainText.StockID, @@ -65,9 +90,11 @@ func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.Wech _, err := biz.ProductRepo.GetByBatchNo(ctx, req.PlainText.StockID) if err != nil { if errors.Is(err, gorm.ErrRecordNotFound) { + biz.Add(req.PlainText.StockID) return nil } if err2.IsDbNotFound(err) { + biz.Add(req.PlainText.StockID) return nil } return err From 96325fde3b8f89458ccf9fae31a262847707817d Mon Sep 17 00:00:00 2001 From: ziming Date: Wed, 17 Dec 2025 15:02:18 +0800 Subject: [PATCH 056/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 64f8b03..cad5537 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -53,6 +53,8 @@ func NewMultiBiz( MultiNotifyDataRepo: multiNotifyDataRepo, MultiNotifyLogRepo: multiNotifyLogRepo, CmbMixRepo: cmbMixRepo, + + NonExistentBatchData: make(map[string]bool), } } From 1f81565e151e66cb0c92db36edb2577f8f67ca57 Mon Sep 17 00:00:00 2001 From: ziming Date: Wed, 17 Dec 2025 15:04:59 +0800 Subject: [PATCH 057/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index cad5537..bf0dedf 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -310,8 +310,8 @@ func (biz *MultiBiz) Request(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl reply, err := biz.CmbMixRepo.Request(ctx, request, nl.RequestURL) if err != nil { - if err2 := biz.notifyFail(ctx, nl, err.Error()); err2 != nil { - return err2 + if err3 := biz.notifyFail(ctx, nl, err.Error()); err3 != nil { + return err3 } return err } @@ -320,8 +320,8 @@ func (biz *MultiBiz) Request(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl errMsg := fmt.Sprintf("回调通知招行返回验证结果发生错误,resp:%+v error:%s", reply, err.Error()) - if err2 := biz.notifyFail(ctx, nl, errMsg); err2 != nil { - return err2 + if err3 := biz.notifyFail(ctx, nl, errMsg); err3 != nil { + return err3 } return err } From c65c993a25fc5486c89e4e764eb4a36bed973962 Mon Sep 17 00:00:00 2001 From: ziming Date: Wed, 17 Dec 2025 15:21:49 +0800 Subject: [PATCH 058/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/bo/wechat_notify_bo.go | 2 +- internal/biz/multi.go | 13 ++++++++++++- internal/pkg/helper/utils_test.go | 2 +- 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/internal/biz/bo/wechat_notify_bo.go b/internal/biz/bo/wechat_notify_bo.go index e26d5ef..26c4007 100644 --- a/internal/biz/bo/wechat_notify_bo.go +++ b/internal/biz/bo/wechat_notify_bo.go @@ -13,7 +13,7 @@ type ConsumeInformation struct { ConsumeTime time.Time `json:"consume_time" validate:"required"` // 核销时间 ConsumeMchid string `json:"consume_mchid"` // 核销商户号 TransactionID string `json:"transaction_id" validate:"required"` // 微信支付交易单号 - ConsumeAmount int `json:"consume_amount" validate:"required"` // 核销金额(单位:分) + ConsumeAmount int `json:"consume_amount"` // 核销金额(单位:分) // 多笔立减金必须 validate:"required" } // PlainText 定义明文数据结构体 diff --git a/internal/biz/multi.go b/internal/biz/multi.go index bf0dedf..224f6e5 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -89,8 +89,9 @@ func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.Wech return lock.NewMutex(biz.rdb.Rdb, cl.TTL).Lock(ctx, cl.Key, func(ctx context.Context) error { - _, err := biz.ProductRepo.GetByBatchNo(ctx, req.PlainText.StockID) + product, err := biz.ProductRepo.GetByBatchNo(ctx, req.PlainText.StockID) if err != nil { + // 数据库不存在该活动批次,过滤掉 if errors.Is(err, gorm.ErrRecordNotFound) { biz.Add(req.PlainText.StockID) return nil @@ -102,6 +103,16 @@ func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.Wech return err } + // 不是多笔立减金,过滤掉 + if product.ActivityId == "" { + biz.Add(req.PlainText.StockID) + return nil + } + + if req.PlainText.ConsumeInformation.ConsumeAmount == 0 { + return fmt.Errorf("消费金额不能为0") + } + order, err := biz.order(ctx, req) if err != nil { return err diff --git a/internal/pkg/helper/utils_test.go b/internal/pkg/helper/utils_test.go index 82d4a7d..b46d992 100644 --- a/internal/pkg/helper/utils_test.go +++ b/internal/pkg/helper/utils_test.go @@ -52,7 +52,7 @@ func TestMd5(t *testing.T) { } func Test_DecodeString(t *testing.T) { - wxNotifyData, _ := base64.StdEncoding.DecodeString("eyJzdW1tYXJ5Ijoi5Luj6YeR5Yi45qC46ZSA6YCa55+lIiwiYXNzb2NpYXRlZF9kYXRhIjoiY291cG9uIiwiZXZlbnRfdHlwZSI6IkNPVVBPTi5VU0UiLCJjcmVhdGVfdGltZSI6IjIwMjUtMTItMTdUMTQ6MjQ6MDQrMDg6MDAiLCJwbGFpbl90ZXh0Ijp7InN0b2NrX2NyZWF0b3JfbWNoaWQiOiIxNzE1MzQ5NTc4Iiwic3RvY2tfaWQiOiIyMTM1NTI5OSIsImNvdXBvbl9pZCI6IjE0MzIxODQzNzA3NSIsInNpbmdsZWl0ZW1fZGlzY291bnRfb2ZmIjpudWxsLCJkaXNjb3VudF90byI6bnVsbCwiY291cG9uX25hbWUiOiLpk7booYzljaHlpJrnrJTnq4vlh48iLCJzdGF0dXMiOiJTRU5ERUQiLCJkZXNjcmlwdGlvbiI6IiIsImNyZWF0ZV90aW1lIjoiMjAyNS0xMi0xMlQyMTo0OTozMiswODowMCIsImNvdXBvbl90eXBlIjoiTk9STUFMIiwibm9fY2FzaCI6ZmFsc2UsImF2YWlsYWJsZV9iZWdpbl90aW1lIjoiMjAyNS0xMi0xMlQwMDowMDowMCswODowMCIsImF2YWlsYWJsZV9lbmRfdGltZSI6IjIwMjUtMTItMThUMjM6NTk6NTkrMDg6MDAiLCJzaW5nbGVpdGVtIjpmYWxzZSwibm9ybWFsX2NvdXBvbl9pbmZvcm1hdGlvbiI6eyJjb3Vwb25fYW1vdW50Ijo4NCwidHJhbnNhY3Rpb25fbWluaW11bSI6MX0sImNvbnN1bWVfaW5mb3JtYXRpb24iOnsiY29uc3VtZV90aW1lIjoiMjAyNS0xMi0xN1QxNDoyNDowNCswODowMCIsImNvbnN1bWVfbWNoaWQiOiIxNDA1MzEyNzAyIiwidHJhbnNhY3Rpb25faWQiOiI0MjAwMDAyOTk4MjAyNTEyMTcxMDI0MDI4OTQzIiwiY29uc3VtZV9hbW91bnQiOjE4LCJnb29kc19kZXRhaWwiOm51bGx9fSwicmVzb3VyY2VfdHlwZSI6ImVuY3J5cHQtcmVzb3VyY2UiLCJvcmlnaW5hbF90eXBlIjoiY291cG9uIiwiaWQiOiIyNmZhMGQ2ZC02NjM0LTU5ZGYtOWU0Yi00M2JjYmFiY2I4OGIifQ==") + wxNotifyData, _ := base64.StdEncoding.DecodeString("eyJzdW1tYXJ5Ijoi5Luj6YeR5Yi45qC46ZSA6YCa55+lIiwiYXNzb2NpYXRlZF9kYXRhIjoiY291cG9uIiwiZXZlbnRfdHlwZSI6IkNPVVBPTi5VU0UiLCJjcmVhdGVfdGltZSI6IjIwMjUtMTItMTdUMTU6MDc6MDkrMDg6MDAiLCJwbGFpbl90ZXh0Ijp7InN0b2NrX2NyZWF0b3JfbWNoaWQiOiIxNzE1MzQ5NTc4Iiwic3RvY2tfaWQiOiIyMTM0NjE1OSIsImNvdXBvbl9pZCI6IjE0NDI4NzM1NDEyNCIsInNpbmdsZWl0ZW1fZGlzY291bnRfb2ZmIjpudWxsLCJkaXNjb3VudF90byI6bnVsbCwiY291cG9uX25hbWUiOiLkvJfpgqbpk7booYwzMDDlhYNMUyIsInN0YXR1cyI6IlVTRUQiLCJkZXNjcmlwdGlvbiI6IiIsImNyZWF0ZV90aW1lIjoiMjAyNS0xMi0xN1QxNTowNjo0MCswODowMCIsImNvdXBvbl90eXBlIjoiTk9STUFMIiwibm9fY2FzaCI6ZmFsc2UsImF2YWlsYWJsZV9iZWdpbl90aW1lIjoiMjAyNS0xMi0xN1QwMDowMDowMCswODowMCIsImF2YWlsYWJsZV9lbmRfdGltZSI6IjIwMjYtMDItMjZUMjM6NTk6NTkrMDg6MDAiLCJzaW5nbGVpdGVtIjpmYWxzZSwibm9ybWFsX2NvdXBvbl9pbmZvcm1hdGlvbiI6eyJjb3Vwb25fYW1vdW50IjozMDAwMCwidHJhbnNhY3Rpb25fbWluaW11bSI6MzAwMDF9LCJjb25zdW1lX2luZm9ybWF0aW9uIjp7ImNvbnN1bWVfdGltZSI6IjIwMjUtMTItMTdUMTU6MDc6MDkrMDg6MDAiLCJjb25zdW1lX21jaGlkIjoiMzQwNzU3MTI3MCIsInRyYW5zYWN0aW9uX2lkIjoiNDIwMDAwMjkwMTIwMjUxMjE3ODEzODYyODM4MiIsImNvbnN1bWVfYW1vdW50IjpudWxsLCJnb29kc19kZXRhaWwiOm51bGx9fSwicmVzb3VyY2VfdHlwZSI6ImVuY3J5cHQtcmVzb3VyY2UiLCJvcmlnaW5hbF90eXBlIjoiY291cG9uIiwiaWQiOiJiYTZkYWNiNi01MDllLTU1ZTMtOGYwMi1iM2YwNDJkZWViNTIifQ==") t.Log(string(wxNotifyData)) } From 06476209b50d6a645e083c4d8c5e4f45ac38e86f Mon Sep 17 00:00:00 2001 From: ziming Date: Wed, 17 Dec 2025 15:36:39 +0800 Subject: [PATCH 059/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 2 +- internal/pkg/helper/utils_test.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 224f6e5..1738dce 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -77,7 +77,7 @@ func (this *MultiBiz) Add(uid string) { func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.WechatVoucherNotifyBo) error { - if !biz.Get(req.PlainText.StockID) { + if biz.Get(req.PlainText.StockID) { return nil } diff --git a/internal/pkg/helper/utils_test.go b/internal/pkg/helper/utils_test.go index b46d992..2e963ab 100644 --- a/internal/pkg/helper/utils_test.go +++ b/internal/pkg/helper/utils_test.go @@ -52,7 +52,7 @@ func TestMd5(t *testing.T) { } func Test_DecodeString(t *testing.T) { - wxNotifyData, _ := base64.StdEncoding.DecodeString("eyJzdW1tYXJ5Ijoi5Luj6YeR5Yi45qC46ZSA6YCa55+lIiwiYXNzb2NpYXRlZF9kYXRhIjoiY291cG9uIiwiZXZlbnRfdHlwZSI6IkNPVVBPTi5VU0UiLCJjcmVhdGVfdGltZSI6IjIwMjUtMTItMTdUMTU6MDc6MDkrMDg6MDAiLCJwbGFpbl90ZXh0Ijp7InN0b2NrX2NyZWF0b3JfbWNoaWQiOiIxNzE1MzQ5NTc4Iiwic3RvY2tfaWQiOiIyMTM0NjE1OSIsImNvdXBvbl9pZCI6IjE0NDI4NzM1NDEyNCIsInNpbmdsZWl0ZW1fZGlzY291bnRfb2ZmIjpudWxsLCJkaXNjb3VudF90byI6bnVsbCwiY291cG9uX25hbWUiOiLkvJfpgqbpk7booYwzMDDlhYNMUyIsInN0YXR1cyI6IlVTRUQiLCJkZXNjcmlwdGlvbiI6IiIsImNyZWF0ZV90aW1lIjoiMjAyNS0xMi0xN1QxNTowNjo0MCswODowMCIsImNvdXBvbl90eXBlIjoiTk9STUFMIiwibm9fY2FzaCI6ZmFsc2UsImF2YWlsYWJsZV9iZWdpbl90aW1lIjoiMjAyNS0xMi0xN1QwMDowMDowMCswODowMCIsImF2YWlsYWJsZV9lbmRfdGltZSI6IjIwMjYtMDItMjZUMjM6NTk6NTkrMDg6MDAiLCJzaW5nbGVpdGVtIjpmYWxzZSwibm9ybWFsX2NvdXBvbl9pbmZvcm1hdGlvbiI6eyJjb3Vwb25fYW1vdW50IjozMDAwMCwidHJhbnNhY3Rpb25fbWluaW11bSI6MzAwMDF9LCJjb25zdW1lX2luZm9ybWF0aW9uIjp7ImNvbnN1bWVfdGltZSI6IjIwMjUtMTItMTdUMTU6MDc6MDkrMDg6MDAiLCJjb25zdW1lX21jaGlkIjoiMzQwNzU3MTI3MCIsInRyYW5zYWN0aW9uX2lkIjoiNDIwMDAwMjkwMTIwMjUxMjE3ODEzODYyODM4MiIsImNvbnN1bWVfYW1vdW50IjpudWxsLCJnb29kc19kZXRhaWwiOm51bGx9fSwicmVzb3VyY2VfdHlwZSI6ImVuY3J5cHQtcmVzb3VyY2UiLCJvcmlnaW5hbF90eXBlIjoiY291cG9uIiwiaWQiOiJiYTZkYWNiNi01MDllLTU1ZTMtOGYwMi1iM2YwNDJkZWViNTIifQ==") + wxNotifyData, _ := base64.StdEncoding.DecodeString("ewogICJpZCI6ICI0YWIyNjk5ZC1lOTFkLTU0NjAtOTgxMC0yNWZkNmQ0YzY5YTUiLAogICJjcmVhdGVfdGltZSI6ICIyMDI1LTEyLTA4VDE3OjU0OjI0KzA4OjAwIiwKICAicmVzb3VyY2VfdHlwZSI6ICJlbmNyeXB0LXJlc291cmNlIiwKICAiZXZlbnRfdHlwZSI6ICJDT1VQT04uVVNFIiwKICAic3VtbWFyeSI6ICLku6Pph5HliLjmoLjplIDpgJrnn6UiLAogICJvcmlnaW5hbF90eXBlIjogImNvdXBvbiIsCiAgImFzc29jaWF0ZWRfZGF0YSI6ICJjb3Vwb24iLAogICJwbGFpbl90ZXh0IjogewogICAgInN0b2NrX2NyZWF0b3JfbWNoaWQiOiAiMTY1MjQ2NTU0MSIsCiAgICAic3RvY2tfaWQiOiAiMjEzODY0ODQiLAogICAgImNvdXBvbl9pZCI6ICIxNDIzODgzNTQ5OTQiLAogICAgImNvdXBvbl9uYW1lIjogIumTtuihjOWNoeWkmueslOeri+WHjyIsCiAgICAiZGVzY3JpcHRpb24iOiAiIiwKICAgICJzdGF0dXMiOiAiU0VOREVEIiwKICAgICJjcmVhdGVfdGltZSI6ICIyMDI1LTEyLTA4VDE3OjUwOjQ4KzA4OjAwIiwKICAgICJjb3Vwb25fdHlwZSI6ICJOT1JNQUwiLAogICAgIm5vX2Nhc2giOiBmYWxzZSwKICAgICJzaW5nbGVpdGVtIjogZmFsc2UsCiAgICAiYnVzaW5lc3NfdHlwZSI6ICIiLAogICAgImNvbnN1bWVfaW5mb3JtYXRpb24iOiB7CiAgICAgICJjb25zdW1lX3RpbWUiOiAiMjAyNS0xMi0wOFQxNzo1NDoyNCswODowMCIsCiAgICAgICJjb25zdW1lX21jaGlkIjogIjEyNzQ5Mzg2MDEiLAogICAgICAidHJhbnNhY3Rpb25faWQiOiAiNDIwMDAwMjk5NjIwMjUxMjA4MzA2MzA1MTgzNCIsCiAgICAgICJjb25zdW1lX2Ftb3VudCI6IDE2CiAgICB9CiAgfQp9") t.Log(string(wxNotifyData)) } From c3d5f18e68517c4d03b7e39fa8ec82b637b8548e Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 18 Dec 2025 14:51:01 +0800 Subject: [PATCH 060/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/data/repoimpl/multi_notify_log.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/data/repoimpl/multi_notify_log.go b/internal/data/repoimpl/multi_notify_log.go index a1d08ea..78e5dcf 100644 --- a/internal/data/repoimpl/multi_notify_log.go +++ b/internal/data/repoimpl/multi_notify_log.go @@ -115,7 +115,7 @@ func (p *MultiNotifyLogRepoImpl) Fail(ctx context.Context, id int64, remark stri RequestStatus: vo.MultiNotifyLogStatusWait.GetValue(), }). Updates(model.MultiNotifyLog{ - RequestStatus: vo.MultiNotifyLogStatusSuccess.GetValue(), + RequestStatus: vo.MultiNotifyLogStatusFail.GetValue(), Response: remark, UpdateTime: &now, }) From c490f8335dc1035706506be710253c19f0f4e224 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 18 Dec 2025 15:05:57 +0800 Subject: [PATCH 061/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 9 +++++---- test/bank_multi_activity_test.go | 29 +---------------------------- 2 files changed, 6 insertions(+), 32 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 1738dce..34d86aa 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -240,10 +240,11 @@ func (biz *MultiBiz) nlCreate(ctx context.Context, req *bo.WechatVoucherNotifyBo ConsumeAmount: mnd.ConsumeAmount, ConsumeTime: mnd.ConsumeTime, TransactionID: req.PlainText.ConsumeInformation.TransactionID, - RequestURL: order.NotifyUrl, - RequestStatus: vo.MultiNotifyLogStatusWait.GetValue(), - OrderCreateTime: order.CreateTime, - CouponCreateTime: &req.PlainText.CreateTime, + //RequestURL: order.NotifyUrl, + RequestURL: biz.bc.Cmb.MultiNotifyUrl, + RequestStatus: vo.MultiNotifyLogStatusWait.GetValue(), + OrderCreateTime: order.CreateTime, + CouponCreateTime: &req.PlainText.CreateTime, } request, err := biz.GetRequest(ctx, nl, order) diff --git a/test/bank_multi_activity_test.go b/test/bank_multi_activity_test.go index 2e3f446..1e61637 100644 --- a/test/bank_multi_activity_test.go +++ b/test/bank_multi_activity_test.go @@ -57,34 +57,7 @@ func Test_MarketingQuery(t *testing.T) { func Test_QixingNotifyData(t *testing.T) { - wxBody := `{ - "id": "4ab2699d-e91d-5460-9810-25fd6d4c69a5", - "create_time": "2025-12-08T17:54:24+08:00", - "resource_type": "encrypt-resource", - "event_type": "COUPON.USE", - "summary": "代金券核销通知", - "original_type": "coupon", - "associated_data": "coupon", - "plain_text": { - "stock_creator_mchid": "1652465541", - "stock_id": "21386484", - "coupon_id": "142388354994", - "coupon_name": "银行卡多笔立减", - "description": "", - "status": "SENDED", - "create_time": "2025-12-08T17:50:48+08:00", - "coupon_type": "NORMAL", - "no_cash": false, - "singleitem": false, - "business_type": "", - "consume_information": { - "consume_time": "2025-12-08T17:54:24+08:00", - "consume_mchid": "1274938601", - "transaction_id": "4200002996202512083063051834", - "consume_amount": 16 - } - } -}` + wxBody := `{"id":"4ab2699d-e91d-5460-9810-25fd6d4c69a5","create_time":"2025-12-08T17:54:24+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","original_type":"coupon","associated_data":"coupon","plain_text":{"stock_creator_mchid":"1652465541","stock_id":"21386484","coupon_id":"142388354994","coupon_name":"银行卡多笔立减","description":"","status":"SENDED","create_time":"2025-12-08T17:50:48+08:00","coupon_type":"NORMAL","no_cash":false,"singleitem":false,"business_type":"","consume_information":{"consume_time":"2025-12-08T17:54:24+08:00","consume_mchid":"1274938601","transaction_id":"4200002996202512083063051834","consume_amount":16}}}` content := base64.StdEncoding.EncodeToString([]byte(wxBody)) ciphertext := helper.Md5(content + "DrY1zLkOH01q0sN66vrmkdpbWsyb41ho") From 9ed8a296cf44fb3180c671e44c8e534164862071 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 18 Dec 2025 15:08:01 +0800 Subject: [PATCH 062/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 34d86aa..7b8c45b 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -228,6 +228,10 @@ func (biz *MultiBiz) mndCreate(ctx context.Context, ip, source string, req *bo.W func (biz *MultiBiz) nlCreate(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) (*bo.MultiNotifyLogBo, error) { + if biz.bc.Cmb.MultiNotifyUrl == "" { + return nil, fmt.Errorf("CMB多笔立减金通知地址为空") + } + nl := &bo.MultiNotifyLogBo{ MultiNotifyDataID: mnd.ID, OrderNo: mnd.OrderNo, From 90a2f6fb32b8226dc89032cb0d1e73fc84cd4272 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 18 Dec 2025 15:15:18 +0800 Subject: [PATCH 063/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/bank_multi_activity_test.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/bank_multi_activity_test.go b/test/bank_multi_activity_test.go index 1e61637..dfe0bad 100644 --- a/test/bank_multi_activity_test.go +++ b/test/bank_multi_activity_test.go @@ -58,6 +58,8 @@ func Test_MarketingQuery(t *testing.T) { func Test_QixingNotifyData(t *testing.T) { wxBody := `{"id":"4ab2699d-e91d-5460-9810-25fd6d4c69a5","create_time":"2025-12-08T17:54:24+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","original_type":"coupon","associated_data":"coupon","plain_text":{"stock_creator_mchid":"1652465541","stock_id":"21386484","coupon_id":"142388354994","coupon_name":"银行卡多笔立减","description":"","status":"SENDED","create_time":"2025-12-08T17:50:48+08:00","coupon_type":"NORMAL","no_cash":false,"singleitem":false,"business_type":"","consume_information":{"consume_time":"2025-12-08T17:54:24+08:00","consume_mchid":"1274938601","transaction_id":"4200002996202512083063051834","consume_amount":16}}}` + t.Log(len(wxBody)) + content := base64.StdEncoding.EncodeToString([]byte(wxBody)) ciphertext := helper.Md5(content + "DrY1zLkOH01q0sN66vrmkdpbWsyb41ho") From a7a2fb88edb153c746249458427a91c4506d40f8 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 19 Dec 2025 14:10:09 +0800 Subject: [PATCH 064/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 22 ++++++++++++++-------- internal/biz/repo/order.go | 4 ++-- internal/data/repoimpl/order.go | 6 +++--- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 7b8c45b..45d5f1e 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -186,12 +186,18 @@ func (biz *MultiBiz) RetryRunByMultiNotifyDataId(ctx context.Context, multiNotif func (biz *MultiBiz) run(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) error { if req.PlainText.Status.IsUsed() { - if err := biz.OrderRepo.OverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime); err != nil { - return fmt.Errorf("订单使用完成修改发生错误 error: %v", err) + if order.Status.IsUse() { + if err := biz.OrderRepo.MultiOverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime, "再次核销完成"); err != nil { + return fmt.Errorf("订单再次核销完成修改发生错误 error: %v", err) + } + } else { + if err := biz.OrderRepo.MultiOverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime, "核销完成"); err != nil { + return fmt.Errorf("订单核销完成修改发生错误 error: %v", err) + } } } else { - if err := biz.OrderRepo.LastUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime); err != nil { - return fmt.Errorf("订单使用修改发生错误 error: %v", err) + if err := biz.OrderRepo.MultiLastUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime); err != nil { + return fmt.Errorf("订单核销修改发生错误 error: %v", err) } } @@ -269,7 +275,7 @@ func (biz *MultiBiz) bizContent(nl *bo.MultiNotifyLogBo, order *bo.OrderBo) (str ActivityId: nl.ActivityNo, // 批次活动号 CouponId: nl.CouponID, // 微信券券号 AcquiredDate: order.ReceiveSuccessTime.Format("2006-01-02 15:04:05.000"), // 券领取时间 - Status: "0", // 券状态 0:可使用,1:已使用 + Status: "1", // 券状态 0:可使用,1:已使用 TransDate: nl.ConsumeTime.Format("2006-01-02 15:04:05.000"), // 核销时间,验券时间,格式yyyy-mm-dd hh:mm:ss.sss TransAmount: fmt.Sprintf("%d", nl.ConsumeAmount), OrderId: nl.TransactionID, // 券核销支付单号 @@ -279,9 +285,9 @@ func (biz *MultiBiz) bizContent(nl *bo.MultiNotifyLogBo, order *bo.OrderBo) (str Ext: "", } - if nl.Status.IsUsed() { - req.Status = "1" - } + //if nl.Status.IsUsed() { + // req.Status = "1" + //} bizJsonBytes, err := json.Marshal(req) if err != nil { diff --git a/internal/biz/repo/order.go b/internal/biz/repo/order.go index 08f920e..b0e26f4 100644 --- a/internal/biz/repo/order.go +++ b/internal/biz/repo/order.go @@ -26,8 +26,8 @@ type OrderRepo interface { Fail(ctx context.Context, id uint64, remark string) error Used(ctx context.Context, id uint64) error NotifyUsed(ctx context.Context, id uint64, transactionId string) error - LastUsed(ctx context.Context, id uint64, lastUseTime time.Time) error - OverUsed(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 Available(ctx context.Context, id uint64) error Expired(ctx context.Context, id uint64) error } diff --git a/internal/data/repoimpl/order.go b/internal/data/repoimpl/order.go index e1d53c6..0ba1690 100644 --- a/internal/data/repoimpl/order.go +++ b/internal/data/repoimpl/order.go @@ -453,7 +453,7 @@ func (p *OrderRepoImpl) Used(ctx context.Context, id uint64) error { return nil } -func (p *OrderRepoImpl) LastUsed(ctx context.Context, id uint64, lastUseTime time.Time) error { +func (p *OrderRepoImpl) MultiLastUsed(ctx context.Context, id uint64, lastUseTime time.Time) error { now := time.Now() tx := p.DB(ctx). @@ -473,7 +473,7 @@ func (p *OrderRepoImpl) LastUsed(ctx context.Context, id uint64, lastUseTime tim return nil } -func (p *OrderRepoImpl) OverUsed(ctx context.Context, id uint64, lastUseTime time.Time) error { +func (p *OrderRepoImpl) MultiOverUsed(ctx context.Context, id uint64, lastUseTime time.Time, remark string) error { now := time.Now() tx := p.DB(ctx). @@ -482,7 +482,7 @@ func (p *OrderRepoImpl) OverUsed(ctx context.Context, id uint64, lastUseTime tim }). Updates(model.Order{ Status: vo.OrderStatusUse.GetValue(), - Remark: "核销完成", + Remark: remark, LastUseTime: &lastUseTime, UpdateTime: &now, }) From b841ad3661f65811c9c9935ddbfb46b859ebbfce Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 19 Dec 2025 14:27:45 +0800 Subject: [PATCH 065/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 45d5f1e..65d57fc 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -7,6 +7,8 @@ import ( "fmt" "github.com/go-kratos/kratos/v2/log" "gorm.io/gorm" + "strconv" + "strings" "sync" err2 "voucher/api/err" v1 "voucher/api/v1" @@ -186,12 +188,29 @@ func (biz *MultiBiz) RetryRunByMultiNotifyDataId(ctx context.Context, multiNotif func (biz *MultiBiz) run(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) error { if req.PlainText.Status.IsUsed() { + if order.Status.IsUse() { - if err := biz.OrderRepo.MultiOverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime, "再次核销完成"); err != nil { + + parts := strings.Split(order.Remark, "_") + l := len(parts) + + remark := "核销完成_" + if l == 1 { + remark = "核销完成_2" + } + if l == 2 { + i, err := strconv.Atoi(parts[1]) + if err != nil { + return fmt.Errorf("订单再次核销完成修改发生错误 error: %v", err) + } + remark = fmt.Sprintf("核销完成_%d", i+1) + } + + if err := biz.OrderRepo.MultiOverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime, remark); err != nil { return fmt.Errorf("订单再次核销完成修改发生错误 error: %v", err) } } else { - if err := biz.OrderRepo.MultiOverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime, "核销完成"); err != nil { + if err := biz.OrderRepo.MultiOverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime, "核销完成_1"); err != nil { return fmt.Errorf("订单核销完成修改发生错误 error: %v", err) } } From 05ba50661c9cdd762d227b83094a68bc2348caa9 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 19 Dec 2025 14:38:14 +0800 Subject: [PATCH 066/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 59 +++++++++++++-------------------- internal/data/repoimpl/order.go | 1 + 2 files changed, 24 insertions(+), 36 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 65d57fc..0189837 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -7,8 +7,6 @@ import ( "fmt" "github.com/go-kratos/kratos/v2/log" "gorm.io/gorm" - "strconv" - "strings" "sync" err2 "voucher/api/err" v1 "voucher/api/v1" @@ -187,45 +185,34 @@ func (biz *MultiBiz) RetryRunByMultiNotifyDataId(ctx context.Context, multiNotif func (biz *MultiBiz) run(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) error { - if req.PlainText.Status.IsUsed() { - - if order.Status.IsUse() { - - parts := strings.Split(order.Remark, "_") - l := len(parts) - - remark := "核销完成_" - if l == 1 { - remark = "核销完成_2" - } - if l == 2 { - i, err := strconv.Atoi(parts[1]) - if err != nil { - return fmt.Errorf("订单再次核销完成修改发生错误 error: %v", err) - } - remark = fmt.Sprintf("核销完成_%d", i+1) - } - - if err := biz.OrderRepo.MultiOverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime, remark); err != nil { - return fmt.Errorf("订单再次核销完成修改发生错误 error: %v", err) - } - } else { - if err := biz.OrderRepo.MultiOverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime, "核销完成_1"); err != nil { - return fmt.Errorf("订单核销完成修改发生错误 error: %v", err) - } - } - } else { - if err := biz.OrderRepo.MultiLastUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime); err != nil { - return fmt.Errorf("订单核销修改发生错误 error: %v", err) - } - } - nl, err := biz.nlCreate(ctx, req, mnd, order) if err != nil { return fmt.Errorf("创建通知日志错误 error: %v", err) } - return biz.Request(ctx, mnd, nl, order) + if err = biz.Request(ctx, mnd, nl, order); err != nil { + return fmt.Errorf("请求错误 error: %v", err) + } + + if req.PlainText.Status.IsUsed() { + + if order.Status.IsUse() { + if err = biz.OrderRepo.MultiOverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime, "再次核销完成"); err != nil { + return fmt.Errorf("订单再次核销完成修改发生错误 error: %v", err) + } + } else { + if err = biz.OrderRepo.MultiOverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime, "核销完成"); err != nil { + return fmt.Errorf("订单核销完成修改发生错误 error: %v", err) + } + } + + } else { + if err = biz.OrderRepo.MultiLastUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime); err != nil { + return fmt.Errorf("订单核销修改发生错误 error: %v", err) + } + } + + return nil } func (biz *MultiBiz) mndCreate(ctx context.Context, ip, source string, req *bo.WechatVoucherNotifyBo, order *bo.OrderBo) (*bo.MultiNotifyDataBo, error) { diff --git a/internal/data/repoimpl/order.go b/internal/data/repoimpl/order.go index 0ba1690..8387cf2 100644 --- a/internal/data/repoimpl/order.go +++ b/internal/data/repoimpl/order.go @@ -462,6 +462,7 @@ func (p *OrderRepoImpl) MultiLastUsed(ctx context.Context, id uint64, lastUseTim }). Updates(model.Order{ Remark: "核销", + Status: vo.OrderStatusSuccess.GetValue(), LastUseTime: &lastUseTime, UpdateTime: &now, }) From 600644595807cb6ab5e19d14e499270589bda9f1 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 19 Dec 2025 15:01:31 +0800 Subject: [PATCH 067/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/v1/cmb_cpn.proto | 2 ++ internal/biz/multi.go | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/api/v1/cmb_cpn.proto b/api/v1/cmb_cpn.proto index db0e675..444411f 100644 --- a/api/v1/cmb_cpn.proto +++ b/api/v1/cmb_cpn.proto @@ -229,6 +229,8 @@ message CmbMultiNotifyRequest { string acquiredDate = 4 [json_name = "acquiredDate"]; // 券状态0:可使用,1:已使用 string status = 5 [json_name = "status"]; + // String|M 整张券总状态0:可使用,1:已使用 + string couponStatus = 13 [json_name = "couponStatus"]; // 券核销时间 格式:yyyy-MM-dd HH:mm:ss.sss string transDate = 6 [json_name = "transDate"]; // 券核销金额,单位-分 diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 0189837..3772de2 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -282,6 +282,7 @@ func (biz *MultiBiz) bizContent(nl *bo.MultiNotifyLogBo, order *bo.OrderBo) (str CouponId: nl.CouponID, // 微信券券号 AcquiredDate: order.ReceiveSuccessTime.Format("2006-01-02 15:04:05.000"), // 券领取时间 Status: "1", // 券状态 0:可使用,1:已使用 + CouponStatus: "0", // 0:可使用,1:已使用 TransDate: nl.ConsumeTime.Format("2006-01-02 15:04:05.000"), // 核销时间,验券时间,格式yyyy-mm-dd hh:mm:ss.sss TransAmount: fmt.Sprintf("%d", nl.ConsumeAmount), OrderId: nl.TransactionID, // 券核销支付单号 @@ -291,9 +292,9 @@ func (biz *MultiBiz) bizContent(nl *bo.MultiNotifyLogBo, order *bo.OrderBo) (str Ext: "", } - //if nl.Status.IsUsed() { - // req.Status = "1" - //} + if nl.Status.IsUsed() { + req.CouponStatus = "1" + } bizJsonBytes, err := json.Marshal(req) if err != nil { From ecb9654743d93374f051e558da6e9c39e2f9fd9e Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 19 Dec 2025 15:23:54 +0800 Subject: [PATCH 068/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/bo/multi_notify_data_bo.go | 6 +++++- internal/biz/multi.go | 1 + internal/data/model/multi_notify_data.gen.go | 1 + test/bank_multi_activity_test.go | 3 ++- 4 files changed, 9 insertions(+), 2 deletions(-) diff --git a/internal/biz/bo/multi_notify_data_bo.go b/internal/biz/bo/multi_notify_data_bo.go index d558e4e..36c5f25 100644 --- a/internal/biz/bo/multi_notify_data_bo.go +++ b/internal/biz/bo/multi_notify_data_bo.go @@ -1,6 +1,9 @@ package bo -import "time" +import ( + "time" + "voucher/internal/biz/vo" +) // MultiNotifyDataBo 领域实体Bo结构,字段和模型字段保持一致 type MultiNotifyDataBo struct { @@ -16,6 +19,7 @@ type MultiNotifyDataBo struct { ConsumeTime *time.Time TransactionID string EventType string + Status vo.WechatVoucherStatus OriginalData string NoticeNum int32 CreateTime *time.Time diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 3772de2..21d7611 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -234,6 +234,7 @@ func (biz *MultiBiz) mndCreate(ctx context.Context, ip, source string, req *bo.W ConsumeTime: &req.PlainText.ConsumeInformation.ConsumeTime, TransactionID: req.PlainText.ConsumeInformation.TransactionID, EventType: req.EventType, + Status: req.PlainText.Status, OriginalData: originalData, }) } diff --git a/internal/data/model/multi_notify_data.gen.go b/internal/data/model/multi_notify_data.gen.go index bffbf9b..85de5ea 100644 --- a/internal/data/model/multi_notify_data.gen.go +++ b/internal/data/model/multi_notify_data.gen.go @@ -24,6 +24,7 @@ type MultiNotifyDatum struct { ConsumeTime *time.Time `gorm:"column:consume_time;not null;comment:核销时间" json:"consume_time"` // 核销时间 TransactionID string `gorm:"column:transaction_id;not null;comment:微信支付系统生成的订单号" json:"transaction_id"` // 微信支付系统生成的订单号 EventType string `gorm:"column:event_type;not null;comment:通知的类型" json:"event_type"` // 通知的类型 + Status string `gorm:"column:status;not null;comment:券状态" json:"status"` // 券状态 OriginalData string `gorm:"column:original_data;not null;comment:微信回调通知原始数据" json:"original_data"` // 微信回调通知原始数据 NoticeNum int32 `gorm:"column:notice_num;not null;comment:通知下游次数" json:"notice_num"` // 通知下游次数 CreateTime *time.Time `gorm:"column:create_time;not null;comment:创建时间" json:"create_time"` // 创建时间 diff --git a/test/bank_multi_activity_test.go b/test/bank_multi_activity_test.go index dfe0bad..c6922cb 100644 --- a/test/bank_multi_activity_test.go +++ b/test/bank_multi_activity_test.go @@ -57,7 +57,8 @@ func Test_MarketingQuery(t *testing.T) { func Test_QixingNotifyData(t *testing.T) { - wxBody := `{"id":"4ab2699d-e91d-5460-9810-25fd6d4c69a5","create_time":"2025-12-08T17:54:24+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","original_type":"coupon","associated_data":"coupon","plain_text":{"stock_creator_mchid":"1652465541","stock_id":"21386484","coupon_id":"142388354994","coupon_name":"银行卡多笔立减","description":"","status":"SENDED","create_time":"2025-12-08T17:50:48+08:00","coupon_type":"NORMAL","no_cash":false,"singleitem":false,"business_type":"","consume_information":{"consume_time":"2025-12-08T17:54:24+08:00","consume_mchid":"1274938601","transaction_id":"4200002996202512083063051834","consume_amount":16}}}` + //wxBody := `{"id":"4ab2699d-e91d-5460-9810-25fd6d4c69a5","create_time":"2025-12-08T17:54:24+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","original_type":"coupon","associated_data":"coupon","plain_text":{"stock_creator_mchid":"1652465541","stock_id":"21386484","coupon_id":"142388354994","coupon_name":"银行卡多笔立减","description":"","status":"SENDED","create_time":"2025-12-08T17:50:48+08:00","coupon_type":"NORMAL","no_cash":false,"singleitem":false,"business_type":"","consume_information":{"consume_time":"2025-12-08T17:54:24+08:00","consume_mchid":"1274938601","transaction_id":"4200002996202512083063051834","consume_amount":16}}}` + wxBody := `{"id":"4ab2699d-e91d-5460-9810-25fd6d4c69a6","create_time":"2025-12-08T17:54:24+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","original_type":"coupon","associated_data":"coupon","plain_text":{"stock_creator_mchid":"1652465541","stock_id":"21386484","coupon_id":"142388354994","coupon_name":"银行卡多笔立减","description":"","status":"USED","create_time":"2025-12-08T17:50:48+08:00","coupon_type":"NORMAL","no_cash":false,"singleitem":false,"business_type":"","consume_information":{"consume_time":"2025-12-08T17:54:24+08:00","consume_mchid":"1274938601","transaction_id":"4200002996202512083063051834","consume_amount":16}}}` t.Log(len(wxBody)) content := base64.StdEncoding.EncodeToString([]byte(wxBody)) From 6ada3821889b20390d94922ded0b1b03dfc1168c Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 19 Dec 2025 15:30:16 +0800 Subject: [PATCH 069/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/data/repoimpl/multi_notify_data.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/data/repoimpl/multi_notify_data.go b/internal/data/repoimpl/multi_notify_data.go index 83a5acd..2fd3c1c 100644 --- a/internal/data/repoimpl/multi_notify_data.go +++ b/internal/data/repoimpl/multi_notify_data.go @@ -60,6 +60,7 @@ func (p *MultiNotifyDataRepoImpl) Create(ctx context.Context, req *bo.MultiNotif ConsumeTime: req.ConsumeTime, TransactionID: req.TransactionID, EventType: req.EventType, + Status: req.Status.GetValue(), OriginalData: req.OriginalData, NoticeNum: 0, CreateTime: &now, From 6fd5dba1ebc243337c726e5c88b4450c8a6e3d0d Mon Sep 17 00:00:00 2001 From: ziming Date: Mon, 22 Dec 2025 09:54:00 +0800 Subject: [PATCH 070/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/data/repoimpl/order.go | 1 - test/bank_multi_activity_test.go | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/data/repoimpl/order.go b/internal/data/repoimpl/order.go index 8387cf2..0ba1690 100644 --- a/internal/data/repoimpl/order.go +++ b/internal/data/repoimpl/order.go @@ -462,7 +462,6 @@ func (p *OrderRepoImpl) MultiLastUsed(ctx context.Context, id uint64, lastUseTim }). Updates(model.Order{ Remark: "核销", - Status: vo.OrderStatusSuccess.GetValue(), LastUseTime: &lastUseTime, UpdateTime: &now, }) diff --git a/test/bank_multi_activity_test.go b/test/bank_multi_activity_test.go index c6922cb..b61358c 100644 --- a/test/bank_multi_activity_test.go +++ b/test/bank_multi_activity_test.go @@ -58,7 +58,7 @@ func Test_MarketingQuery(t *testing.T) { func Test_QixingNotifyData(t *testing.T) { //wxBody := `{"id":"4ab2699d-e91d-5460-9810-25fd6d4c69a5","create_time":"2025-12-08T17:54:24+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","original_type":"coupon","associated_data":"coupon","plain_text":{"stock_creator_mchid":"1652465541","stock_id":"21386484","coupon_id":"142388354994","coupon_name":"银行卡多笔立减","description":"","status":"SENDED","create_time":"2025-12-08T17:50:48+08:00","coupon_type":"NORMAL","no_cash":false,"singleitem":false,"business_type":"","consume_information":{"consume_time":"2025-12-08T17:54:24+08:00","consume_mchid":"1274938601","transaction_id":"4200002996202512083063051834","consume_amount":16}}}` - wxBody := `{"id":"4ab2699d-e91d-5460-9810-25fd6d4c69a6","create_time":"2025-12-08T17:54:24+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","original_type":"coupon","associated_data":"coupon","plain_text":{"stock_creator_mchid":"1652465541","stock_id":"21386484","coupon_id":"142388354994","coupon_name":"银行卡多笔立减","description":"","status":"USED","create_time":"2025-12-08T17:50:48+08:00","coupon_type":"NORMAL","no_cash":false,"singleitem":false,"business_type":"","consume_information":{"consume_time":"2025-12-08T17:54:24+08:00","consume_mchid":"1274938601","transaction_id":"4200002996202512083063051834","consume_amount":16}}}` + wxBody := `{"id":"4ab2699d-e91d-5460-9810-25fd6d4c69a6","create_time":"2025-12-08T17:54:24+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","original_type":"coupon","associated_data":"coupon","plain_text":{"stock_creator_mchid":"1652465541","stock_id":"21386484","coupon_id":"118758145502","coupon_name":"银行卡多笔立减","description":"","status":"USED","create_time":"2025-12-08T17:50:48+08:00","coupon_type":"NORMAL","no_cash":false,"singleitem":false,"business_type":"","consume_information":{"consume_time":"2025-12-08T17:54:24+08:00","consume_mchid":"1274938601","transaction_id":"4200002996202512083063051834","consume_amount":16}}}` t.Log(len(wxBody)) content := base64.StdEncoding.EncodeToString([]byte(wxBody)) From 888de99381a4bee8f12e9ecf106ccc8789ba9779 Mon Sep 17 00:00:00 2001 From: ziming Date: Mon, 22 Dec 2025 17:58:03 +0800 Subject: [PATCH 071/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/cron_notice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/biz/cron_notice.go b/internal/biz/cron_notice.go index 279e954..c65d294 100644 --- a/internal/biz/cron_notice.go +++ b/internal/biz/cron_notice.go @@ -86,7 +86,7 @@ func (this *VoucherBiz) timeSliceQuery(ctx context.Context, startTime, endTime t log.Warnf("订单定时通知,开始处理,按每两个小时分片处理,范围:%s到%s", startTime.Format(time.DateTime), endTime.Format(time.DateTime)) - duration := 2 * time.Hour + duration := 3 * time.Hour eg := new(errgroup.Group) eg.SetLimit(10) From 5d1ff1bdde7b981fb8434e3db6baf3cd9a024e59 Mon Sep 17 00:00:00 2001 From: ziming Date: Wed, 28 Jan 2026 09:36:30 +0800 Subject: [PATCH 072/144] query order --- internal/server/http.go | 1 + test/bank_multi_activity_test.go | 13 +++++++++++++ test/coupon.go | 18 +++++++++--------- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/internal/server/http.go b/internal/server/http.go index ac4c16e..c265340 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -46,6 +46,7 @@ func NewHTTPServer( srv.Route("/voucher/").POST("notifyRetry/{id}", cmb.NotifyRetry) // 查询订单状态及微信状态 srv.Route("/voucher/").POST("queryOrder/{order_no}/{phone}/{is_notice}", cmb.QueryOrder) + srv.Route("/voucher/").GET("query-order/{order_no}/{phone}/{is_notice}", cmb.QueryOrder) // 查询商品 srv.Route("/voucher/").POST("queryStock/{product_no}", cmb.QueryStock) // 注册商品tag到刚哥那边 diff --git a/test/bank_multi_activity_test.go b/test/bank_multi_activity_test.go index b61358c..057fcca 100644 --- a/test/bank_multi_activity_test.go +++ b/test/bank_multi_activity_test.go @@ -55,6 +55,19 @@ func Test_MarketingQuery(t *testing.T) { //不行的,需要是在自己appid下的才能查到 } +func Test_QixingNotifyDataDecodeString(t *testing.T) { + + content := `eyJzdW1tYXJ5Ijoi5Luj6YeR5Yi45qC46ZSA6YCa55+lIiwiYXNzb2NpYXRlZF9kYXRhIjoiY291cG9uIiwiZXZlbnRfdHlwZSI6IkNPVVBPTi5VU0UiLCJjcmVhdGVfdGltZSI6IjIwMjUtMTItMjRUMTk6NDg6NDYrMDg6MDAiLCJwbGFpbl90ZXh0Ijp7InN0b2NrX2NyZWF0b3JfbWNoaWQiOiIxNzE1MzQ5NTc4Iiwic3RvY2tfaWQiOiIyMTQzODg1MSIsImNvdXBvbl9pZCI6IjE0NDQ2MTIyOTg5MCIsInNpbmdsZWl0ZW1fZGlzY291bnRfb2ZmIjpudWxsLCJkaXNjb3VudF90byI6bnVsbCwiY291cG9uX25hbWUiOiLpk7booYzljaHlpJrnrJTnq4vlh48iLCJzdGF0dXMiOiJVU0VEIiwiZGVzY3JpcHRpb24iOiIiLCJjcmVhdGVfdGltZSI6IjIwMjUtMTItMThUMTU6NDQ6NTIrMDg6MDAiLCJjb3Vwb25fdHlwZSI6Ik5PUk1BTCIsIm5vX2Nhc2giOmZhbHNlLCJhdmFpbGFibGVfYmVnaW5fdGltZSI6IjIwMjUtMTItMThUMDA6MDA6MDArMDg6MDAiLCJhdmFpbGFibGVfZW5kX3RpbWUiOiIyMDI2LTAxLTE2VDIzOjU5OjU5KzA4OjAwIiwic2luZ2xlaXRlbSI6ZmFsc2UsIm5vcm1hbF9jb3Vwb25faW5mb3JtYXRpb24iOnsiY291cG9uX2Ftb3VudCI6MzAwLCJ0cmFuc2FjdGlvbl9taW5pbXVtIjoxODAwfSwiY29uc3VtZV9pbmZvcm1hdGlvbiI6eyJjb25zdW1lX3RpbWUiOiIyMDI1LTEyLTI0VDE5OjQ4OjQ2KzA4OjAwIiwiY29uc3VtZV9tY2hpZCI6IjEyNzQ5Mzg2MDEiLCJ0cmFuc2FjdGlvbl9pZCI6IjQyMDAwMDI5MTcyMDI1MTIyNDgzODQ2MTg0MzgiLCJjb25zdW1lX2Ftb3VudCI6MzAwLCJnb29kc19kZXRhaWwiOm51bGx9fSwicmVzb3VyY2VfdHlwZSI6ImVuY3J5cHQtcmVzb3VyY2UiLCJvcmlnaW5hbF90eXBlIjoiY291cG9uIiwiaWQiOiI4NzkxZTMyZS0zYjFiLTViNDktYTNiNi1mYzY0YTc5NTVlZjEifQ==` + + b, err := base64.StdEncoding.DecodeString(content) + if err != nil { + t.Errorf("base64.StdEncoding.DecodeString() error = %v", err) + return + } + + t.Log(string(b)) +} + func Test_QixingNotifyData(t *testing.T) { //wxBody := `{"id":"4ab2699d-e91d-5460-9810-25fd6d4c69a5","create_time":"2025-12-08T17:54:24+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","original_type":"coupon","associated_data":"coupon","plain_text":{"stock_creator_mchid":"1652465541","stock_id":"21386484","coupon_id":"142388354994","coupon_name":"银行卡多笔立减","description":"","status":"SENDED","create_time":"2025-12-08T17:50:48+08:00","coupon_type":"NORMAL","no_cash":false,"singleitem":false,"business_type":"","consume_information":{"consume_time":"2025-12-08T17:54:24+08:00","consume_mchid":"1274938601","transaction_id":"4200002996202512083063051834","consume_amount":16}}}` diff --git a/test/coupon.go b/test/coupon.go index 9b8d40a..64cc2d4 100644 --- a/test/coupon.go +++ b/test/coupon.go @@ -57,12 +57,12 @@ func SendCoupon() { } req := cashcoupons.SendCouponRequest{ - OutRequestNo: core.String("FZQM1948534036766040066"), // {CouponId:129623470711} + OutRequestNo: core.String("LQ2011991700944699394"), // {CouponId:129623470711} // 微信为发券方商户分配的公众账号ID,接口传入的所有appid应该为公众号的appid(在mp.weixin.qq.com申请的),不能为APP的appid(在open.weixin.qq.com申请的)。 Appid: core.String("wxd27e255810842ba8"), - Openid: core.String("o3dEt5cA8jt3Kz5wNzAO6-3YQHsE"), - StockId: core.String("21104160"), - StockCreatorMchid: core.String("1715349578"), + Openid: core.String("o3dEt5b_1lFtKc-aAT3tiYjJIGwk"), + StockId: core.String("21502886"), + StockCreatorMchid: core.String("1652465541"), } fmt.Printf("\nreq:%+v", req) @@ -102,9 +102,9 @@ func QueryCoupon() { return } - appId := "wx619991cc795028f5" - openId := "oSNb4fulXAleiammvWXnz1pRghAE" - couponId := "142388354994" + appId := "wxd27e255810842ba8" + openId := "o3dEt5Wq3v-bEBXXkzvIlMgMh7Kc" + couponId := "149248300483" req := cashcoupons.QueryCouponRequest{ CouponId: core.String(couponId), @@ -157,8 +157,8 @@ func QueryProduct() { } req := cashcoupons.QueryStockRequest{ - StockId: core.String("21386505"), - StockCreatorMchid: core.String("1715349578"), + StockId: core.String("21502886"), + StockCreatorMchid: core.String("1652465541"), } svc := cashcoupons.StockApiService{Client: client} From 6fdd1f3fd68d67cb5b0f313e9090eb2b6386983c Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 29 Jan 2026 16:47:04 +0800 Subject: [PATCH 073/144] query order --- internal/biz/cron_notice.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/biz/cron_notice.go b/internal/biz/cron_notice.go index c65d294..581c478 100644 --- a/internal/biz/cron_notice.go +++ b/internal/biz/cron_notice.go @@ -86,10 +86,10 @@ func (this *VoucherBiz) timeSliceQuery(ctx context.Context, startTime, endTime t log.Warnf("订单定时通知,开始处理,按每两个小时分片处理,范围:%s到%s", startTime.Format(time.DateTime), endTime.Format(time.DateTime)) - duration := 3 * time.Hour + duration := 4 * time.Hour eg := new(errgroup.Group) - eg.SetLimit(10) + eg.SetLimit(8) for start := startTime; start.Before(endTime); start = start.Add(duration) { From ad51cfae46e40951993e7fb6cea3a9cc3b5b0b2e Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 29 Jan 2026 16:48:02 +0800 Subject: [PATCH 074/144] query order --- internal/biz/cron_notice.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/biz/cron_notice.go b/internal/biz/cron_notice.go index 581c478..ec00c6a 100644 --- a/internal/biz/cron_notice.go +++ b/internal/biz/cron_notice.go @@ -86,7 +86,7 @@ func (this *VoucherBiz) timeSliceQuery(ctx context.Context, startTime, endTime t log.Warnf("订单定时通知,开始处理,按每两个小时分片处理,范围:%s到%s", startTime.Format(time.DateTime), endTime.Format(time.DateTime)) - duration := 4 * time.Hour + duration := 6 * time.Hour eg := new(errgroup.Group) eg.SetLimit(8) From 7ec609137a76c09f4e25d1f3733dbfdd51ff495d Mon Sep 17 00:00:00 2001 From: ziming Date: Mon, 9 Feb 2026 14:24:37 +0800 Subject: [PATCH 075/144] query order --- internal/service/script.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/service/script.go b/internal/service/script.go index 62b3282..1596013 100644 --- a/internal/service/script.go +++ b/internal/service/script.go @@ -78,6 +78,7 @@ func (this *CmbService) QueryOrder(ctx http.Context) error { return err } + ctx.Header().Set("Content-Type", "text/html; charset=utf-8") return ctx.String(http2.StatusOK, str) } From 8d420e0d00be11c72fe4144a7d1721a2b9a6a8fd Mon Sep 17 00:00:00 2001 From: ziming Date: Mon, 9 Feb 2026 14:31:19 +0800 Subject: [PATCH 076/144] query order --- internal/service/script.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/script.go b/internal/service/script.go index 1596013..a661ee0 100644 --- a/internal/service/script.go +++ b/internal/service/script.go @@ -78,7 +78,7 @@ func (this *CmbService) QueryOrder(ctx http.Context) error { return err } - ctx.Header().Set("Content-Type", "text/html; charset=utf-8") + ctx.Response().Header().Add("Content-Type", "text/html; charset=utf-8") return ctx.String(http2.StatusOK, str) } From 24412038331dc69ce1f62349067cbc7873141462 Mon Sep 17 00:00:00 2001 From: ziming Date: Mon, 9 Feb 2026 14:35:31 +0800 Subject: [PATCH 077/144] query order --- internal/service/script.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/service/script.go b/internal/service/script.go index a661ee0..e7f1572 100644 --- a/internal/service/script.go +++ b/internal/service/script.go @@ -79,7 +79,9 @@ func (this *CmbService) QueryOrder(ctx http.Context) error { } ctx.Response().Header().Add("Content-Type", "text/html; charset=utf-8") - return ctx.String(http2.StatusOK, str) + ctx.Response().WriteHeader(http2.StatusOK) + _, err = ctx.Response().Write([]byte(str)) + return err } func (this *CmbService) QueryStock(ctx http.Context) error { From 42b88fd9f9ef97a0d19bb26984efba9e6d8ed4f6 Mon Sep 17 00:00:00 2001 From: ziming Date: Mon, 9 Feb 2026 14:40:30 +0800 Subject: [PATCH 078/144] query order --- internal/service/script.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/script.go b/internal/service/script.go index e7f1572..c51b809 100644 --- a/internal/service/script.go +++ b/internal/service/script.go @@ -78,7 +78,7 @@ func (this *CmbService) QueryOrder(ctx http.Context) error { return err } - ctx.Response().Header().Add("Content-Type", "text/html; charset=utf-8") + ctx.Response().Header().Add("Content-Type", "text/plain; charset=utf-8") ctx.Response().WriteHeader(http2.StatusOK) _, err = ctx.Response().Write([]byte(str)) return err From 9fc1ab9c8240c5438a80797cc6203ddd5fc9c54a Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 5 Mar 2026 14:14:58 +0800 Subject: [PATCH 079/144] query order notify --- internal/biz/do/rds_mq.go | 12 + internal/biz/order_notify_retry.go | 10 + internal/biz/repo/order.go | 1 + internal/biz/repo/orderBak.go | 10 - internal/biz/repo/order_bak.go | 17 ++ internal/biz/timeslicequery/base.go | 7 +- internal/biz/timeslicequery/query.go | 20 +- internal/biz/timeslicequery/query_bak.go | 62 +++++ .../biz/timeslicequery/retry_query_notice.go | 246 ++++++++++++++++++ internal/data/repoimpl/order.go | 55 ++++ internal/data/repoimpl/order_bak.go | 145 +++++++++++ internal/server/http.go | 1 + internal/server/rds_consume.go | 4 + internal/service/script.go | 19 ++ internal/service/wechat_query.go | 27 ++ test/coupon.go | 6 +- 16 files changed, 625 insertions(+), 17 deletions(-) delete mode 100644 internal/biz/repo/orderBak.go create mode 100644 internal/biz/repo/order_bak.go create mode 100644 internal/biz/timeslicequery/query_bak.go create mode 100644 internal/biz/timeslicequery/retry_query_notice.go diff --git a/internal/biz/do/rds_mq.go b/internal/biz/do/rds_mq.go index 3009437..4a587fc 100644 --- a/internal/biz/do/rds_mq.go +++ b/internal/biz/do/rds_mq.go @@ -33,3 +33,15 @@ type WechatUsedQuery struct { StartTime string `json:"start_time"` EndTime string `json:"end_time"` } + +type RetryQueryNotice struct { + ProductNo string `json:"product_no"` + + ReceiveSuccessStartTime string `json:"receive_success_start_time"` + ReceiveSuccessEndTime string `json:"receive_success_end_time"` + + OrderNos []string `json:"order_nos"` + BatchNos []string `json:"batch_nos"` + OutBizNos []string `json:"out_biz_nos"` + VoucherNos []string `json:"voucher_nos"` +} diff --git a/internal/biz/order_notify_retry.go b/internal/biz/order_notify_retry.go index b7af66c..ce8b8c2 100644 --- a/internal/biz/order_notify_retry.go +++ b/internal/biz/order_notify_retry.go @@ -9,6 +9,16 @@ import ( "voucher/internal/biz/do" ) +func (this *VoucherBiz) PushRetryOrderNotice(ctx http.Context, bodyBytes []byte) error { + + _, err := this.rdb.Rdb.RPush(ctx, "retryQueryNotice", string(bodyBytes)).Result() + if err != nil { + return fmt.Errorf("添加到队列失败:%v", err) + } + + return nil +} + func (this *VoucherBiz) PushOrderNotifyRetry(ctx http.Context, req *do.OrderNotifyRetry) error { queue := this.bc.RdsMQ.GetOrderNotifyRetry() diff --git a/internal/biz/repo/order.go b/internal/biz/repo/order.go index b0e26f4..633ff9a 100644 --- a/internal/biz/repo/order.go +++ b/internal/biz/repo/order.go @@ -15,6 +15,7 @@ type OrderRepo interface { FinFailByStockIdInBatches(ctx context.Context, batchNo string, fun func(ctx context.Context, rows []*bo.OrderBo) error) error FindIngInBatches(ctx context.Context, fun func(ctx context.Context, rows []*bo.OrderBo) error) error FindInBatches(ctx context.Context, w *bo.FindInBatchesUseBo, fun func(ctx context.Context, rows []*bo.OrderBo) error) error + FindRetryQuery(ctx context.Context, req *do.RetryQueryNotice, fun func(ctx context.Context, rows []*bo.OrderBo) error) error GetByOutBizNo(ctx context.Context, t vo.OrderType, outBizNo string) (*bo.OrderBo, error) GetByOrderNo(ctx context.Context, orderNo string) (*bo.OrderBo, error) GetByCouponId(ctx context.Context, merchantNo, batchNo, voucherNo string) (*bo.OrderBo, error) diff --git a/internal/biz/repo/orderBak.go b/internal/biz/repo/orderBak.go deleted file mode 100644 index 2c60b03..0000000 --- a/internal/biz/repo/orderBak.go +++ /dev/null @@ -1,10 +0,0 @@ -package repo - -import ( - "context" - "voucher/internal/biz/bo" -) - -type OrderBakRepo interface { - SpecifyFindInBatches(ctx context.Context, w *bo.FindInBatchesBo, fun func(ctx context.Context, rows []*bo.OrderBo) error) error -} diff --git a/internal/biz/repo/order_bak.go b/internal/biz/repo/order_bak.go new file mode 100644 index 0000000..18ed531 --- /dev/null +++ b/internal/biz/repo/order_bak.go @@ -0,0 +1,17 @@ +package repo + +import ( + "context" + "voucher/internal/biz/bo" + "voucher/internal/biz/do" +) + +type OrderBakRepo interface { + SpecifyFindInBatches(ctx context.Context, w *bo.FindInBatchesBo, fun func(ctx context.Context, rows []*bo.OrderBo) error) error + FindRetryQuery(ctx context.Context, req *do.RetryQueryNotice, fun func(ctx context.Context, rows []*bo.OrderBo) error) error + GetByID(ctx context.Context, id uint64) (*bo.OrderBo, error) + + Used(ctx context.Context, id uint64) error + Available(ctx context.Context, id uint64) error + Expired(ctx context.Context, id uint64) error +} diff --git a/internal/biz/timeslicequery/base.go b/internal/biz/timeslicequery/base.go index 81155bd..f3a6348 100644 --- a/internal/biz/timeslicequery/base.go +++ b/internal/biz/timeslicequery/base.go @@ -19,8 +19,9 @@ type Query struct { rdb *data.Rdb cmb *cmb.Cmb - productRepo repo.ProductRepo - orderRepo repo.OrderRepo + productRepo repo.ProductRepo + orderRepo repo.OrderRepo + orderBakRepo repo.OrderBakRepo wechatCpnRepo wechatrepo.WechatCpnRepo mqSendMixRepo mixrepos.MQSendMixRepo @@ -32,6 +33,7 @@ func NewQuery( cmb *cmb.Cmb, productRepo repo.ProductRepo, orderRepo repo.OrderRepo, + orderBakRepo repo.OrderBakRepo, wechatCpnRepo wechatrepo.WechatCpnRepo, mqSendMixRepo mixrepos.MQSendMixRepo) *Query { return &Query{ @@ -41,6 +43,7 @@ func NewQuery( cmb: cmb, productRepo: productRepo, orderRepo: orderRepo, + orderBakRepo: orderBakRepo, wechatCpnRepo: wechatCpnRepo, mqSendMixRepo: mqSendMixRepo} } diff --git a/internal/biz/timeslicequery/query.go b/internal/biz/timeslicequery/query.go index eb1f58c..2b2cced 100644 --- a/internal/biz/timeslicequery/query.go +++ b/internal/biz/timeslicequery/query.go @@ -32,7 +32,8 @@ func (v *Query) wechatQuery(ctx context.Context, order *bo.OrderBo, useNum *int) func (v *Query) queryUsed(ctx context.Context, order *bo.OrderBo) error { if order.Status.IsUse() { - return v.notify(ctx, order) + _, err := v.cmb.Notify(ctx, order) + return err } if err := v.orderRepo.Used(ctx, order.ID); err != nil { @@ -45,7 +46,8 @@ func (v *Query) queryUsed(ctx context.Context, order *bo.OrderBo) error { func (v *Query) queryExpired(ctx context.Context, order *bo.OrderBo) error { if order.Status.IsExpired() { - return nil + _, err := v.cmb.Notify(ctx, order) + return err } if err := v.orderRepo.Expired(ctx, order.ID); err != nil { @@ -55,6 +57,20 @@ func (v *Query) queryExpired(ctx context.Context, order *bo.OrderBo) error { return v.notify(ctx, order) } +func (v *Query) querySuccess(ctx context.Context, order *bo.OrderBo) error { + + if order.Status.IsSuccess() { + _, err := v.cmb.Notify(ctx, order) + return err + } + + if err := v.orderRepo.Available(ctx, order.ID); err != nil { + return err + } + + return v.notify(ctx, order) +} + func (v *Query) notify(ctx context.Context, order *bo.OrderBo) error { order, err := v.orderRepo.GetByID(ctx, order.ID) diff --git a/internal/biz/timeslicequery/query_bak.go b/internal/biz/timeslicequery/query_bak.go new file mode 100644 index 0000000..b38f75c --- /dev/null +++ b/internal/biz/timeslicequery/query_bak.go @@ -0,0 +1,62 @@ +package timeslicequery + +import ( + "context" + "voucher/internal/biz/bo" +) + +func (v *Query) queryUsedBak(ctx context.Context, order *bo.OrderBo) error { + + if order.Status.IsUse() { + _, err := v.cmb.Notify(ctx, order) + return err + } + + if err := v.orderBakRepo.Used(ctx, order.ID); err != nil { + return err + } + + return v.notify(ctx, order) +} + +func (v *Query) queryExpiredBak(ctx context.Context, order *bo.OrderBo) error { + + if order.Status.IsExpired() { + _, err := v.cmb.Notify(ctx, order) + return err + } + + if err := v.orderBakRepo.Expired(ctx, order.ID); err != nil { + return err + } + + return v.notify(ctx, order) +} + +func (v *Query) querySuccessBak(ctx context.Context, order *bo.OrderBo) error { + + if order.Status.IsSuccess() { + _, err := v.cmb.Notify(ctx, order) + return err + } + + if err := v.orderBakRepo.Available(ctx, order.ID); err != nil { + return err + } + + return v.notify(ctx, order) +} + +func (v *Query) notifyBak(ctx context.Context, order *bo.OrderBo) error { + + order, err := v.orderBakRepo.GetByID(ctx, order.ID) + if err != nil { + return err + } + + if _, err = v.cmb.Notify(ctx, order); err != nil { + return err + } + + return nil +} diff --git a/internal/biz/timeslicequery/retry_query_notice.go b/internal/biz/timeslicequery/retry_query_notice.go new file mode 100644 index 0000000..8646391 --- /dev/null +++ b/internal/biz/timeslicequery/retry_query_notice.go @@ -0,0 +1,246 @@ +package timeslicequery + +import ( + "context" + "encoding/json" + "fmt" + "github.com/go-kratos/kratos/v2/log" + "github.com/hashicorp/go-multierror" + "golang.org/x/sync/errgroup" + "runtime" + "sync" + "time" + "voucher/internal/biz/bo" + "voucher/internal/biz/do" +) + +func (v *Query) RetryQueryNotice(ctx context.Context, msg string) error { + + var req *do.RetryQueryNotice + + if err := json.Unmarshal([]byte(msg), &req); err != nil { + return err + } + + err := v.RetryQueryNoticeOrder(ctx, req) + if err != nil { + return err + } + + return v.RetryQueryNoticeOrderBak(ctx, req) +} + +func (v *Query) RetryQueryNoticeOrder(ctx context.Context, req *do.RetryQueryNotice) error { + + start := time.Now() + num := 0 + errNum := 0 + sucNum := 0 + + var mu sync.Mutex + errs := make([]error, 0) + + eg := new(errgroup.Group) + eg.SetLimit(5) + + err := v.orderRepo.FindRetryQuery(ctx, req, func(ctx context.Context, rows []*bo.OrderBo) error { + + eg.Go(func() error { + + defer func() { + if err := recover(); err != nil { + // 获取调用栈信息 + _, file, line, _ := runtime.Caller(1) // 1 表示获取当前调用者的调用信息 + + mu.Lock() + errs = append(errs, fmt.Errorf("panic: %v,file:%s, line:%d", err, file, line)) + mu.Unlock() + } + }() + + for _, order := range rows { + + if err := v.retryQueryNoticeOrder(ctx, order); err != nil { + + logFields := map[string]string{ + "order_no": order.OrderNo, + "coupon_id": order.VoucherNo, + "open_id": order.Account, + "stock_id": order.BatchNo, + "err": err.Error(), + } + + log.Errorf("微信券查询order,错误:%+v", logFields) + + errNum++ + + if errNum > 20 { + return fmt.Errorf("微信券查询order,已经连续发生20次错误%+v", logFields) + } + + } else { + sucNum++ + } + } + + return nil + }) + + return nil + }) + + // 等待所有任务完成 + if err := eg.Wait(); err != nil { + return fmt.Errorf("微信券查询order,任务执行失败: %v", err) + } + + end := time.Now() + + logFields := map[string]any{ + "num": num, + "sucNum": sucNum, + "errNum": errNum, + "elapsed": end.Sub(start).String(), + } + log.Warnf("微信券查询order,处理完毕:%+v", logFields) + + // 收集错误 + var result error + for _, err2 := range errs { + result = multierror.Append(result, err2) + } + + return err +} + +func (v *Query) retryQueryNoticeOrder(ctx context.Context, order *bo.OrderBo) error { + + if order.Status.IsExpired() { + _, err := v.cmb.Notify(ctx, order) + return err + } + + status, err := v.wechatCpnRepo.Query(ctx, order) + if err != nil { + return err + } + + if status.IsUse() { + return v.queryUsed(ctx, order) + } else if status.IsSuccess() { + return v.querySuccess(ctx, order) + } else if status.IsExpired() { + return v.queryExpired(ctx, order) + } + + return nil +} + +func (v *Query) RetryQueryNoticeOrderBak(ctx context.Context, req *do.RetryQueryNotice) error { + + start := time.Now() + num := 0 + errNum := 0 + sucNum := 0 + + var mu sync.Mutex + errs := make([]error, 0) + + eg := new(errgroup.Group) + eg.SetLimit(5) + + err := v.orderBakRepo.FindRetryQuery(ctx, req, func(ctx context.Context, rows []*bo.OrderBo) error { + + eg.Go(func() error { + + defer func() { + if err := recover(); err != nil { + // 获取调用栈信息 + _, file, line, _ := runtime.Caller(1) // 1 表示获取当前调用者的调用信息 + + mu.Lock() + errs = append(errs, fmt.Errorf("panic: %v,file:%s, line:%d", err, file, line)) + mu.Unlock() + } + }() + + for _, order := range rows { + if err := v.retryQueryNoticeOrderBal(ctx, order); err != nil { + + logFields := map[string]string{ + "order_no": order.OrderNo, + "coupon_id": order.VoucherNo, + "open_id": order.Account, + "stock_id": order.BatchNo, + "err": err.Error(), + } + + log.Errorf("微信券查询orderBak,错误:%+v", logFields) + + errNum++ + + if errNum > 20 { + return fmt.Errorf("微信券查询orderBak,已经连续发生20次错误%+v", logFields) + } + + } else { + sucNum++ + } + } + + return nil + }) + + return nil + }) + + if err != nil { + return err + } + + // 等待所有任务完成 + if err2 := eg.Wait(); err2 != nil { + return fmt.Errorf("微信券查询orderBak,任务执行失败: %v", err2) + } + + end := time.Now() + + logFields := map[string]any{ + "num": num, + "sucNum": sucNum, + "errNum": errNum, + "elapsed": end.Sub(start).String(), + } + log.Warnf("微信券查询orderBak,处理完毕:%+v", logFields) + + // 收集错误 + var result error + for _, err2 := range errs { + result = multierror.Append(result, err2) + } + + return result +} + +func (v *Query) retryQueryNoticeOrderBal(ctx context.Context, order *bo.OrderBo) error { + + if order.Status.IsExpired() { + _, err := v.cmb.Notify(ctx, order) + return err + } + + status, err := v.wechatCpnRepo.Query(ctx, order) + if err != nil { + return err + } + + if status.IsUse() { + return v.queryUsedBak(ctx, order) + } else if status.IsSuccess() { + return v.querySuccessBak(ctx, order) + } else if status.IsExpired() { + return v.queryExpiredBak(ctx, order) + } + + return nil +} diff --git a/internal/data/repoimpl/order.go b/internal/data/repoimpl/order.go index 0ba1690..8fa2c54 100644 --- a/internal/data/repoimpl/order.go +++ b/internal/data/repoimpl/order.go @@ -206,6 +206,61 @@ func (p *OrderRepoImpl) FindInBatches(ctx context.Context, req *bo.FindInBatches return nil } +func (p *OrderRepoImpl) FindRetryQuery(ctx context.Context, req *do.RetryQueryNotice, fun func(ctx context.Context, rows []*bo.OrderBo) error) error { + + statusArr := []uint8{ + vo.OrderStatusSuccess.GetValue(), + vo.OrderStatusUse.GetValue(), + vo.OrderStatusExpired.GetValue(), + } + + tx := p.DB(ctx). + Where("`status` in (?)", statusArr). + Where("activity_id = ''") + + if req.ProductNo != "" { + tx = tx.Where("product_no = ?", req.ProductNo) + } + + if req.ReceiveSuccessStartTime != "" { + tx = tx.Where("receive_success_time > ?", req.ReceiveSuccessStartTime) + } + if req.ReceiveSuccessEndTime != "" { + tx = tx.Where("receive_success_time <= ?", req.ReceiveSuccessEndTime) + } + + if req.ProductNo != "" { + tx = tx.Where("product_no = ?", req.ProductNo) + } + + if req.OrderNos != nil { + tx = tx.Where("order_no IN (?)", req.OrderNos) + } + + if req.OutBizNos != nil { + tx = tx.Where("out_biz_no IN (?)", req.OutBizNos) + } + + if req.VoucherNos != nil { + tx = tx.Where("voucher_no IN (?)", req.VoucherNos) + } + + var results = make([]*model.Order, 0) + + tx.Order("receive_success_time asc") // 显式清除排序,移除默认的 ORDER BY + + result := tx.FindInBatches(&results, 1000, func(tx *gorm.DB, batch int) error { + + return fun(ctx, p.ToBos(results)) + }) + + if result.Error != nil { + return result.Error + } + + return nil +} + func (p *OrderRepoImpl) Create(ctx context.Context, req *bo.OrderBo) (*bo.OrderBo, error) { now := time.Now() diff --git a/internal/data/repoimpl/order_bak.go b/internal/data/repoimpl/order_bak.go index 6509075..49c72d9 100644 --- a/internal/data/repoimpl/order_bak.go +++ b/internal/data/repoimpl/order_bak.go @@ -2,9 +2,15 @@ package repoimpl import ( "context" + "errors" + "fmt" "gorm.io/gorm" + "time" + err2 "voucher/api/err" "voucher/internal/biz/bo" + "voucher/internal/biz/do" "voucher/internal/biz/repo" + "voucher/internal/biz/vo" "voucher/internal/data" "voucher/internal/data/model" ) @@ -61,3 +67,142 @@ func (p *OrderBakRepoImpl) SpecifyFindInBatches(ctx context.Context, req *bo.Fin return nil } + +func (p *OrderBakRepoImpl) FindRetryQuery(ctx context.Context, req *do.RetryQueryNotice, fun func(ctx context.Context, rows []*bo.OrderBo) error) error { + + statusArr := []uint8{ + vo.OrderStatusSuccess.GetValue(), + vo.OrderStatusUse.GetValue(), + vo.OrderStatusExpired.GetValue(), + } + + tx := p.DB(ctx). + Where("`status` in (?)", statusArr). + Where("activity_id = ''") + + if req.ProductNo != "" { + tx = tx.Where("product_no = ?", req.ProductNo) + } + + if req.ReceiveSuccessStartTime != "" { + tx = tx.Where("receive_success_time > ?", req.ReceiveSuccessStartTime) + } + if req.ReceiveSuccessEndTime != "" { + tx = tx.Where("receive_success_time <= ?", req.ReceiveSuccessEndTime) + } + + if req.ProductNo != "" { + tx = tx.Where("product_no = ?", req.ProductNo) + } + + if req.OrderNos != nil { + tx = tx.Where("order_no IN (?)", req.OrderNos) + } + + if req.OutBizNos != nil { + tx = tx.Where("out_biz_no IN (?)", req.OutBizNos) + } + + if req.VoucherNos != nil { + tx = tx.Where("voucher_no IN (?)", req.VoucherNos) + } + + var results = make([]*model.OrderBak, 0) + + tx.Order("receive_success_time asc") // 显式清除排序,移除默认的 ORDER BY + + result := tx.FindInBatches(&results, 1000, func(tx *gorm.DB, batch int) error { + + return fun(ctx, p.ToBos(results)) + }) + + if result.Error != nil { + return result.Error + } + + return nil +} + +func (p *OrderBakRepoImpl) GetByID(ctx context.Context, id uint64) (*bo.OrderBo, error) { + info := &model.OrderBak{} + + tx := p.DB(ctx).Where(model.OrderBak{ID: id}).First(&info) + + if tx.Error != nil { + + if errors.Is(tx.Error, gorm.ErrRecordNotFound) { + return nil, err2.ErrorDbNotFound("订单数据不存在") + } + + return nil, fmt.Errorf("order db fail %w", tx.Error) + } + + if tx.RowsAffected == 0 { + return nil, err2.ErrorDbNotFound("订单数据不存在") + } + + return p.ToBo(info), nil +} + +func (p *OrderBakRepoImpl) Used(ctx context.Context, id uint64) error { + now := time.Now() + + tx := p.DB(ctx). + Where(model.OrderBak{ + ID: id, + }). + Updates(model.OrderBak{ + Status: vo.OrderStatusUse.GetValue(), + Remark: "核销", + LastUseTime: &now, + UpdateTime: &now, + }) + + if tx.Error != nil { + return fmt.Errorf("update db fail %w", tx.Error) + } + + return nil +} + +func (p *OrderBakRepoImpl) Expired(ctx context.Context, id uint64) error { + now := time.Now() + + tx := p.DB(ctx). + Where(model.OrderBak{ + ID: id, + }). + Updates(model.OrderBak{ + Status: vo.OrderStatusExpired.GetValue(), + Remark: "过期", + UpdateTime: &now, + }) + + if tx.Error != nil { + return fmt.Errorf("update db fail %w", tx.Error) + } + + return nil +} + +func (p *OrderBakRepoImpl) Available(ctx context.Context, id uint64) error { + now := time.Now() + + tx := p.DB(ctx). + Where(model.OrderBak{ + ID: id, + Status: vo.OrderStatusUse.GetValue(), + }). + Updates(model.OrderBak{ + Status: vo.OrderStatusSuccess.GetValue(), + Remark: "重置为成功,领取成功时间重置", + ReceiveSuccessTime: &now, // 领取成功时间重置 + UpdateTime: &now, + }) + + if tx.Error != nil { + return fmt.Errorf("update db fail %w", tx.Error) + } + + return nil +} diff --git a/internal/server/http.go b/internal/server/http.go index c265340..ca5ce10 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -42,6 +42,7 @@ func NewHTTPServer( // 订单通知重试 -- 不健全 srv.Route("/voucher/").POST("orderNotifyRetry", cmb.OrderNotifyRetry) + srv.Route("/voucher/").POST("retryOrderNotice", cmb.RetryOrderNotice) // 重试通知 srv.Route("/voucher/").POST("notifyRetry/{id}", cmb.NotifyRetry) // 查询订单状态及微信状态 diff --git a/internal/server/rds_consume.go b/internal/server/rds_consume.go index c1c9636..64e6ebd 100644 --- a/internal/server/rds_consume.go +++ b/internal/server/rds_consume.go @@ -26,6 +26,10 @@ func NewRdbConsumer( ) *RdbConsumer { manager := rdsmq.NewConsumerManager() + if cfTr := voucherService.GetRetryQueryNoticeConfig(); cfTr != nil { + manager.Add(cfTr) + } + if cf := voucherService.GetWechatQueryConfig(); cf != nil { manager.Add(cf) } diff --git a/internal/service/script.go b/internal/service/script.go index c51b809..cd7d2b9 100644 --- a/internal/service/script.go +++ b/internal/service/script.go @@ -11,6 +11,25 @@ import ( "voucher/internal/biz/do" ) +func (this *CmbService) RetryOrderNotice(ctx http.Context) error { + + bodyBytes, err := io.ReadAll(ctx.Request().Body) + if err != nil { + return err + } + + if bodyBytes == nil { + return fmt.Errorf("bodyBytes is empty") + } + + var req *do.RetryQueryNotice + if err = json.Unmarshal(bodyBytes, &req); err != nil { + return err + } + + return this.VoucherBiz.PushRetryOrderNotice(ctx, bodyBytes) +} + func (this *CmbService) OrderNotifyRetry(ctx http.Context) error { bodyBytes, err := io.ReadAll(ctx.Request().Body) diff --git a/internal/service/wechat_query.go b/internal/service/wechat_query.go index 6e44485..dac4b56 100644 --- a/internal/service/wechat_query.go +++ b/internal/service/wechat_query.go @@ -80,3 +80,30 @@ func (s *VoucherService) WechatTimeSliceQueryHandle(ctx context.Context, msg str return nil } + +func (s *VoucherService) GetRetryQueryNoticeConfig() *rdsmq.ConsumeConfig { + + return &rdsmq.ConsumeConfig{ + Rdb: s.rdb.Rdb, + QueueName: "retryQueryNotice", + NumWorkers: 1, + WaitTime: 30, + RetryNum: 1, + Fn: s.RetryQueryNotice, + Logger: s.logHelper, + } +} + +func (s *VoucherService) RetryQueryNotice(ctx context.Context, msg string) error { + + if msg == "" { + s.logHelper.Errorf("wechat TimeSlice query error: batchNo is empty") + return nil + } + + if err := s.timeSliceQuery.RetryQueryNotice(ctx, msg); err != nil { + s.logHelper.Errorf("retry query notice msg:%s error: %v", msg, err) + } + + return nil +} diff --git a/test/coupon.go b/test/coupon.go index 64cc2d4..f63f57e 100644 --- a/test/coupon.go +++ b/test/coupon.go @@ -102,9 +102,9 @@ func QueryCoupon() { return } - appId := "wxd27e255810842ba8" - openId := "o3dEt5Wq3v-bEBXXkzvIlMgMh7Kc" - couponId := "149248300483" + appId := "wx619991cc795028f5" + openId := "oSNb4fnWoktz7YVNIXE5bvoB3-1w" + couponId := "106048490308" req := cashcoupons.QueryCouponRequest{ CouponId: core.String(couponId), From 10727c1a00f840ed867f9650728570083058c5bf Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 5 Mar 2026 18:04:19 +0800 Subject: [PATCH 080/144] query order notify --- internal/biz/timeslicequery/retry_query_notice.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/biz/timeslicequery/retry_query_notice.go b/internal/biz/timeslicequery/retry_query_notice.go index 8646391..63d8ff2 100644 --- a/internal/biz/timeslicequery/retry_query_notice.go +++ b/internal/biz/timeslicequery/retry_query_notice.go @@ -94,13 +94,11 @@ func (v *Query) RetryQueryNoticeOrder(ctx context.Context, req *do.RetryQueryNot return fmt.Errorf("微信券查询order,任务执行失败: %v", err) } - end := time.Now() - logFields := map[string]any{ "num": num, "sucNum": sucNum, "errNum": errNum, - "elapsed": end.Sub(start).String(), + "elapsed": time.Now().Sub(start).String(), } log.Warnf("微信券查询order,处理完毕:%+v", logFields) @@ -131,6 +129,8 @@ func (v *Query) retryQueryNoticeOrder(ctx context.Context, order *bo.OrderBo) er return v.querySuccess(ctx, order) } else if status.IsExpired() { return v.queryExpired(ctx, order) + } else { + log.Warnf("微信券查询order,未知状态orderNo:%s,status:%d", order.OrderNo, status) } return nil @@ -203,13 +203,11 @@ func (v *Query) RetryQueryNoticeOrderBak(ctx context.Context, req *do.RetryQuery return fmt.Errorf("微信券查询orderBak,任务执行失败: %v", err2) } - end := time.Now() - logFields := map[string]any{ "num": num, "sucNum": sucNum, "errNum": errNum, - "elapsed": end.Sub(start).String(), + "elapsed": time.Now().Sub(start).String(), } log.Warnf("微信券查询orderBak,处理完毕:%+v", logFields) @@ -240,6 +238,8 @@ func (v *Query) retryQueryNoticeOrderBal(ctx context.Context, order *bo.OrderBo) return v.querySuccessBak(ctx, order) } else if status.IsExpired() { return v.queryExpiredBak(ctx, order) + } else { + log.Warnf("微信券查询orderBak,未知状态orderNo:%s,status:%d", order.OrderNo, status) } return nil From 9bd8646e57ec8e220cb473ba1ef5d3335d94253e Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 5 Mar 2026 18:05:05 +0800 Subject: [PATCH 081/144] query order notify --- internal/server/http.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/server/http.go b/internal/server/http.go index ca5ce10..0941fe6 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -42,6 +42,7 @@ func NewHTTPServer( // 订单通知重试 -- 不健全 srv.Route("/voucher/").POST("orderNotifyRetry", cmb.OrderNotifyRetry) + // 重试订单通知,查询微信状态再通知下游招行 srv.Route("/voucher/").POST("retryOrderNotice", cmb.RetryOrderNotice) // 重试通知 srv.Route("/voucher/").POST("notifyRetry/{id}", cmb.NotifyRetry) From bbfc91da57790c5a7fcb91a28c06b419b98f0af3 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 13 Mar 2026 15:37:51 +0800 Subject: [PATCH 082/144] =?UTF-8?q?=E5=A4=9A=E7=AC=94=E7=AB=8B=E5=87=8F?= =?UTF-8?q?=E9=87=91=EF=BC=8C=E9=94=99=E8=AF=AF=E6=98=A0=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api/v1/cmb_cpn.proto | 2 + internal/biz/businesserr/cmb.go | 60 ++++ .../cpn_code.go => biz/businesserr/cpn.go} | 310 +++++++++++------- internal/biz/businesserr/cpn_multi.go | 197 +++++++++++ internal/biz/businesserr/err.go | 80 +++++ internal/biz/cmb/notify.go | 13 +- internal/biz/order.go | 29 +- internal/biz/repo/order.go | 2 +- internal/biz/wechat_notify.go | 2 +- internal/data/repoimpl/order.go | 4 +- .../wechatrepoimpl/bank_multi_activity.go | 20 +- internal/data/wechatrepoimpl/cpn.go | 92 +++++- internal/data/wechatrepoimpl/cpn_code_test.go | 38 --- internal/pkg/helper/sort_struct.go | 53 +++ .../{biz/bo => pkg/supplier/qixing}/qixing.go | 6 +- internal/pkg/supplier/qixing/query.go | 59 ++++ internal/service/order.go | 57 ++-- internal/service/qixing.go | 7 +- test/bank_multi_activity_test.go | 32 +- test/cmb_test.go | 166 ++++++++++ test/coupon.go | 34 +- test/coupon_test.go | 56 +++- 22 files changed, 1076 insertions(+), 243 deletions(-) create mode 100644 internal/biz/businesserr/cmb.go rename internal/{data/wechatrepoimpl/cpn_code.go => biz/businesserr/cpn.go} (50%) create mode 100644 internal/biz/businesserr/cpn_multi.go create mode 100644 internal/biz/businesserr/err.go create mode 100644 internal/pkg/helper/sort_struct.go rename internal/{biz/bo => pkg/supplier/qixing}/qixing.go (88%) create mode 100644 internal/pkg/supplier/qixing/query.go create mode 100644 test/cmb_test.go diff --git a/api/v1/cmb_cpn.proto b/api/v1/cmb_cpn.proto index 444411f..5ca44e6 100644 --- a/api/v1/cmb_cpn.proto +++ b/api/v1/cmb_cpn.proto @@ -209,6 +209,8 @@ message CmbNotifyRequest { // 扩展字段 string ext = 13 [json_name = "ext"]; string attach = 14 [json_name = "attach"]; + // 招行唯一流水号 + string transactionId = 15 [json_name = "transactionId"]; } message CmbNotifyReply { diff --git a/internal/biz/businesserr/cmb.go b/internal/biz/businesserr/cmb.go new file mode 100644 index 0000000..f84460d --- /dev/null +++ b/internal/biz/businesserr/cmb.go @@ -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, + }, + }, +} diff --git a/internal/data/wechatrepoimpl/cpn_code.go b/internal/biz/businesserr/cpn.go similarity index 50% rename from internal/data/wechatrepoimpl/cpn_code.go rename to internal/biz/businesserr/cpn.go index 4dfe297..06eeb41 100644 --- a/internal/data/wechatrepoimpl/cpn_code.go +++ b/internal/biz/businesserr/cpn.go @@ -1,19 +1,18 @@ -package wechatrepoimpl +package businesserr import ( "fmt" - "strings" - "github.com/go-kratos/kratos/v2/errors" + "strings" 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 ( + SYSTEM_ERROR ErrCode = "SYSTEM_ERROR" + SIGN_ERROR ErrCode = "SIGN_ERROR" + APPID_MCHID_NOT_MATCH ErrCode = "APPID_MCHID_NOT_MATCH" INVALID_REQUEST ErrCode = "INVALID_REQUEST" PARAM_ERROR ErrCode = "PARAM_ERROR" @@ -26,196 +25,255 @@ const ( FREQUENCY_LIMITED ErrCode = "FREQUENCY_LIMITED" ) -// APIError 定义 API 错误结构体 -type APIError struct { - StatusCode int `json:"status_code"` - ErrorCode ErrCode `json:"error_code"` - Description string `json:"description"` - Hint string `json:"hint"` -} +const ( + BATCH_NOT_STARTED ErrCode = "BATCH_NOT_STARTED" + BATCH_ENDED ErrCode = "BATCH_ENDED" +) + +// 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: { { - StatusCode: 400, - ErrorCode: APPID_MCHID_NOT_MATCH, - Description: "商户号与AppID不匹配", - Hint: "调用接口的商户号需与接口传入的AppID有绑定关系,请参考常见问题Q4", + StatusCode: 400, + ErrorCode: APPID_MCHID_NOT_MATCH, + Description: "商户号与AppID不匹配", + Hint: "调用接口的商户号需与接口传入的AppID有绑定关系,请参考常见问题Q4", + ThirdErrCode: ThirdErrCodeDefault, }, }, INVALID_REQUEST: { { - StatusCode: 400, - ErrorCode: INVALID_REQUEST, - Description: "OpenID与AppID不匹配", - Hint: "OpenID与AppID需有对应关系", + StatusCode: 400, + ErrorCode: INVALID_REQUEST, + Description: "HTTP 请求不符合微信支付 APIv3 接口规则", + Hint: "请参阅 接口规则", + ThirdErrCode: ThirdErrCodeDefault, }, { - StatusCode: 400, - ErrorCode: INVALID_REQUEST, - Description: "非法的商户号", - Hint: "请检查商户号准确性", + StatusCode: 400, + ErrorCode: INVALID_REQUEST, + Description: "OpenID与AppID不匹配", + Hint: "OpenID与AppID需有对应关系", + ThirdErrCode: ThirdErrCodeAppIDOpenIDMismatch, }, { - StatusCode: 400, - ErrorCode: INVALID_REQUEST, - Description: "调用频率过高", - Hint: "请降低API调用频率", + StatusCode: 400, + ErrorCode: INVALID_REQUEST, + Description: "非法的商户号", + Hint: "请检查商户号准确性", + ThirdErrCode: ThirdErrCodeDefault, }, { - StatusCode: 400, - ErrorCode: INVALID_REQUEST, - Description: "活动已结束或未激活", - Hint: "请检查批次状态", + StatusCode: 400, + ErrorCode: INVALID_REQUEST, + Description: "调用频率过高", + Hint: "请降低API调用频率", + ThirdErrCode: ThirdErrCodeCallHigh, }, { - StatusCode: 400, - ErrorCode: INVALID_REQUEST, - Description: "批次信息获取失败,请确认参数是否有误", - Hint: "请检查创建商户号与批次号的对应关系", + StatusCode: 400, + ErrorCode: INVALID_REQUEST, + Description: "活动已结束或未激活", + Hint: "请检查批次状态", + ThirdErrCode: ThirdErrCodeBatchEnded, //前置判断时间处理一下 + }, + { + StatusCode: 400, + ErrorCode: INVALID_REQUEST, + Description: "批次信息获取失败,请确认参数是否有误", + Hint: "请检查创建商户号与批次号的对应关系", + ThirdErrCode: ThirdErrCodeDefault, }, }, PARAM_ERROR: { { - StatusCode: 400, - ErrorCode: PARAM_ERROR, - Description: "AppID必填", - Hint: "请输入AppID", + StatusCode: 400, + ErrorCode: PARAM_ERROR, + Description: "参数错误", + Hint: "请根据错误提示正确传入参数", + ThirdErrCode: ThirdErrCodeDefault, + }, + + { + StatusCode: 400, + ErrorCode: PARAM_ERROR, + Description: "AppID必填", + Hint: "请输入AppID", + ThirdErrCode: ThirdErrCodeDefault, }, { - StatusCode: 400, - ErrorCode: PARAM_ERROR, - Description: "OpenID必填", - Hint: "请输入OpenID", + StatusCode: 400, + ErrorCode: PARAM_ERROR, + Description: "OpenID必填", + Hint: "请输入OpenID", + ThirdErrCode: ThirdErrCodeDefault, }, { - StatusCode: 400, - ErrorCode: PARAM_ERROR, - Description: "批次号必填", - Hint: "请输入批次号", + StatusCode: 400, + ErrorCode: PARAM_ERROR, + Description: "批次号必填", + Hint: "请输入批次号", + ThirdErrCode: ThirdErrCodeDefault, }, { - StatusCode: 400, - ErrorCode: PARAM_ERROR, - Description: "商户号必填", - Hint: "请输入商户号", + StatusCode: 400, + ErrorCode: PARAM_ERROR, + Description: "商户号必填", + Hint: "请输入商户号", + ThirdErrCode: ThirdErrCodeDefault, }, { - StatusCode: 400, - ErrorCode: PARAM_ERROR, - Description: "非法的批次状态", - Hint: "请检查批次状态,仅支持发放状态为“运营中”的代金券批次", + StatusCode: 400, + ErrorCode: PARAM_ERROR, + Description: "非法的批次状态", + Hint: "请检查批次状态,仅支持发放状态为“运营中”的代金券批次", + ThirdErrCode: ThirdErrCodeDefault, }, }, MCH_NOT_EXISTS: { { - StatusCode: 403, - ErrorCode: MCH_NOT_EXISTS, - Description: "商户号不合法", - Hint: "请检查商户号准确性", + StatusCode: 403, + ErrorCode: MCH_NOT_EXISTS, + Description: "商户号不合法", + Hint: "请检查商户号准确性", + ThirdErrCode: ThirdErrCodeDefault, }, }, NOT_ENOUGH: { { - StatusCode: 403, - ErrorCode: NOT_ENOUGH, - Description: "批次预算不足", - Hint: "批次预算已发放完,请补充批次预算", + StatusCode: 403, + ErrorCode: NOT_ENOUGH, + Description: "批次预算不足", + Hint: "批次预算已发放完,请补充批次预算", + ThirdErrCode: ThirdErrCodeAdvanceFundingNotEnough, }, { - StatusCode: 403, - ErrorCode: NOT_ENOUGH, - Description: "发券超过单天限额", - Hint: "已超过该批次设置的单天发放限制额度,无法发放", + StatusCode: 403, + ErrorCode: NOT_ENOUGH, + Description: "发券超过单天限额", + Hint: "已超过该批次设置的单天发放限制额度,无法发放", + ThirdErrCode: ThirdErrCodeDailyLimit, }, { - StatusCode: 403, - ErrorCode: NOT_ENOUGH, - Description: "账户余额不足,请充值", - Hint: "商户号余额不足,无法继续发券,请充值", + StatusCode: 403, + ErrorCode: NOT_ENOUGH, + Description: "账户余额不足,请充值", + Hint: "商户号余额不足,无法继续发券,请充值", + ThirdErrCode: ThirdErrCodeAdvanceFundingNotEnough, }, { - StatusCode: 403, - ErrorCode: NOT_ENOUGH, - Description: "批次预算耗尽", - Hint: "该批次的预算已经耗尽", + StatusCode: 403, + ErrorCode: NOT_ENOUGH, + Description: "批次预算耗尽", + Hint: "该批次的预算已经耗尽", + ThirdErrCode: ThirdErrCodeAdvanceFundingNotEnough, }, }, REQUEST_BLOCKED: { { - StatusCode: 403, - ErrorCode: REQUEST_BLOCKED, - Description: "商户无权发券", - Hint: "该批次不支持其他商户发放,请参考常见问题Q1", + StatusCode: 403, + ErrorCode: REQUEST_BLOCKED, + Description: "商户无权发券", + Hint: "该批次不支持其他商户发放,请参考常见问题Q1", + ThirdErrCode: ThirdErrCodeDefault, }, { - StatusCode: 403, - ErrorCode: REQUEST_BLOCKED, - Description: "批次不支持跨商户发券", - Hint: "该批次不支持其他商户发放,请参考常见问题Q1", + StatusCode: 403, + ErrorCode: REQUEST_BLOCKED, + Description: "批次不支持跨商户发券", + Hint: "该批次不支持其他商户发放,请参考常见问题Q1", + ThirdErrCode: ThirdErrCodeDefault, }, { - StatusCode: 403, - ErrorCode: REQUEST_BLOCKED, - Description: "用户被限领拦截", - Hint: "该用户已达到该批次的领取上限,请参考常见问题Q6", + StatusCode: 403, + ErrorCode: REQUEST_BLOCKED, + Description: "用户被限领拦截", + Hint: "该用户已达到该批次的领取上限,请参考常见问题Q6", + ThirdErrCode: ThirdErrCodeUserParticipationExceeded, }, { - StatusCode: 403, - ErrorCode: REQUEST_BLOCKED, - Description: "不能在API渠道发放", - Hint: "请检查批次信息,仅支持发放微信支付代金券,不支持发放立减与折扣", + StatusCode: 403, + ErrorCode: REQUEST_BLOCKED, + Description: "不能在API渠道发放", + Hint: "请检查批次信息,仅支持发放微信支付代金券,不支持发放立减与折扣", + ThirdErrCode: ThirdErrCodeDefault, }, { - StatusCode: 403, - ErrorCode: REQUEST_BLOCKED, - Description: "不支持指定面额发券", - Hint: "仅在发券时指定面额及门槛的场景才生效,常规发券场景请勿传入该信息", + StatusCode: 403, + ErrorCode: REQUEST_BLOCKED, + Description: "不支持指定面额发券", + Hint: "仅在发券时指定面额及门槛的场景才生效,常规发券场景请勿传入该信息", + ThirdErrCode: ThirdErrCodeDefault, }, { - StatusCode: 403, - ErrorCode: REQUEST_BLOCKED, - Description: "仅在广告场景下发放批次", - Hint: "该批次已在朋友圈广告发放,不支持在其他渠道发放", + StatusCode: 403, + ErrorCode: REQUEST_BLOCKED, + Description: "仅在广告场景下发放批次", + Hint: "该批次已在朋友圈广告发放,不支持在其他渠道发放", + ThirdErrCode: ThirdErrCodeDefault, }, }, RULE_LIMIT: { { - StatusCode: 403, - ErrorCode: RULE_LIMIT, - Description: "用户已达最大领券次数", - Hint: "该用户已达到该批次的领取上限,请参考常见问题Q6", + StatusCode: 403, + ErrorCode: RULE_LIMIT, + Description: "用户已达最大领券次数", + Hint: "该用户已达到该批次的领取上限,请参考常见问题Q6", + ThirdErrCode: ThirdErrCodeUserParticipationExceeded, }, { - StatusCode: 403, - ErrorCode: RULE_LIMIT, - Description: "被自然人规则拦截", - Hint: "该自然人已达到该批次的领取上限,请参考常见问题Q6", + StatusCode: 403, + ErrorCode: RULE_LIMIT, + Description: "被自然人规则拦截", + Hint: "该自然人已达到该批次的领取上限,请参考常见问题Q6", + ThirdErrCode: ThirdErrCodeUserAccountAbnormal, }, }, USER_ACCOUNT_ABNORMAL: { { - StatusCode: 403, - ErrorCode: USER_ACCOUNT_ABNORMAL, - Description: "用户非法", - Hint: "用户命中微信支付风控模型,请参考常见问题Q5", + StatusCode: 403, + ErrorCode: USER_ACCOUNT_ABNORMAL, + Description: "用户非法", + Hint: "用户命中微信支付风控模型,请参考常见问题Q5", + ThirdErrCode: ThirdErrCodeUserAccountFrozen, }, }, RESOURCE_NOT_EXISTS: { { - StatusCode: 404, - ErrorCode: RESOURCE_NOT_EXISTS, - Description: "批次不存在", - Hint: "请检查批次及制券商户号信息", + StatusCode: 404, + ErrorCode: RESOURCE_NOT_EXISTS, + Description: "批次不存在", + Hint: "请检查批次及制券商户号信息", + ThirdErrCode: ThirdErrCodeDefault, }, }, FREQUENCY_LIMITED: { { - StatusCode: 429, - ErrorCode: FREQUENCY_LIMITED, - Description: "当前请求人数过多,请稍后重试", - Hint: "请降低API调用频率", + StatusCode: 429, + ErrorCode: FREQUENCY_LIMITED, + Description: "当前请求人数过多,请稍后重试", + Hint: "请降低API调用频率", + ThirdErrCode: ThirdErrCodeCallHigh, }, }, } @@ -284,13 +342,13 @@ var WechatError = map[string]*errors.Error{ ErrorWechatAccountFail: err2.ErrorWechatAccountFail(ErrorWechatAccountFail), } -type ErrBody struct { +type WechatErrBody struct { Code ErrCode `json:"Code"` Message string `json:"Message"` } // GetWechatError 根据错误描述获取具体的错误处理 -func (s ErrBody) GetWechatError() *errors.Error { +func (s WechatErrBody) GetWechatError() *errors.Error { lowerDesc := strings.ToLower(s.Message) for desc, err := range WechatError { if strings.ToLower(desc) == lowerDesc { diff --git a/internal/biz/businesserr/cpn_multi.go b/internal/biz/businesserr/cpn_multi.go new file mode 100644 index 0000000..182f5ac --- /dev/null +++ b/internal/biz/businesserr/cpn_multi.go @@ -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, + }, + }, +} diff --git a/internal/biz/businesserr/err.go b/internal/biz/businesserr/err.go new file mode 100644 index 0000000..3bd9943 --- /dev/null +++ b/internal/biz/businesserr/err.go @@ -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, + } +} diff --git a/internal/biz/cmb/notify.go b/internal/biz/cmb/notify.go index 4c7d80e..1a0e54f 100644 --- a/internal/biz/cmb/notify.go +++ b/internal/biz/cmb/notify.go @@ -63,12 +63,13 @@ func (v *Cmb) bizContent(_ context.Context, order *bo.OrderBo, orderNotify *bo.O } req := &v1.CmbNotifyRequest{ - Ticket: orderNotify.OrderNo, - Status: cmbStatus.GetValue(), - TransDate: "", // 格式yyyy-mm-dd hh:mm:ss.sss - OrgNo: v.bc.Cmb.OrgNo, - Attach: order.Attach, - Ext: "", + Ticket: orderNotify.OrderNo, + Status: cmbStatus.GetValue(), + TransDate: "", // 格式yyyy-mm-dd hh:mm:ss.sss + OrgNo: v.bc.Cmb.OrgNo, + Attach: order.Attach, + Ext: "", + TransactionId: order.OutBizNo, // 招行订单号 } if cmbStatus == vo.CmbStatusUse { diff --git a/internal/biz/order.go b/internal/biz/order.go index c00dda2..c6dede3 100644 --- a/internal/biz/order.go +++ b/internal/biz/order.go @@ -3,16 +3,18 @@ package biz import ( "context" "fmt" + "time" err2 "voucher/api/err" "voucher/internal/biz/bo" + "voucher/internal/biz/businesserr" "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) if err3 != nil { - return "", err3 + return nil, err3 } 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 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) 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) 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) { @@ -130,8 +140,9 @@ func (this *VoucherBiz) fail(ctx context.Context, order *bo.OrderBo, errReq erro 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 } diff --git a/internal/biz/repo/order.go b/internal/biz/repo/order.go index 633ff9a..2da57b0 100644 --- a/internal/biz/repo/order.go +++ b/internal/biz/repo/order.go @@ -26,7 +26,7 @@ type OrderRepo interface { Success(ctx context.Context, id uint64, voucherNo string) error Fail(ctx context.Context, id uint64, remark string) 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 MultiOverUsed(ctx context.Context, id uint64, lastUseTime time.Time, remark string) error Available(ctx context.Context, id uint64) error diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index be88e76..877975c 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -84,7 +84,7 @@ func (this *VoucherBiz) notifyUsed(ctx context.Context, order *bo.OrderBo, req * 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 } diff --git a/internal/data/repoimpl/order.go b/internal/data/repoimpl/order.go index 8fa2c54..03a25c7 100644 --- a/internal/data/repoimpl/order.go +++ b/internal/data/repoimpl/order.go @@ -549,7 +549,7 @@ func (p *OrderRepoImpl) MultiOverUsed(ctx context.Context, id uint64, lastUseTim 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() tx := p.DB(ctx). @@ -560,7 +560,7 @@ func (p *OrderRepoImpl) NotifyUsed(ctx context.Context, id uint64, transactionId Status: vo.OrderStatusUse.GetValue(), TransactionId: transactionId, Remark: "微信回调核销", - LastUseTime: &now, + LastUseTime: &lastUseTime, UpdateTime: &now, }) diff --git a/internal/data/wechatrepoimpl/bank_multi_activity.go b/internal/data/wechatrepoimpl/bank_multi_activity.go index 2e0fc0c..0e877d5 100644 --- a/internal/data/wechatrepoimpl/bank_multi_activity.go +++ b/internal/data/wechatrepoimpl/bank_multi_activity.go @@ -1,11 +1,14 @@ package wechatrepoimpl import ( + "encoding/json" "errors" "fmt" + "github.com/go-kratos/kratos/v2/log" "github.com/wechatpay-apiv3/wechatpay-go/core" err2 "voucher/api/err" "voucher/internal/biz/bo" + "voucher/internal/biz/businesserr" "voucher/internal/biz/wechatrepo" "voucher/internal/conf" "voucher/internal/data" @@ -43,11 +46,20 @@ func (w *BankMultiActivityImpl) Order(order *bo.OrderBo) (couponId string, err e var e *utils.ApiException if errors.As(err, &e) { - apiErr, err3 := marketing.BuildErr(e.Body()) - if err3 != nil { - return "", fmt.Errorf("ApiException analysis err: %+v", err3) + // 格式:{"code":"INVALID_REQUEST","message":"对应单号已超出重试期;请查单确认后决定是否换单请求"} + var beer *businesserr.BusinessErr + 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 diff --git a/internal/data/wechatrepoimpl/cpn.go b/internal/data/wechatrepoimpl/cpn.go index 5fd9078..367162c 100644 --- a/internal/data/wechatrepoimpl/cpn.go +++ b/internal/data/wechatrepoimpl/cpn.go @@ -9,14 +9,18 @@ import ( "github.com/wechatpay-apiv3/wechatpay-go/services/cashcoupons" "io" "net/http" + "strings" "time" err2 "voucher/api/err" "voucher/internal/biz/bo" + "voucher/internal/biz/businesserr" "voucher/internal/biz/vo" "voucher/internal/biz/wechatrepo" "voucher/internal/conf" "voucher/internal/data" + "voucher/internal/pkg/helper" "voucher/internal/pkg/request" + "voucher/internal/pkg/supplier/qixing" ) // 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 { + // // 格式:{"code":"INVALID_REQUEST","message":"对应单号已超出重试期;请查单确认后决定是否换单请求"} bodyBytes, err := io.ReadAll(result.Response.Body) if err != nil { return err2.ErrorWechatFAIL(fmt.Sprintf("读取微信错误返回body报错:%s", err.Error())) } - var errBody ErrBody - if err = json.Unmarshal(bodyBytes, &errBody); err != nil { + var beer *businesserr.BusinessErr + if err = json.Unmarshal(bodyBytes, &beer); err != nil { log.Errorf("微信错误返回body解析报错,body:%s,err:%s", string(bodyBytes), 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) { @@ -97,12 +102,85 @@ func (c *CpnRepoImpl) Order(ctx context.Context, order *bo.OrderBo) (string, err 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{ - CouponId: core.String(orderWechat.VoucherNo), - Appid: core.String(orderWechat.AppID), - Openid: core.String(orderWechat.Account), + CouponId: core.String(order.VoucherNo), + Appid: core.String(order.AppID), + Openid: core.String(order.Account), } client, err := c.GetClient(ctx) diff --git a/internal/data/wechatrepoimpl/cpn_code_test.go b/internal/data/wechatrepoimpl/cpn_code_test.go index ef2e8b0..00762b5 100644 --- a/internal/data/wechatrepoimpl/cpn_code_test.go +++ b/internal/data/wechatrepoimpl/cpn_code_test.go @@ -1,39 +1 @@ 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) -} diff --git a/internal/pkg/helper/sort_struct.go b/internal/pkg/helper/sort_struct.go new file mode 100644 index 0000000..7661391 --- /dev/null +++ b/internal/pkg/helper/sort_struct.go @@ -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 +} diff --git a/internal/biz/bo/qixing.go b/internal/pkg/supplier/qixing/qixing.go similarity index 88% rename from internal/biz/bo/qixing.go rename to internal/pkg/supplier/qixing/qixing.go index 6eaf2da..38d0489 100644 --- a/internal/biz/bo/qixing.go +++ b/internal/pkg/supplier/qixing/qixing.go @@ -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 { Content string `json:"content" validate:"required"` diff --git a/internal/pkg/supplier/qixing/query.go b/internal/pkg/supplier/qixing/query.go new file mode 100644 index 0000000..6c6424f --- /dev/null +++ b/internal/pkg/supplier/qixing/query.go @@ -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"` +} diff --git a/internal/service/order.go b/internal/service/order.go index 398076b..148df0f 100644 --- a/internal/service/order.go +++ b/internal/service/order.go @@ -3,29 +3,28 @@ package service import ( "context" "encoding/json" - "github.com/go-kratos/kratos/v2/errors" - err2 "voucher/api/err" v1 "voucher/api/v1" "voucher/internal/biz/bo" + "voucher/internal/biz/businesserr" "voucher/internal/biz/vo" ) 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 { - 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) if err != nil { - return "", err + return nil, err } boReq := &bo.OrderCreateReqBo{ @@ -39,12 +38,12 @@ func (c *CmbService) order(ctx context.Context, request *v1.CmbRequest) (string, NotifyUrl: c.bc.Cmb.NotifyUrl, } - orderNo, err := c.VoucherBiz.CmbOrder(ctx, boReq) + order, err := c.VoucherBiz.CmbOrder(ctx, boReq) 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) { @@ -60,19 +59,37 @@ func (c *CmbService) OrderSuccess(ctx context.Context, orderNo string) (*v1.CmbR 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 { - se.Reason = err2.CmbErr_CMB_UNKNOWN.String() - } + if e, ok := err.(*businesserr.BusinessErr); ok { - bizReply := &v1.CmbOrderReply{ - RespCode: vo.CmbResponseStatusFail.GetValue(), - RespMsg: se.Message, - CodeNo: "", - ThirdErrCode: se.Reason, + 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), + } + } + + } else { + bizReply = &v1.CmbOrderReply{ + RespCode: vo.CmbResponseStatusFail.GetValue(), + RespMsg: err.Error(), + CodeNo: "", + ThirdErrCode: string(businesserr.ThirdErrCodeDefault), + } } replyBizContent, _ := json.Marshal(bizReply) diff --git a/internal/service/qixing.go b/internal/service/qixing.go index 134c9c5..ba36742 100644 --- a/internal/service/qixing.go +++ b/internal/service/qixing.go @@ -12,6 +12,7 @@ import ( "voucher/internal/biz/bo" "voucher/internal/conf" "voucher/internal/pkg/helper" + "voucher/internal/pkg/supplier/qixing" ) 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)) } - var req *bo.QiXingRequestBo + var req *qixing.QiXingRequestBo if err = json.Unmarshal(bodyBytes, &req); err != nil { 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)) @@ -89,13 +90,13 @@ func (srv *TripartiteService) QiXingNotify(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", }) } 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, }) } diff --git a/test/bank_multi_activity_test.go b/test/bank_multi_activity_test.go index 057fcca..d8bcbc6 100644 --- a/test/bank_multi_activity_test.go +++ b/test/bank_multi_activity_test.go @@ -3,10 +3,13 @@ package test import ( "encoding/base64" "encoding/json" + "errors" "testing" "time" - "voucher/internal/biz/bo" + "voucher/internal/biz/businesserr" "voucher/internal/pkg/helper" + "voucher/internal/pkg/supplier/qixing" + "voucher/internal/pkg/wechat/utils" ) func Test_MarketingSend(t *testing.T) { @@ -25,22 +28,39 @@ func Test_MarketingSend(t *testing.T) { } func Test_MarketingQuery(t *testing.T) { + + appId := "wx619991cc795028f5" + openId := "oSNb4fixFZ4uRvcP6F25_FySgUE0" + couponId := "147079366189" + tests := []struct { name string appId, openId, couponId string }{ { name: "查询绑定多笔立减活动的代金券详情", // 查询的商户非创建方商户 查询商户要为创建方商户 - appId: "wx619991cc795028f5", - openId: "oSNb4ftgnWC22Z0cWTjsQebdr2Yk", - couponId: "139923450432", + appId: appId, + openId: openId, + couponId: couponId, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { resp, err := MarketingQuery(tt.appId, tt.openId, tt.couponId) if err != nil { - t.Errorf("MarketingQuery() error = %v", err) + + 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) + } + return } t.Logf("MarketingQuery() = %v", resp) @@ -78,7 +98,7 @@ func Test_QixingNotifyData(t *testing.T) { ciphertext := helper.Md5(content + "DrY1zLkOH01q0sN66vrmkdpbWsyb41ho") - req := bo.QiXingRequestBo{ + req := qixing.QiXingRequestBo{ Content: content, Timestamp: time.Now().UnixMilli(), Ciphertext: ciphertext, diff --git a/test/cmb_test.go b/test/cmb_test.go new file mode 100644 index 0000000..0181fdc --- /dev/null +++ b/test/cmb_test.go @@ -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) +} diff --git a/test/coupon.go b/test/coupon.go index f63f57e..2cc88a0 100644 --- a/test/coupon.go +++ b/test/coupon.go @@ -10,6 +10,7 @@ import ( "os" "path/filepath" "time" + "voucher/internal/biz/businesserr" "voucher/internal/biz/do" "voucher/internal/conf" "voucher/internal/data" @@ -33,14 +34,13 @@ var bc2 = &conf.Bootstrap{ }, } -func SendCoupon() { +func SendCoupon() error { ctx := context.Background() dir, err := os.Getwd() if err != nil { - fmt.Printf("os.Getwd() error = %v", err) - return + return fmt.Errorf("os.Getwd() error = %v", err) } parentDir := filepath.Dir(dir) @@ -52,8 +52,7 @@ func SendCoupon() { } client, err := data.GetClient(ctx, server) if err != nil { - fmt.Println(err) - return + return fmt.Errorf("data.GetClient(ctx, server) error = %v", err) } req := cashcoupons.SendCouponRequest{ @@ -62,7 +61,7 @@ func SendCoupon() { Appid: core.String("wxd27e255810842ba8"), Openid: core.String("o3dEt5b_1lFtKc-aAT3tiYjJIGwk"), StockId: core.String("21502886"), - StockCreatorMchid: core.String("1652465541"), + StockCreatorMchid: core.String("16524655411111"), } fmt.Printf("\nreq:%+v", req) @@ -71,15 +70,26 @@ func SendCoupon() { resp, result, err := svc.SendCoupon(ctx, req) 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) - return + return nil } -func QueryCoupon() { +func QueryCoupon(appId, openId, couponId string) { ctx := context.Background() @@ -102,10 +112,6 @@ func QueryCoupon() { return } - appId := "wx619991cc795028f5" - openId := "oSNb4fnWoktz7YVNIXE5bvoB3-1w" - couponId := "106048490308" - req := cashcoupons.QueryCouponRequest{ CouponId: core.String(couponId), Appid: core.String(appId), diff --git a/test/coupon_test.go b/test/coupon_test.go index 61daa6f..2dfe81f 100644 --- a/test/coupon_test.go +++ b/test/coupon_test.go @@ -2,6 +2,7 @@ package test import ( "testing" + "voucher/internal/biz/businesserr" ) func Test_SendCoupon(t *testing.T) { @@ -14,22 +15,38 @@ func Test_SendCoupon(t *testing.T) { } for _, tt := range tests { 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) + } + } }) } } func Test_QueryCoupon(t *testing.T) { 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 { 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) +} From 81c53823af810ba68bf7060499de94577f88f77c Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 13 Mar 2026 15:55:41 +0800 Subject: [PATCH 083/144] =?UTF-8?q?=E9=94=99=E8=AF=AF=E6=98=A0=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/order.go | 18 +++++++++--------- internal/service/order.go | 26 +++++++++++++++++--------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/internal/biz/order.go b/internal/biz/order.go index c6dede3..1d8c3c3 100644 --- a/internal/biz/order.go +++ b/internal/biz/order.go @@ -10,11 +10,11 @@ import ( "voucher/internal/biz/vo" ) -func (this *VoucherBiz) CmbOrder(ctx context.Context, req *bo.OrderCreateReqBo) (*bo.OrderBo, error) { +func (this *VoucherBiz) CmbOrder(ctx context.Context, req *bo.OrderCreateReqBo) (*bo.OrderBo, *bo.ProductBo, error) { order, err3 := this.GetByOutBizNo(ctx, req) if err3 != nil { - return nil, err3 + return nil, nil, err3 } if order != nil { @@ -22,32 +22,32 @@ func (this *VoucherBiz) CmbOrder(ctx context.Context, req *bo.OrderCreateReqBo) if order.Status.IsFail() || order.Status.IsIng() { if err4 := this.orderRetry(ctx, order); err4 != nil { - return nil, err4 + return order, nil, err4 } } - return order, nil + return order, nil, nil } product, err3 := this.ProductRepo.GetByProductNo(ctx, req.ProductNo) if err3 != nil { - return nil, err3 + return nil, product, err3 } nowTime := time.Now() if nowTime.Before(*product.StartTime) { - return nil, businesserr.BatchNotStartedError + return nil, product, businesserr.BatchNotStartedError } if nowTime.After(*product.EndTime) { - return nil, businesserr.BatchEndedError + return nil, product, businesserr.BatchEndedError } order, err3 = this.order(ctx, req, product) if err3 != nil { - return nil, err3 + return nil, product, err3 } - return order, nil + return order, product, nil } func (this *VoucherBiz) order(ctx context.Context, req *bo.OrderCreateReqBo, product *bo.ProductBo) (*bo.OrderBo, error) { diff --git a/internal/service/order.go b/internal/service/order.go index 148df0f..81d33a5 100644 --- a/internal/service/order.go +++ b/internal/service/order.go @@ -11,20 +11,20 @@ import ( func (c *CmbService) Order(ctx context.Context, request *v1.CmbRequest) (*v1.CmbReply, error) { - order, err := c.order(ctx, request) + order, product, err := c.order(ctx, request) if err != nil { - return c.OrderFail(ctx, order, err) + return c.OrderFail(ctx, order, product, err) } return c.OrderSuccess(ctx, order.OrderNo) } -func (c *CmbService) order(ctx context.Context, request *v1.CmbRequest) (*bo.OrderBo, error) { +func (c *CmbService) order(ctx context.Context, request *v1.CmbRequest) (*bo.OrderBo, *bo.ProductBo, error) { bizContent, err := c.CmbMixRepo.OrderVerify(ctx, request) if err != nil { - return nil, err + return nil, nil, err } boReq := &bo.OrderCreateReqBo{ @@ -38,12 +38,12 @@ func (c *CmbService) order(ctx context.Context, request *v1.CmbRequest) (*bo.Ord NotifyUrl: c.bc.Cmb.NotifyUrl, } - order, err := c.VoucherBiz.CmbOrder(ctx, boReq) + order, product, err := c.VoucherBiz.CmbOrder(ctx, boReq) if err != nil { - return nil, err + return nil, nil, err } - return order, nil + return order, product, nil } func (c *CmbService) OrderSuccess(ctx context.Context, orderNo string) (*v1.CmbReply, error) { @@ -59,13 +59,21 @@ func (c *CmbService) OrderSuccess(ctx context.Context, orderNo string) (*v1.CmbR return c.GetResponse(ctx, replyBizContent) } -func (c *CmbService) OrderFail(ctx context.Context, order *bo.OrderBo, err error) (*v1.CmbReply, error) { +func (c *CmbService) OrderFail(ctx context.Context, order *bo.OrderBo, product *bo.ProductBo, err error) (*v1.CmbReply, error) { bizReply := &v1.CmbOrderReply{} if e, ok := err.(*businesserr.BusinessErr); ok { - if order.ActivityId != "" { + isMultiErr := false + + if product != nil && product.ActivityId != "" { + isMultiErr = true + } else if order != nil && order.ActivityId != "" { + isMultiErr = true + } + + if isMultiErr { cmbAPIError := e.HandleMULTIThirdErrCode() bizReply = &v1.CmbOrderReply{ RespCode: vo.CmbResponseStatusFail.GetValue(), From 07bacd006bf4d827cca2d38ea039f31d21eb1498 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 13 Mar 2026 16:05:06 +0800 Subject: [PATCH 084/144] =?UTF-8?q?=E9=94=99=E8=AF=AF=E6=98=A0=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/repo/order.go | 4 +- internal/data/repoimpl/order.go | 72 ++++++++++++++++----------------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/internal/biz/repo/order.go b/internal/biz/repo/order.go index 2da57b0..1b4a592 100644 --- a/internal/biz/repo/order.go +++ b/internal/biz/repo/order.go @@ -12,8 +12,8 @@ type OrderRepo interface { FinUsedInBatches(ctx context.Context, req *do.WechatUsedQuery, fun func(ctx context.Context, rows []*bo.OrderBo) error) error SpecifyFindInBatches(ctx context.Context, w *bo.FindInBatchesBo, fun func(ctx context.Context, rows []*bo.OrderBo) error) error FinSucByStockIdInBatches(ctx context.Context, req *do.WechatQuery, fun func(ctx context.Context, rows []*bo.OrderBo) error) error - FinFailByStockIdInBatches(ctx context.Context, batchNo string, fun func(ctx context.Context, rows []*bo.OrderBo) error) error - FindIngInBatches(ctx context.Context, fun func(ctx context.Context, rows []*bo.OrderBo) error) error + //FinFailByStockIdInBatches(ctx context.Context, batchNo string, fun func(ctx context.Context, rows []*bo.OrderBo) error) error + //FindIngInBatches(ctx context.Context, fun func(ctx context.Context, rows []*bo.OrderBo) error) error FindInBatches(ctx context.Context, w *bo.FindInBatchesUseBo, fun func(ctx context.Context, rows []*bo.OrderBo) error) error FindRetryQuery(ctx context.Context, req *do.RetryQueryNotice, fun func(ctx context.Context, rows []*bo.OrderBo) error) error GetByOutBizNo(ctx context.Context, t vo.OrderType, outBizNo string) (*bo.OrderBo, error) diff --git a/internal/data/repoimpl/order.go b/internal/data/repoimpl/order.go index 03a25c7..93fca53 100644 --- a/internal/data/repoimpl/order.go +++ b/internal/data/repoimpl/order.go @@ -145,43 +145,43 @@ func (p *OrderRepoImpl) FinUsedInBatches(ctx context.Context, req *do.WechatUsed return nil } -func (p *OrderRepoImpl) FinFailByStockIdInBatches(ctx context.Context, batchNo string, fun func(ctx context.Context, rows []*bo.OrderBo) error) error { +//func (p *OrderRepoImpl) FinFailByStockIdInBatches(ctx context.Context, batchNo string, fun func(ctx context.Context, rows []*bo.OrderBo) error) error { +// +// var results = make([]*model.Order, 0) +// +// result := p.DB(ctx). +// Where("batch_no = ?", batchNo). +// Where("`status` = ?", vo.OrderStatusFail.GetValue()). +// Order("receive_success_time asc"). // 显式清除排序,移除默认的 ORDER BY +// FindInBatches(&results, 200, func(tx *gorm.DB, batch int) error { +// return fun(ctx, p.ToBos(results)) +// }) +// +// if result.Error != nil { +// return result.Error +// } +// +// return nil +//} - var results = make([]*model.Order, 0) - - result := p.DB(ctx). - Where("batch_no = ?", batchNo). - Where("`status` = ?", vo.OrderStatusFail.GetValue()). - Order("receive_success_time asc"). // 显式清除排序,移除默认的 ORDER BY - FindInBatches(&results, 200, func(tx *gorm.DB, batch int) error { - return fun(ctx, p.ToBos(results)) - }) - - if result.Error != nil { - return result.Error - } - - return nil -} - -func (p *OrderRepoImpl) FindIngInBatches(ctx context.Context, fun func(ctx context.Context, rows []*bo.OrderBo) error) error { - - var results = make([]*model.Order, 0) - - result := p.DB(ctx). - Where("`status` = ?", vo.OrderStatusIng.GetValue()). - Limit(20). - Order("receive_success_time asc"). // 显式清除排序,移除默认的 ORDER BY - FindInBatches(&results, 10, func(tx *gorm.DB, batch int) error { - return fun(ctx, p.ToBos(results)) - }) - - if result.Error != nil { - return result.Error - } - - return nil -} +//func (p *OrderRepoImpl) FindIngInBatches(ctx context.Context, fun func(ctx context.Context, rows []*bo.OrderBo) error) error { +// +// var results = make([]*model.Order, 0) +// +// result := p.DB(ctx). +// Where("`status` = ?", vo.OrderStatusIng.GetValue()). +// Limit(20). +// Order("receive_success_time asc"). // 显式清除排序,移除默认的 ORDER BY +// FindInBatches(&results, 10, func(tx *gorm.DB, batch int) error { +// return fun(ctx, p.ToBos(results)) +// }) +// +// if result.Error != nil { +// return result.Error +// } +// +// return nil +//} func (p *OrderRepoImpl) FindInBatches(ctx context.Context, req *bo.FindInBatchesUseBo, fun func(ctx context.Context, rows []*bo.OrderBo) error) error { From c6028b8c6e43c3bea77a2f8c8c2761f6ed5b54d0 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 13 Mar 2026 16:13:09 +0800 Subject: [PATCH 085/144] =?UTF-8?q?=E9=94=99=E8=AF=AF=E6=98=A0=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/data/repoimpl/order.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/internal/data/repoimpl/order.go b/internal/data/repoimpl/order.go index 93fca53..05f1b8c 100644 --- a/internal/data/repoimpl/order.go +++ b/internal/data/repoimpl/order.go @@ -74,8 +74,11 @@ func (p *OrderRepoImpl) SpecifyFindInBatches(ctx context.Context, req *bo.FindIn func (p *OrderRepoImpl) FinSucByStockIdInBatches(ctx context.Context, req *do.WechatQuery, fun func(ctx context.Context, rows []*bo.OrderBo) error) error { tx := p.DB(ctx). - Where("`status` in (?)", []uint8{vo.OrderStatusSuccess.GetValue(), vo.OrderStatusUse.GetValue(), vo.OrderStatusExpired.GetValue()}). - Where("activity_id = ''") + Where("`status` in (?)", []uint8{ + vo.OrderStatusSuccess.GetValue(), + vo.OrderStatusUse.GetValue(), + vo.OrderStatusExpired.GetValue(), + }).Where("activity_id = ''") if req.ProductNo != "" { tx = tx.Where("product_no = ?", req.ProductNo) From 0ddde21287c7931f53a61677a592d39a487a7599 Mon Sep 17 00:00:00 2001 From: ziming Date: Wed, 25 Mar 2026 14:08:29 +0800 Subject: [PATCH 086/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- configs/config.yaml | 7 + internal/biz/multi.go | 52 +- internal/biz/provider_set.go | 2 +- internal/biz/wechat.go | 26 + internal/biz/wechat_notify.go | 4 +- .../biz/wechatrepo/bank_multi_activity.go | 3 + internal/conf/conf.pb.go | 700 ++++++++++-------- internal/conf/conf.proto | 9 + .../wechatrepoimpl/bank_multi_activity.go | 22 + internal/data/wx.go | 36 + .../pkg/wechat/srv/marketing/marketing.go | 17 +- internal/pkg/wechat/utils/aes.go | 87 +++ internal/pkg/wechat/utils/wxpay_utility.go | 33 + .../pkg/wechat/utils/wxpay_utility_test.go | 68 ++ internal/server/http.go | 6 + internal/service/notify.go | 79 ++ internal/service/provider_set.go | 1 + internal/service/voucher.go | 2 +- test/bank_multi_activity.go | 33 + test/bank_multi_activity_test.go | 67 ++ test/coupon.go | 59 +- test/coupon_test.go | 18 +- 22 files changed, 980 insertions(+), 351 deletions(-) create mode 100644 internal/biz/wechat.go create mode 100644 internal/pkg/wechat/utils/aes.go create mode 100644 internal/pkg/wechat/utils/wxpay_utility_test.go create mode 100644 internal/service/notify.go diff --git a/configs/config.yaml b/configs/config.yaml index 6c00b25..cca15ea 100644 --- a/configs/config.yaml +++ b/configs/config.yaml @@ -53,6 +53,13 @@ wechat: mchCertificateSerialNumber: "6006B8208815DB5EAC5BF2E783CB9D34082C3772" #商户证书序列号 wechatPayPublicKeyID: "PUB_KEY_ID_0117109533612025031800326400002563" #微信支付公钥ID +wechatSubject: + - mchID: "1100040695" # 证书所属商户 + name: "福建峦峰网络科技有限公司" + mchApiV3Key: "d9af70585b18ae206d981548c766563f" + mchCertificateSerialNumber: "46712853869DB0EDAA9B4DF97DADEECD4CCDC85B" #商户证书序列号 + wechatPayPublicKeyID: "PUB_KEY_ID_0111000406952026032500382251001000" #微信支付公钥ID,有它则为新的“公私钥”模式 + cmb: sm2Prk: "8d39ff3d2559258c163f4510f082727f51531e1953ab203d5ab1ea4a6d94fd73" sm2Puk: "04d827a7dbaaa358ce45b8c7794a7f54819f5c175005a702370e47f135ef6f5f9732758b1474f218419fe9e87f90c28c3b05f08254c651db27df35fae67b77b2e4" # 公钥,给到招行密钥 diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 21d7611..9869e38 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -7,8 +7,6 @@ import ( "fmt" "github.com/go-kratos/kratos/v2/log" "gorm.io/gorm" - "sync" - err2 "voucher/api/err" v1 "voucher/api/v1" "voucher/internal/biz/bo" "voucher/internal/biz/cmb" @@ -29,9 +27,6 @@ type MultiBiz struct { MultiNotifyDataRepo repo.MultiNotifyDataRepo MultiNotifyLogRepo repo.MultiNotifyLogRepo CmbMixRepo mixrepos.CmbMixRepo - - mu sync.RWMutex - NonExistentBatchData map[string]bool } func NewMultiBiz( @@ -53,34 +48,11 @@ func NewMultiBiz( MultiNotifyDataRepo: multiNotifyDataRepo, MultiNotifyLogRepo: multiNotifyLogRepo, CmbMixRepo: cmbMixRepo, - - NonExistentBatchData: make(map[string]bool), } } -func (this *MultiBiz) Get(uid string) bool { - - if _, ok := this.NonExistentBatchData[uid]; ok { - return ok - } - - return false -} - -func (this *MultiBiz) Add(uid string) { - - this.mu.Lock() - defer this.mu.Unlock() - - this.NonExistentBatchData[uid] = true -} - func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.WechatVoucherNotifyBo) error { - if biz.Get(req.PlainText.StockID) { - return nil - } - cl := vo.MultiNotifyLockKey.BuildCache([]string{ source, req.PlainText.StockID, @@ -89,26 +61,6 @@ func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.Wech return lock.NewMutex(biz.rdb.Rdb, cl.TTL).Lock(ctx, cl.Key, func(ctx context.Context) error { - product, err := biz.ProductRepo.GetByBatchNo(ctx, req.PlainText.StockID) - if err != nil { - // 数据库不存在该活动批次,过滤掉 - if errors.Is(err, gorm.ErrRecordNotFound) { - biz.Add(req.PlainText.StockID) - return nil - } - if err2.IsDbNotFound(err) { - biz.Add(req.PlainText.StockID) - return nil - } - return err - } - - // 不是多笔立减金,过滤掉 - if product.ActivityId == "" { - biz.Add(req.PlainText.StockID) - return nil - } - if req.PlainText.ConsumeInformation.ConsumeAmount == 0 { return fmt.Errorf("消费金额不能为0") } @@ -118,6 +70,10 @@ func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.Wech return err } + if order.ActivityId != "" { + return fmt.Errorf("批次活动ID不为空,不是多笔立减金,请检查") + } + if err = biz.Run(ctx, ip, source, req, order); err != nil { return err } diff --git a/internal/biz/provider_set.go b/internal/biz/provider_set.go index 1b2e794..293b190 100644 --- a/internal/biz/provider_set.go +++ b/internal/biz/provider_set.go @@ -5,4 +5,4 @@ import ( ) // ProviderSetBiz is biz providers. -var ProviderSetBiz = wire.NewSet(NewVoucherBiz, NewMultiBiz) +var ProviderSetBiz = wire.NewSet(NewVoucherBiz, NewMultiBiz, NewWechatBiz) diff --git a/internal/biz/wechat.go b/internal/biz/wechat.go new file mode 100644 index 0000000..e9761df --- /dev/null +++ b/internal/biz/wechat.go @@ -0,0 +1,26 @@ +package biz + +import ( + "context" + "net/http" + "voucher/internal/biz/bo" + "voucher/internal/biz/wechatrepo" +) + +type WechatBiz struct { + BankMultiActivityRepo wechatrepo.BankMultiActivityRepo +} + +func NewWechatBiz(bankMultiActivityRepo wechatrepo.BankMultiActivityRepo) *WechatBiz { + return &WechatBiz{BankMultiActivityRepo: bankMultiActivityRepo} +} + +func (biz *WechatBiz) CallBack(ctx context.Context, mchId string, headers *http.Header, respBody []byte) (*bo.WechatVoucherNotifyBo, error) { + + response, err := biz.BankMultiActivityRepo.Notify(ctx, mchId, headers, respBody) + if err != nil { + return nil, err + } + + return response, nil +} diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index be88e76..2ddc5bb 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -11,7 +11,7 @@ import ( "voucher/internal/pkg/lock" ) -func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, tag string, req *bo.WechatVoucherNotifyBo) error { +func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, ip, tag string, req *bo.WechatVoucherNotifyBo) error { c := vo.WechatNotifyConsumeLockKey.BuildCache([]string{tag, req.PlainText.StockID, req.PlainText.CouponID}) @@ -23,7 +23,7 @@ func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, tag string, re } if order.ActivityId != "" { - return this.MultiBiz.Run(ctx, "127.0.0.1", "lsxd_"+req.PlainText.StockCreatorMchid, req, order) + return this.MultiBiz.Run(ctx, ip, "lsxd_"+req.PlainText.StockCreatorMchid, req, order) } if req.PlainText.Status.IsSended() { diff --git a/internal/biz/wechatrepo/bank_multi_activity.go b/internal/biz/wechatrepo/bank_multi_activity.go index 640efbf..626102e 100644 --- a/internal/biz/wechatrepo/bank_multi_activity.go +++ b/internal/biz/wechatrepo/bank_multi_activity.go @@ -1,9 +1,12 @@ package wechatrepo import ( + "context" + "net/http" "voucher/internal/biz/bo" ) type BankMultiActivityRepo interface { Order(order *bo.OrderBo) (couponId string, err error) + Notify(ctx context.Context, mchId string, headers *http.Header, respBody []byte) (response *bo.WechatVoucherNotifyBo, err error) } diff --git a/internal/conf/conf.pb.go b/internal/conf/conf.pb.go index 7f53e1e..298901e 100644 --- a/internal/conf/conf.pb.go +++ b/internal/conf/conf.pb.go @@ -26,18 +26,19 @@ type Bootstrap struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - Server *Server `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"` - Logs *Logs `protobuf:"bytes,2,opt,name=logs,proto3" json:"logs,omitempty"` - Data *Data `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` - RocketMQ *RocketMQ `protobuf:"bytes,4,opt,name=rocketMQ,proto3" json:"rocketMQ,omitempty"` - Wechat *Wechat `protobuf:"bytes,5,opt,name=wechat,proto3" json:"wechat,omitempty"` - Cmb *Cmb `protobuf:"bytes,6,opt,name=cmb,proto3" json:"cmb,omitempty"` - WechatNotifyMQ *WechatNotifyMQ `protobuf:"bytes,7,opt,name=wechatNotifyMQ,proto3" json:"wechatNotifyMQ,omitempty"` - Alarm *Alarm `protobuf:"bytes,8,opt,name=alarm,proto3" json:"alarm,omitempty"` - Cron *Cron `protobuf:"bytes,9,opt,name=cron,proto3" json:"cron,omitempty"` - RdsMQ *RdsMQ `protobuf:"bytes,10,opt,name=rdsMQ,proto3" json:"rdsMQ,omitempty"` - AliYunSms *AliYunSms `protobuf:"bytes,11,opt,name=aliYunSms,proto3" json:"aliYunSms,omitempty"` - Tripartite *Tripartite `protobuf:"bytes,12,opt,name=tripartite,proto3" json:"tripartite,omitempty"` + Server *Server `protobuf:"bytes,1,opt,name=server,proto3" json:"server,omitempty"` + Logs *Logs `protobuf:"bytes,2,opt,name=logs,proto3" json:"logs,omitempty"` + Data *Data `protobuf:"bytes,3,opt,name=data,proto3" json:"data,omitempty"` + RocketMQ *RocketMQ `protobuf:"bytes,4,opt,name=rocketMQ,proto3" json:"rocketMQ,omitempty"` + Wechat *Wechat `protobuf:"bytes,5,opt,name=wechat,proto3" json:"wechat,omitempty"` + Cmb *Cmb `protobuf:"bytes,6,opt,name=cmb,proto3" json:"cmb,omitempty"` + WechatNotifyMQ *WechatNotifyMQ `protobuf:"bytes,7,opt,name=wechatNotifyMQ,proto3" json:"wechatNotifyMQ,omitempty"` + Alarm *Alarm `protobuf:"bytes,8,opt,name=alarm,proto3" json:"alarm,omitempty"` + Cron *Cron `protobuf:"bytes,9,opt,name=cron,proto3" json:"cron,omitempty"` + RdsMQ *RdsMQ `protobuf:"bytes,10,opt,name=rdsMQ,proto3" json:"rdsMQ,omitempty"` + AliYunSms *AliYunSms `protobuf:"bytes,11,opt,name=aliYunSms,proto3" json:"aliYunSms,omitempty"` + Tripartite *Tripartite `protobuf:"bytes,12,opt,name=tripartite,proto3" json:"tripartite,omitempty"` + WechatSubject []*WechatSubject `protobuf:"bytes,13,rep,name=wechatSubject,proto3" json:"wechatSubject,omitempty"` } func (x *Bootstrap) Reset() { @@ -156,6 +157,13 @@ func (x *Bootstrap) GetTripartite() *Tripartite { return nil } +func (x *Bootstrap) GetWechatSubject() []*WechatSubject { + if x != nil { + return x.WechatSubject + } + return nil +} + type Server struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1143,6 +1151,85 @@ func (x *Logs) GetAccess() string { return "" } +type WechatSubject struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MchID string `protobuf:"bytes,1,opt,name=mchID,proto3" json:"mchID,omitempty"` + Name string `protobuf:"bytes,4,opt,name=name,proto3" json:"name,omitempty"` + MchCertificateSerialNumber string `protobuf:"bytes,2,opt,name=mchCertificateSerialNumber,proto3" json:"mchCertificateSerialNumber,omitempty"` + WechatPayPublicKeyID string `protobuf:"bytes,3,opt,name=wechatPayPublicKeyID,proto3" json:"wechatPayPublicKeyID,omitempty"` + MchApiV3Key string `protobuf:"bytes,6,opt,name=mchApiV3Key,proto3" json:"mchApiV3Key,omitempty"` +} + +func (x *WechatSubject) Reset() { + *x = WechatSubject{} + if protoimpl.UnsafeEnabled { + mi := &file_conf_conf_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *WechatSubject) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*WechatSubject) ProtoMessage() {} + +func (x *WechatSubject) ProtoReflect() protoreflect.Message { + mi := &file_conf_conf_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use WechatSubject.ProtoReflect.Descriptor instead. +func (*WechatSubject) Descriptor() ([]byte, []int) { + return file_conf_conf_proto_rawDescGZIP(), []int{14} +} + +func (x *WechatSubject) GetMchID() string { + if x != nil { + return x.MchID + } + return "" +} + +func (x *WechatSubject) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *WechatSubject) GetMchCertificateSerialNumber() string { + if x != nil { + return x.MchCertificateSerialNumber + } + return "" +} + +func (x *WechatSubject) GetWechatPayPublicKeyID() string { + if x != nil { + return x.WechatPayPublicKeyID + } + return "" +} + +func (x *WechatSubject) GetMchApiV3Key() string { + if x != nil { + return x.MchApiV3Key + } + return "" +} + type Server_HTTP struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1158,7 +1245,7 @@ type Server_HTTP struct { func (x *Server_HTTP) Reset() { *x = Server_HTTP{} if protoimpl.UnsafeEnabled { - mi := &file_conf_conf_proto_msgTypes[14] + mi := &file_conf_conf_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1171,7 +1258,7 @@ func (x *Server_HTTP) String() string { func (*Server_HTTP) ProtoMessage() {} func (x *Server_HTTP) ProtoReflect() protoreflect.Message { - mi := &file_conf_conf_proto_msgTypes[14] + mi := &file_conf_conf_proto_msgTypes[15] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1238,7 +1325,7 @@ type Data_Database struct { func (x *Data_Database) Reset() { *x = Data_Database{} if protoimpl.UnsafeEnabled { - mi := &file_conf_conf_proto_msgTypes[15] + mi := &file_conf_conf_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1251,7 +1338,7 @@ func (x *Data_Database) String() string { func (*Data_Database) ProtoMessage() {} func (x *Data_Database) ProtoReflect() protoreflect.Message { - mi := &file_conf_conf_proto_msgTypes[15] + mi := &file_conf_conf_proto_msgTypes[16] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1328,7 +1415,7 @@ type Data_Redis struct { func (x *Data_Redis) Reset() { *x = Data_Redis{} if protoimpl.UnsafeEnabled { - mi := &file_conf_conf_proto_msgTypes[16] + mi := &file_conf_conf_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1341,7 +1428,7 @@ func (x *Data_Redis) String() string { func (*Data_Redis) ProtoMessage() {} func (x *Data_Redis) ProtoReflect() protoreflect.Message { - mi := &file_conf_conf_proto_msgTypes[16] + mi := &file_conf_conf_proto_msgTypes[17] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1432,7 +1519,7 @@ type Cron_CommandMap struct { func (x *Cron_CommandMap) Reset() { *x = Cron_CommandMap{} if protoimpl.UnsafeEnabled { - mi := &file_conf_conf_proto_msgTypes[18] + mi := &file_conf_conf_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1445,7 +1532,7 @@ func (x *Cron_CommandMap) String() string { func (*Cron_CommandMap) ProtoMessage() {} func (x *Cron_CommandMap) ProtoReflect() protoreflect.Message { - mi := &file_conf_conf_proto_msgTypes[18] + mi := &file_conf_conf_proto_msgTypes[19] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1490,7 +1577,7 @@ type RdsMQ_Queue struct { func (x *RdsMQ_Queue) Reset() { *x = RdsMQ_Queue{} if protoimpl.UnsafeEnabled { - mi := &file_conf_conf_proto_msgTypes[20] + mi := &file_conf_conf_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1503,7 +1590,7 @@ func (x *RdsMQ_Queue) String() string { func (*RdsMQ_Queue) ProtoMessage() {} func (x *RdsMQ_Queue) ProtoReflect() protoreflect.Message { - mi := &file_conf_conf_proto_msgTypes[20] + mi := &file_conf_conf_proto_msgTypes[21] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1565,7 +1652,7 @@ type Tripartite_QiXing struct { func (x *Tripartite_QiXing) Reset() { *x = Tripartite_QiXing{} if protoimpl.UnsafeEnabled { - mi := &file_conf_conf_proto_msgTypes[21] + mi := &file_conf_conf_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1578,7 +1665,7 @@ func (x *Tripartite_QiXing) String() string { func (*Tripartite_QiXing) ProtoMessage() {} func (x *Tripartite_QiXing) ProtoReflect() protoreflect.Message { - mi := &file_conf_conf_proto_msgTypes[21] + mi := &file_conf_conf_proto_msgTypes[22] if protoimpl.UnsafeEnabled && x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1608,7 +1695,7 @@ var file_conf_conf_proto_rawDesc = []byte{ 0x6f, 0x12, 0x0e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x22, 0xdd, 0x04, 0x0a, 0x09, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x12, + 0x6f, 0x22, 0xa2, 0x05, 0x0a, 0x09, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x12, 0x2e, 0x0a, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x52, 0x06, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, @@ -1646,233 +1733,250 @@ var file_conf_conf_proto_rawDesc = []byte{ 0x69, 0x74, 0x65, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54, 0x72, 0x69, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x65, 0x52, 0x0a, 0x74, 0x72, 0x69, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, - 0x65, 0x22, 0xff, 0x01, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x12, 0x2f, 0x0a, 0x04, - 0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, - 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x53, 0x65, 0x72, 0x76, - 0x65, 0x72, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x04, 0x68, 0x74, 0x74, 0x70, 0x1a, 0xc3, 0x01, - 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, - 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, - 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x61, 0x64, 0x64, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x24, 0x0a, 0x0d, 0x69, 0x73, 0x4f, - 0x70, 0x65, 0x6e, 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, - 0x52, 0x0d, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x12, - 0x32, 0x0a, 0x14, 0x69, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x65, 0x71, - 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x69, - 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x65, 0x71, 0x48, 0x65, 0x61, 0x64, - 0x65, 0x72, 0x73, 0x22, 0x94, 0x05, 0x0a, 0x04, 0x44, 0x61, 0x74, 0x61, 0x12, 0x2d, 0x0a, 0x02, - 0x64, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, - 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x44, - 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x02, 0x64, 0x62, 0x12, 0x30, 0x0a, 0x05, 0x72, - 0x65, 0x64, 0x69, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x76, 0x6f, 0x75, - 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, 0x61, 0x74, 0x61, - 0x2e, 0x52, 0x65, 0x64, 0x69, 0x73, 0x52, 0x05, 0x72, 0x65, 0x64, 0x69, 0x73, 0x1a, 0xc5, 0x01, - 0x0a, 0x08, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x72, - 0x69, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x64, 0x72, 0x69, 0x76, - 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, - 0x78, 0x49, 0x64, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x6d, 0x61, 0x78, - 0x49, 0x64, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x4f, 0x70, 0x65, 0x6e, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x3b, - 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x4c, 0x69, 0x66, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, - 0x6d, 0x61, 0x78, 0x4c, 0x69, 0x66, 0x65, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x69, - 0x73, 0x44, 0x65, 0x62, 0x75, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, - 0x44, 0x65, 0x62, 0x75, 0x67, 0x1a, 0xe2, 0x02, 0x0a, 0x05, 0x52, 0x65, 0x64, 0x69, 0x73, 0x12, - 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, - 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x3b, 0x0a, - 0x0b, 0x72, 0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x72, - 0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x3d, 0x0a, 0x0c, 0x77, 0x72, - 0x69, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, - 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, - 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0c, 0x77, 0x72, 0x69, - 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x61, 0x73, - 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x61, 0x73, - 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, 0x6f, 0x6c, 0x53, 0x69, 0x7a, - 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6f, 0x6f, 0x6c, 0x53, 0x69, 0x7a, - 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x6d, 0x69, 0x6e, 0x49, 0x64, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x6e, - 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x69, 0x6e, 0x49, 0x64, 0x6c, 0x65, - 0x43, 0x6f, 0x6e, 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x4d, 0x61, 0x78, - 0x49, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, - 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, - 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x4d, - 0x61, 0x78, 0x49, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x64, 0x62, - 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x64, 0x62, 0x22, 0x97, 0x02, 0x0a, 0x08, 0x52, - 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x4d, 0x51, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x1c, 0x0a, 0x09, 0x61, - 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, - 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, 0x73, 0x65, 0x63, 0x72, 0x65, - 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x73, 0x65, - 0x63, 0x72, 0x65, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x42, 0x0a, 0x08, 0x65, 0x76, 0x65, - 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x76, 0x6f, - 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x6f, 0x63, - 0x6b, 0x65, 0x74, 0x4d, 0x51, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x45, 0x6e, - 0x74, 0x72, 0x79, 0x52, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x1a, 0x55, 0x0a, - 0x0d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, - 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, - 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, - 0x18, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, - 0x3a, 0x02, 0x38, 0x01, 0x22, 0x88, 0x01, 0x0a, 0x08, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x61, - 0x70, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x28, 0x0a, - 0x0f, 0x70, 0x65, 0x72, 0x43, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x43, 0x6e, 0x74, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x70, 0x65, 0x72, 0x43, 0x6f, 0x72, 0x6f, 0x75, - 0x74, 0x69, 0x6e, 0x65, 0x43, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0e, 0x69, 0x73, 0x4f, 0x70, 0x65, - 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, - 0x0e, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x22, - 0xa6, 0x01, 0x0a, 0x06, 0x57, 0x65, 0x63, 0x68, 0x61, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x63, - 0x68, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x63, 0x68, 0x49, 0x44, - 0x12, 0x3e, 0x0a, 0x1a, 0x6d, 0x63, 0x68, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, - 0x74, 0x65, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x6d, 0x63, 0x68, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, - 0x63, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, 0x72, - 0x12, 0x32, 0x0a, 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x50, 0x61, 0x79, 0x50, 0x75, 0x62, - 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, - 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x50, 0x61, 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, - 0x65, 0x79, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xff, 0x02, 0x0a, 0x03, 0x43, 0x6d, 0x62, - 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6d, - 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x61, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6d, 0x32, 0x50, 0x72, 0x6b, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6d, 0x32, 0x50, 0x72, 0x6b, 0x12, 0x16, 0x0a, 0x06, - 0x73, 0x6d, 0x32, 0x50, 0x75, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6d, - 0x32, 0x50, 0x75, 0x6b, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6d, 0x62, 0x53, 0x6d, 0x32, 0x50, 0x69, - 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6d, 0x62, 0x53, 0x6d, 0x32, 0x50, - 0x69, 0x6b, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6d, 0x62, 0x53, 0x6d, 0x32, 0x50, 0x75, 0x6b, 0x18, - 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6d, 0x62, 0x53, 0x6d, 0x32, 0x50, 0x75, 0x6b, - 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x20, 0x0a, 0x0b, - 0x63, 0x6d, 0x62, 0x4b, 0x65, 0x79, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x18, 0x08, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0b, 0x63, 0x6d, 0x62, 0x4b, 0x65, 0x79, 0x41, 0x6c, 0x69, 0x61, 0x73, 0x12, 0x14, - 0x0a, 0x05, 0x6f, 0x72, 0x67, 0x4e, 0x6f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, - 0x72, 0x67, 0x4e, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x72, - 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, - 0x72, 0x6c, 0x12, 0x26, 0x0a, 0x0e, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x4e, 0x6f, 0x74, 0x69, 0x66, - 0x79, 0x55, 0x72, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6d, 0x75, 0x6c, 0x74, - 0x69, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x72, 0x6c, 0x12, 0x28, 0x0a, 0x0f, 0x6e, 0x6f, - 0x74, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x79, 0x73, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, - 0x44, 0x61, 0x79, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x45, 0x6e, - 0x64, 0x44, 0x61, 0x79, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0d, 0x6e, 0x6f, 0x74, - 0x69, 0x63, 0x65, 0x45, 0x6e, 0x64, 0x44, 0x61, 0x79, 0x73, 0x22, 0xc6, 0x02, 0x0a, 0x0e, 0x57, - 0x65, 0x63, 0x68, 0x61, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x4d, 0x51, 0x12, 0x20, 0x0a, - 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, - 0x28, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, - 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x49, - 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x49, - 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x18, - 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x63, 0x65, 0x49, - 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x07, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x67, 0x72, 0x6f, - 0x75, 0x70, 0x49, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x67, 0x72, 0x6f, 0x75, - 0x70, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, - 0x61, 0x67, 0x55, 0x72, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x55, 0x72, 0x6c, 0x12, 0x26, 0x0a, 0x0e, 0x69, - 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x18, 0x0b, 0x20, - 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x75, - 0x6d, 0x65, 0x72, 0x22, 0x9b, 0x01, 0x0a, 0x05, 0x41, 0x6c, 0x61, 0x72, 0x6d, 0x12, 0x1e, 0x0a, - 0x0a, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x55, 0x52, 0x4c, 0x12, 0x16, 0x0a, - 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, - 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x74, 0x41, 0x6c, 0x6c, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x74, 0x41, 0x6c, 0x6c, 0x12, 0x1c, 0x0a, 0x09, 0x61, - 0x74, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, - 0x61, 0x74, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x0e, 0x77, 0x61, 0x72, - 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x05, 0x20, 0x03, 0x28, - 0x09, 0x52, 0x0e, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, - 0x73, 0x22, 0x84, 0x02, 0x0a, 0x04, 0x43, 0x72, 0x6f, 0x6e, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, - 0x4f, 0x70, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4f, 0x70, - 0x65, 0x6e, 0x12, 0x44, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, - 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x72, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6d, - 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x0a, 0x63, 0x6f, - 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x1a, 0x3e, 0x0a, 0x0a, 0x43, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, - 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x18, - 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x5e, 0x0a, 0x0f, 0x43, 0x6f, 0x6d, 0x6d, - 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, - 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x35, 0x0a, - 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1f, 0x2e, 0x76, - 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x72, - 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc4, 0x04, 0x0a, 0x05, 0x52, 0x64, 0x73, - 0x4d, 0x51, 0x12, 0x3d, 0x0a, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, - 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, - 0x75, 0x65, 0x75, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x51, 0x75, 0x65, 0x72, - 0x79, 0x12, 0x4f, 0x0a, 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x53, - 0x6c, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x65, 0x12, 0x43, 0x0a, 0x0d, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x53, 0x75, 0x62, 0x6a, 0x65, + 0x63, 0x74, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, + 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x57, 0x65, 0x63, 0x68, 0x61, 0x74, + 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x52, 0x0d, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x53, + 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x22, 0xff, 0x01, 0x0a, 0x06, 0x53, 0x65, 0x72, 0x76, 0x65, + 0x72, 0x12, 0x2f, 0x0a, 0x04, 0x68, 0x74, 0x74, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, - 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x14, 0x77, 0x65, - 0x63, 0x68, 0x61, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, - 0x72, 0x79, 0x12, 0x3d, 0x0a, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x52, 0x65, 0x74, 0x72, - 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, - 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, - 0x75, 0x65, 0x75, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x52, 0x65, 0x74, 0x72, - 0x79, 0x12, 0x3d, 0x0a, 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, - 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, - 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, - 0x65, 0x75, 0x65, 0x52, 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, - 0x12, 0x47, 0x0a, 0x10, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, - 0x65, 0x74, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, - 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, - 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x10, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x4e, 0x6f, - 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x3b, 0x0a, 0x0a, 0x75, 0x73, 0x65, - 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, - 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, - 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x0a, 0x75, 0x73, 0x65, 0x64, - 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x1a, 0xa6, 0x01, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x75, 0x65, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x1a, 0x0a, 0x08, - 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x75, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, - 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x75, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x6e, 0x75, 0x6d, 0x57, - 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x6e, 0x75, - 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x35, 0x0a, 0x08, 0x77, 0x61, 0x69, 0x74, - 0x54, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, + 0x2e, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2e, 0x48, 0x54, 0x54, 0x50, 0x52, 0x04, 0x68, 0x74, + 0x74, 0x70, 0x1a, 0xc3, 0x01, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x12, 0x18, 0x0a, 0x07, 0x6e, + 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, + 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, 0x12, 0x33, 0x0a, 0x07, 0x74, 0x69, 0x6d, + 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x77, 0x61, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x22, - 0xb9, 0x01, 0x0a, 0x09, 0x41, 0x6c, 0x69, 0x59, 0x75, 0x6e, 0x53, 0x6d, 0x73, 0x12, 0x20, 0x0a, - 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x18, 0x01, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, 0x64, 0x12, - 0x28, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, - 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x65, 0x6e, 0x64, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x65, 0x6e, 0x64, - 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x4e, 0x61, 0x6d, - 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x69, 0x67, 0x6e, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x57, 0x61, 0x72, - 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x74, 0x65, 0x6d, 0x70, - 0x6c, 0x61, 0x74, 0x65, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x22, 0x69, 0x0a, 0x0a, 0x54, - 0x72, 0x69, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x65, 0x12, 0x39, 0x0a, 0x06, 0x71, 0x69, 0x58, - 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x76, 0x6f, 0x75, 0x63, - 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x54, 0x72, 0x69, 0x70, 0x61, - 0x72, 0x74, 0x69, 0x74, 0x65, 0x2e, 0x51, 0x69, 0x58, 0x69, 0x6e, 0x67, 0x52, 0x06, 0x71, 0x69, - 0x58, 0x69, 0x6e, 0x67, 0x1a, 0x20, 0x0a, 0x06, 0x51, 0x69, 0x58, 0x69, 0x6e, 0x67, 0x12, 0x16, - 0x0a, 0x06, 0x61, 0x70, 0x70, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, - 0x61, 0x70, 0x70, 0x4b, 0x65, 0x79, 0x22, 0x3a, 0x0a, 0x04, 0x4c, 0x6f, 0x67, 0x73, 0x12, 0x1a, - 0x0a, 0x08, 0x62, 0x75, 0x73, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x08, 0x62, 0x75, 0x73, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x63, - 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x63, 0x63, 0x65, - 0x73, 0x73, 0x42, 0x17, 0x5a, 0x15, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2f, 0x63, 0x70, - 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x3b, 0x63, 0x6f, 0x6e, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x07, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x24, + 0x0a, 0x0d, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x77, 0x61, 0x67, 0x67, 0x65, 0x72, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x77, 0x61, + 0x67, 0x67, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x14, 0x69, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x52, 0x65, 0x71, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x08, 0x52, 0x14, 0x69, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x65, + 0x71, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x73, 0x22, 0x94, 0x05, 0x0a, 0x04, 0x44, 0x61, 0x74, + 0x61, 0x12, 0x2d, 0x0a, 0x02, 0x64, 0x62, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1d, 0x2e, + 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x44, + 0x61, 0x74, 0x61, 0x2e, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x52, 0x02, 0x64, 0x62, + 0x12, 0x30, 0x0a, 0x05, 0x72, 0x65, 0x64, 0x69, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x2e, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x65, 0x64, 0x69, 0x73, 0x52, 0x05, 0x72, 0x65, 0x64, + 0x69, 0x73, 0x1a, 0xc5, 0x01, 0x0a, 0x08, 0x44, 0x61, 0x74, 0x61, 0x62, 0x61, 0x73, 0x65, 0x12, + 0x16, 0x0a, 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x64, 0x72, 0x69, 0x76, 0x65, 0x72, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, + 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, + 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, 0x49, 0x64, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, + 0x52, 0x07, 0x6d, 0x61, 0x78, 0x49, 0x64, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x6d, 0x61, 0x78, + 0x4f, 0x70, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x4f, + 0x70, 0x65, 0x6e, 0x12, 0x3b, 0x0a, 0x0b, 0x6d, 0x61, 0x78, 0x4c, 0x69, 0x66, 0x65, 0x74, 0x69, + 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, + 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x6d, 0x61, 0x78, 0x4c, 0x69, 0x66, 0x65, 0x74, 0x69, 0x6d, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x69, 0x73, 0x44, 0x65, 0x62, 0x75, 0x67, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x08, 0x52, 0x07, 0x69, 0x73, 0x44, 0x65, 0x62, 0x75, 0x67, 0x1a, 0xe2, 0x02, 0x0a, 0x05, 0x52, + 0x65, 0x64, 0x69, 0x73, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x12, + 0x0a, 0x04, 0x61, 0x64, 0x64, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, + 0x64, 0x72, 0x12, 0x3b, 0x0a, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, + 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x52, 0x0b, 0x72, 0x65, 0x61, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, + 0x3d, 0x0a, 0x0c, 0x77, 0x72, 0x69, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x52, 0x0c, 0x77, 0x72, 0x69, 0x74, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x6f, + 0x6f, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x05, 0x52, 0x08, 0x70, 0x6f, + 0x6f, 0x6c, 0x53, 0x69, 0x7a, 0x65, 0x12, 0x22, 0x0a, 0x0c, 0x6d, 0x69, 0x6e, 0x49, 0x64, 0x6c, + 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0c, 0x6d, 0x69, + 0x6e, 0x49, 0x64, 0x6c, 0x65, 0x43, 0x6f, 0x6e, 0x6e, 0x73, 0x12, 0x43, 0x0a, 0x0f, 0x63, 0x6f, + 0x6e, 0x6e, 0x4d, 0x61, 0x78, 0x49, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x08, 0x20, + 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0f, + 0x63, 0x6f, 0x6e, 0x6e, 0x4d, 0x61, 0x78, 0x49, 0x64, 0x6c, 0x65, 0x54, 0x69, 0x6d, 0x65, 0x12, + 0x0e, 0x0a, 0x02, 0x64, 0x62, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x02, 0x64, 0x62, 0x22, + 0x97, 0x02, 0x0a, 0x08, 0x52, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x4d, 0x51, 0x12, 0x12, 0x0a, 0x04, + 0x61, 0x64, 0x64, 0x72, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, 0x64, 0x64, 0x72, + 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x09, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x12, 0x1c, + 0x0a, 0x09, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79, 0x12, 0x20, 0x0a, 0x0b, + 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x42, + 0x0a, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x18, 0x05, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x26, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, + 0x67, 0x2e, 0x52, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x4d, 0x51, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, + 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x4d, + 0x61, 0x70, 0x1a, 0x55, 0x0a, 0x0d, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x52, 0x05, + 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x88, 0x01, 0x0a, 0x08, 0x45, 0x76, + 0x65, 0x6e, 0x74, 0x4d, 0x61, 0x70, 0x12, 0x14, 0x0a, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x12, 0x14, 0x0a, 0x05, + 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x70, + 0x69, 0x63, 0x12, 0x28, 0x0a, 0x0f, 0x70, 0x65, 0x72, 0x43, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, + 0x6e, 0x65, 0x43, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x0f, 0x70, 0x65, 0x72, + 0x43, 0x6f, 0x72, 0x6f, 0x75, 0x74, 0x69, 0x6e, 0x65, 0x43, 0x6e, 0x74, 0x12, 0x26, 0x0a, 0x0e, + 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x18, 0x04, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x73, + 0x75, 0x6d, 0x65, 0x72, 0x22, 0xa6, 0x01, 0x0a, 0x06, 0x57, 0x65, 0x63, 0x68, 0x61, 0x74, 0x12, + 0x14, 0x0a, 0x05, 0x6d, 0x63, 0x68, 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x6d, 0x63, 0x68, 0x49, 0x44, 0x12, 0x3e, 0x0a, 0x1a, 0x6d, 0x63, 0x68, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x6d, 0x63, 0x68, 0x43, 0x65, + 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, + 0x75, 0x6d, 0x62, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x50, + 0x61, 0x79, 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x49, 0x44, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x50, 0x61, 0x79, 0x50, 0x75, + 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x49, 0x44, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xff, 0x02, + 0x0a, 0x03, 0x43, 0x6d, 0x62, 0x12, 0x10, 0x0a, 0x03, 0x6d, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x03, 0x6d, 0x69, 0x64, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x69, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x61, 0x69, 0x64, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6d, 0x32, + 0x50, 0x72, 0x6b, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x6d, 0x32, 0x50, 0x72, + 0x6b, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x6d, 0x32, 0x50, 0x75, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x06, 0x73, 0x6d, 0x32, 0x50, 0x75, 0x6b, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6d, 0x62, + 0x53, 0x6d, 0x32, 0x50, 0x69, 0x6b, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6d, + 0x62, 0x53, 0x6d, 0x32, 0x50, 0x69, 0x6b, 0x12, 0x1c, 0x0a, 0x09, 0x63, 0x6d, 0x62, 0x53, 0x6d, + 0x32, 0x50, 0x75, 0x6b, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x63, 0x6d, 0x62, 0x53, + 0x6d, 0x32, 0x50, 0x75, 0x6b, 0x12, 0x1a, 0x0a, 0x08, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x69, 0x61, + 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6b, 0x65, 0x79, 0x41, 0x6c, 0x69, 0x61, + 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x63, 0x6d, 0x62, 0x4b, 0x65, 0x79, 0x41, 0x6c, 0x69, 0x61, 0x73, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6d, 0x62, 0x4b, 0x65, 0x79, 0x41, 0x6c, + 0x69, 0x61, 0x73, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x72, 0x67, 0x4e, 0x6f, 0x18, 0x09, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x05, 0x6f, 0x72, 0x67, 0x4e, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x6f, 0x74, + 0x69, 0x66, 0x79, 0x55, 0x72, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x55, 0x72, 0x6c, 0x12, 0x26, 0x0a, 0x0e, 0x6d, 0x75, 0x6c, 0x74, 0x69, + 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x72, 0x6c, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0e, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x55, 0x72, 0x6c, 0x12, + 0x28, 0x0a, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x53, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, + 0x79, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x03, 0x52, 0x0f, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, + 0x53, 0x74, 0x61, 0x72, 0x74, 0x44, 0x61, 0x79, 0x73, 0x12, 0x24, 0x0a, 0x0d, 0x6e, 0x6f, 0x74, + 0x69, 0x63, 0x65, 0x45, 0x6e, 0x64, 0x44, 0x61, 0x79, 0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x03, + 0x52, 0x0d, 0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x45, 0x6e, 0x64, 0x44, 0x61, 0x79, 0x73, 0x22, + 0xc6, 0x02, 0x0a, 0x0e, 0x57, 0x65, 0x63, 0x68, 0x61, 0x74, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, + 0x4d, 0x51, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, + 0x65, 0x79, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, + 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x65, 0x6e, 0x64, 0x50, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, + 0x67, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x72, 0x65, + 0x67, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x0a, 0x69, 0x6e, 0x73, 0x74, 0x61, 0x6e, + 0x63, 0x65, 0x49, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x69, 0x6e, 0x73, 0x74, + 0x61, 0x6e, 0x63, 0x65, 0x49, 0x64, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x18, + 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x6f, 0x70, 0x69, 0x63, 0x12, 0x10, 0x0a, 0x03, + 0x74, 0x61, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x18, + 0x0a, 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x07, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0e, 0x72, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x55, 0x72, 0x6c, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x54, 0x61, 0x67, 0x55, 0x72, 0x6c, + 0x12, 0x26, 0x0a, 0x0e, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, + 0x65, 0x72, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0e, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, + 0x43, 0x6f, 0x6e, 0x73, 0x75, 0x6d, 0x65, 0x72, 0x22, 0x9b, 0x01, 0x0a, 0x05, 0x41, 0x6c, 0x61, + 0x72, 0x6d, 0x12, 0x1e, 0x0a, 0x0a, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x55, 0x52, 0x4c, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x77, 0x65, 0x62, 0x68, 0x6f, 0x6f, 0x6b, 0x55, + 0x52, 0x4c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x06, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x61, 0x74, + 0x41, 0x6c, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x05, 0x61, 0x74, 0x41, 0x6c, 0x6c, + 0x12, 0x1c, 0x0a, 0x09, 0x61, 0x74, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x04, 0x20, + 0x03, 0x28, 0x09, 0x52, 0x09, 0x61, 0x74, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x73, 0x12, 0x26, + 0x0a, 0x0e, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x4d, 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x73, + 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0e, 0x77, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x4d, + 0x6f, 0x62, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x84, 0x02, 0x0a, 0x04, 0x43, 0x72, 0x6f, 0x6e, 0x12, + 0x16, 0x0a, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, + 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x12, 0x44, 0x0a, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, + 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x76, 0x6f, + 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x43, 0x72, 0x6f, + 0x6e, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, + 0x79, 0x52, 0x0a, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x1a, 0x3e, 0x0a, + 0x0a, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x69, + 0x73, 0x4f, 0x70, 0x65, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4f, + 0x70, 0x65, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x1a, 0x5e, 0x0a, + 0x0f, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, 0x61, 0x70, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x35, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x1f, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, + 0x69, 0x67, 0x2e, 0x43, 0x72, 0x6f, 0x6e, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x61, 0x6e, 0x64, 0x4d, + 0x61, 0x70, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xc4, 0x04, + 0x0a, 0x05, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x12, 0x3d, 0x0a, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, + 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, + 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, + 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, + 0x74, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x4f, 0x0a, 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, + 0x54, 0x69, 0x6d, 0x65, 0x53, 0x6c, 0x69, 0x63, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, + 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, + 0x65, 0x52, 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x53, 0x6c, 0x69, + 0x63, 0x65, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x3d, 0x0a, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, + 0x74, 0x52, 0x65, 0x74, 0x72, 0x79, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, + 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, + 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x0b, 0x77, 0x65, 0x63, 0x68, 0x61, + 0x74, 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x3d, 0x0a, 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, + 0x6f, 0x74, 0x69, 0x66, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, + 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, + 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x0b, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, + 0x6f, 0x74, 0x69, 0x66, 0x79, 0x12, 0x47, 0x0a, 0x10, 0x6f, 0x72, 0x64, 0x65, 0x72, 0x4e, 0x6f, + 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x74, 0x72, 0x79, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, + 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, 0x10, 0x6f, 0x72, + 0x64, 0x65, 0x72, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x52, 0x65, 0x74, 0x72, 0x79, 0x12, 0x3b, + 0x0a, 0x0a, 0x75, 0x73, 0x65, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x18, 0x06, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x2e, 0x52, 0x64, 0x73, 0x4d, 0x51, 0x2e, 0x51, 0x75, 0x65, 0x75, 0x65, 0x52, + 0x0a, 0x75, 0x73, 0x65, 0x64, 0x4e, 0x6f, 0x74, 0x69, 0x66, 0x79, 0x1a, 0xa6, 0x01, 0x0a, 0x05, + 0x51, 0x75, 0x65, 0x75, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x69, 0x73, 0x4f, + 0x70, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x69, 0x73, 0x4f, 0x70, 0x65, + 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x75, 0x6d, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x0d, 0x52, 0x08, 0x72, 0x65, 0x74, 0x72, 0x79, 0x4e, 0x75, 0x6d, 0x12, 0x1e, 0x0a, + 0x0a, 0x6e, 0x75, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, + 0x0d, 0x52, 0x0a, 0x6e, 0x75, 0x6d, 0x57, 0x6f, 0x72, 0x6b, 0x65, 0x72, 0x73, 0x12, 0x35, 0x0a, + 0x08, 0x77, 0x61, 0x69, 0x74, 0x54, 0x69, 0x6d, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x08, 0x77, 0x61, 0x69, 0x74, + 0x54, 0x69, 0x6d, 0x65, 0x22, 0xb9, 0x01, 0x0a, 0x09, 0x41, 0x6c, 0x69, 0x59, 0x75, 0x6e, 0x53, + 0x6d, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x49, + 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, + 0x65, 0x79, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x0f, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, + 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x61, + 0x63, 0x63, 0x65, 0x73, 0x73, 0x4b, 0x65, 0x79, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x1a, + 0x0a, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x08, 0x65, 0x6e, 0x64, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1a, 0x0a, 0x08, 0x73, 0x69, + 0x67, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x73, 0x69, + 0x67, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x28, 0x0a, 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, + 0x74, 0x65, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0f, 0x74, 0x65, 0x6d, 0x70, 0x6c, 0x61, 0x74, 0x65, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, + 0x22, 0x69, 0x0a, 0x0a, 0x54, 0x72, 0x69, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x65, 0x12, 0x39, + 0x0a, 0x06, 0x71, 0x69, 0x58, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x21, + 0x2e, 0x76, 0x6f, 0x75, 0x63, 0x68, 0x65, 0x72, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, + 0x54, 0x72, 0x69, 0x70, 0x61, 0x72, 0x74, 0x69, 0x74, 0x65, 0x2e, 0x51, 0x69, 0x58, 0x69, 0x6e, + 0x67, 0x52, 0x06, 0x71, 0x69, 0x58, 0x69, 0x6e, 0x67, 0x1a, 0x20, 0x0a, 0x06, 0x51, 0x69, 0x58, + 0x69, 0x6e, 0x67, 0x12, 0x16, 0x0a, 0x06, 0x61, 0x70, 0x70, 0x4b, 0x65, 0x79, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x61, 0x70, 0x70, 0x4b, 0x65, 0x79, 0x22, 0x3a, 0x0a, 0x04, 0x4c, + 0x6f, 0x67, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x62, 0x75, 0x73, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x62, 0x75, 0x73, 0x69, 0x6e, 0x65, 0x73, 0x73, 0x12, + 0x16, 0x0a, 0x06, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x06, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x22, 0xcf, 0x01, 0x0a, 0x0d, 0x57, 0x65, 0x63, 0x68, + 0x61, 0x74, 0x53, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x6d, 0x63, 0x68, + 0x49, 0x44, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6d, 0x63, 0x68, 0x49, 0x44, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x1a, 0x6d, 0x63, 0x68, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, + 0x69, 0x63, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, 0x62, 0x65, + 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x1a, 0x6d, 0x63, 0x68, 0x43, 0x65, 0x72, 0x74, + 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x53, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x4e, 0x75, 0x6d, + 0x62, 0x65, 0x72, 0x12, 0x32, 0x0a, 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x50, 0x61, 0x79, + 0x50, 0x75, 0x62, 0x6c, 0x69, 0x63, 0x4b, 0x65, 0x79, 0x49, 0x44, 0x18, 0x03, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x14, 0x77, 0x65, 0x63, 0x68, 0x61, 0x74, 0x50, 0x61, 0x79, 0x50, 0x75, 0x62, 0x6c, + 0x69, 0x63, 0x4b, 0x65, 0x79, 0x49, 0x44, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x63, 0x68, 0x41, 0x70, + 0x69, 0x56, 0x33, 0x4b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x63, + 0x68, 0x41, 0x70, 0x69, 0x56, 0x33, 0x4b, 0x65, 0x79, 0x42, 0x17, 0x5a, 0x15, 0x76, 0x6f, 0x75, + 0x63, 0x68, 0x65, 0x72, 0x2f, 0x63, 0x70, 0x6e, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x3b, 0x63, 0x6f, + 0x6e, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1887,7 +1991,7 @@ func file_conf_conf_proto_rawDescGZIP() []byte { return file_conf_conf_proto_rawDescData } -var file_conf_conf_proto_msgTypes = make([]protoimpl.MessageInfo, 22) +var file_conf_conf_proto_msgTypes = make([]protoimpl.MessageInfo, 23) var file_conf_conf_proto_goTypes = []any{ (*Bootstrap)(nil), // 0: voucher.config.Bootstrap (*Server)(nil), // 1: voucher.config.Server @@ -1903,15 +2007,16 @@ var file_conf_conf_proto_goTypes = []any{ (*AliYunSms)(nil), // 11: voucher.config.AliYunSms (*Tripartite)(nil), // 12: voucher.config.Tripartite (*Logs)(nil), // 13: voucher.config.Logs - (*Server_HTTP)(nil), // 14: voucher.config.Server.HTTP - (*Data_Database)(nil), // 15: voucher.config.Data.Database - (*Data_Redis)(nil), // 16: voucher.config.Data.Redis - nil, // 17: voucher.config.RocketMQ.EventMapEntry - (*Cron_CommandMap)(nil), // 18: voucher.config.Cron.CommandMap - nil, // 19: voucher.config.Cron.CommandMapEntry - (*RdsMQ_Queue)(nil), // 20: voucher.config.RdsMQ.Queue - (*Tripartite_QiXing)(nil), // 21: voucher.config.Tripartite.QiXing - (*durationpb.Duration)(nil), // 22: google.protobuf.Duration + (*WechatSubject)(nil), // 14: voucher.config.WechatSubject + (*Server_HTTP)(nil), // 15: voucher.config.Server.HTTP + (*Data_Database)(nil), // 16: voucher.config.Data.Database + (*Data_Redis)(nil), // 17: voucher.config.Data.Redis + nil, // 18: voucher.config.RocketMQ.EventMapEntry + (*Cron_CommandMap)(nil), // 19: voucher.config.Cron.CommandMap + nil, // 20: voucher.config.Cron.CommandMapEntry + (*RdsMQ_Queue)(nil), // 21: voucher.config.RdsMQ.Queue + (*Tripartite_QiXing)(nil), // 22: voucher.config.Tripartite.QiXing + (*durationpb.Duration)(nil), // 23: google.protobuf.Duration } var file_conf_conf_proto_depIdxs = []int32{ 1, // 0: voucher.config.Bootstrap.server:type_name -> voucher.config.Server @@ -1926,31 +2031,32 @@ var file_conf_conf_proto_depIdxs = []int32{ 10, // 9: voucher.config.Bootstrap.rdsMQ:type_name -> voucher.config.RdsMQ 11, // 10: voucher.config.Bootstrap.aliYunSms:type_name -> voucher.config.AliYunSms 12, // 11: voucher.config.Bootstrap.tripartite:type_name -> voucher.config.Tripartite - 14, // 12: voucher.config.Server.http:type_name -> voucher.config.Server.HTTP - 15, // 13: voucher.config.Data.db:type_name -> voucher.config.Data.Database - 16, // 14: voucher.config.Data.redis:type_name -> voucher.config.Data.Redis - 17, // 15: voucher.config.RocketMQ.eventMap:type_name -> voucher.config.RocketMQ.EventMapEntry - 19, // 16: voucher.config.Cron.commandMap:type_name -> voucher.config.Cron.CommandMapEntry - 20, // 17: voucher.config.RdsMQ.wechatQuery:type_name -> voucher.config.RdsMQ.Queue - 20, // 18: voucher.config.RdsMQ.wechatTimeSliceQuery:type_name -> voucher.config.RdsMQ.Queue - 20, // 19: voucher.config.RdsMQ.wechatRetry:type_name -> voucher.config.RdsMQ.Queue - 20, // 20: voucher.config.RdsMQ.retryNotify:type_name -> voucher.config.RdsMQ.Queue - 20, // 21: voucher.config.RdsMQ.orderNotifyRetry:type_name -> voucher.config.RdsMQ.Queue - 20, // 22: voucher.config.RdsMQ.usedNotify:type_name -> voucher.config.RdsMQ.Queue - 21, // 23: voucher.config.Tripartite.qiXing:type_name -> voucher.config.Tripartite.QiXing - 22, // 24: voucher.config.Server.HTTP.timeout:type_name -> google.protobuf.Duration - 22, // 25: voucher.config.Data.Database.maxLifetime:type_name -> google.protobuf.Duration - 22, // 26: voucher.config.Data.Redis.readTimeout:type_name -> google.protobuf.Duration - 22, // 27: voucher.config.Data.Redis.writeTimeout:type_name -> google.protobuf.Duration - 22, // 28: voucher.config.Data.Redis.connMaxIdleTime:type_name -> google.protobuf.Duration - 4, // 29: voucher.config.RocketMQ.EventMapEntry.value:type_name -> voucher.config.EventMap - 18, // 30: voucher.config.Cron.CommandMapEntry.value:type_name -> voucher.config.Cron.CommandMap - 22, // 31: voucher.config.RdsMQ.Queue.waitTime:type_name -> google.protobuf.Duration - 32, // [32:32] is the sub-list for method output_type - 32, // [32:32] is the sub-list for method input_type - 32, // [32:32] is the sub-list for extension type_name - 32, // [32:32] is the sub-list for extension extendee - 0, // [0:32] is the sub-list for field type_name + 14, // 12: voucher.config.Bootstrap.wechatSubject:type_name -> voucher.config.WechatSubject + 15, // 13: voucher.config.Server.http:type_name -> voucher.config.Server.HTTP + 16, // 14: voucher.config.Data.db:type_name -> voucher.config.Data.Database + 17, // 15: voucher.config.Data.redis:type_name -> voucher.config.Data.Redis + 18, // 16: voucher.config.RocketMQ.eventMap:type_name -> voucher.config.RocketMQ.EventMapEntry + 20, // 17: voucher.config.Cron.commandMap:type_name -> voucher.config.Cron.CommandMapEntry + 21, // 18: voucher.config.RdsMQ.wechatQuery:type_name -> voucher.config.RdsMQ.Queue + 21, // 19: voucher.config.RdsMQ.wechatTimeSliceQuery:type_name -> voucher.config.RdsMQ.Queue + 21, // 20: voucher.config.RdsMQ.wechatRetry:type_name -> voucher.config.RdsMQ.Queue + 21, // 21: voucher.config.RdsMQ.retryNotify:type_name -> voucher.config.RdsMQ.Queue + 21, // 22: voucher.config.RdsMQ.orderNotifyRetry:type_name -> voucher.config.RdsMQ.Queue + 21, // 23: voucher.config.RdsMQ.usedNotify:type_name -> voucher.config.RdsMQ.Queue + 22, // 24: voucher.config.Tripartite.qiXing:type_name -> voucher.config.Tripartite.QiXing + 23, // 25: voucher.config.Server.HTTP.timeout:type_name -> google.protobuf.Duration + 23, // 26: voucher.config.Data.Database.maxLifetime:type_name -> google.protobuf.Duration + 23, // 27: voucher.config.Data.Redis.readTimeout:type_name -> google.protobuf.Duration + 23, // 28: voucher.config.Data.Redis.writeTimeout:type_name -> google.protobuf.Duration + 23, // 29: voucher.config.Data.Redis.connMaxIdleTime:type_name -> google.protobuf.Duration + 4, // 30: voucher.config.RocketMQ.EventMapEntry.value:type_name -> voucher.config.EventMap + 19, // 31: voucher.config.Cron.CommandMapEntry.value:type_name -> voucher.config.Cron.CommandMap + 23, // 32: voucher.config.RdsMQ.Queue.waitTime:type_name -> google.protobuf.Duration + 33, // [33:33] is the sub-list for method output_type + 33, // [33:33] is the sub-list for method input_type + 33, // [33:33] is the sub-list for extension type_name + 33, // [33:33] is the sub-list for extension extendee + 0, // [0:33] is the sub-list for field type_name } func init() { file_conf_conf_proto_init() } @@ -2128,7 +2234,7 @@ func file_conf_conf_proto_init() { } } file_conf_conf_proto_msgTypes[14].Exporter = func(v any, i int) any { - switch v := v.(*Server_HTTP); i { + switch v := v.(*WechatSubject); i { case 0: return &v.state case 1: @@ -2140,7 +2246,7 @@ func file_conf_conf_proto_init() { } } file_conf_conf_proto_msgTypes[15].Exporter = func(v any, i int) any { - switch v := v.(*Data_Database); i { + switch v := v.(*Server_HTTP); i { case 0: return &v.state case 1: @@ -2152,6 +2258,18 @@ func file_conf_conf_proto_init() { } } file_conf_conf_proto_msgTypes[16].Exporter = func(v any, i int) any { + switch v := v.(*Data_Database); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_conf_conf_proto_msgTypes[17].Exporter = func(v any, i int) any { switch v := v.(*Data_Redis); i { case 0: return &v.state @@ -2163,7 +2281,7 @@ func file_conf_conf_proto_init() { return nil } } - file_conf_conf_proto_msgTypes[18].Exporter = func(v any, i int) any { + file_conf_conf_proto_msgTypes[19].Exporter = func(v any, i int) any { switch v := v.(*Cron_CommandMap); i { case 0: return &v.state @@ -2175,7 +2293,7 @@ func file_conf_conf_proto_init() { return nil } } - file_conf_conf_proto_msgTypes[20].Exporter = func(v any, i int) any { + file_conf_conf_proto_msgTypes[21].Exporter = func(v any, i int) any { switch v := v.(*RdsMQ_Queue); i { case 0: return &v.state @@ -2187,7 +2305,7 @@ func file_conf_conf_proto_init() { return nil } } - file_conf_conf_proto_msgTypes[21].Exporter = func(v any, i int) any { + file_conf_conf_proto_msgTypes[22].Exporter = func(v any, i int) any { switch v := v.(*Tripartite_QiXing); i { case 0: return &v.state @@ -2206,7 +2324,7 @@ func file_conf_conf_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_conf_conf_proto_rawDesc, NumEnums: 0, - NumMessages: 22, + NumMessages: 23, NumExtensions: 0, NumServices: 0, }, diff --git a/internal/conf/conf.proto b/internal/conf/conf.proto index 1bc014f..aabe777 100644 --- a/internal/conf/conf.proto +++ b/internal/conf/conf.proto @@ -18,6 +18,7 @@ message Bootstrap { RdsMQ rdsMQ = 10; AliYunSms aliYunSms = 11; Tripartite tripartite = 12; + repeated WechatSubject wechatSubject = 13; } message Server { @@ -158,4 +159,12 @@ message Tripartite { message Logs { string business = 1; string access = 2; +} + +message WechatSubject { + string mchID = 1; + string name = 4; + string mchCertificateSerialNumber = 2; + string wechatPayPublicKeyID = 3; + string mchApiV3Key = 6; } \ No newline at end of file diff --git a/internal/data/wechatrepoimpl/bank_multi_activity.go b/internal/data/wechatrepoimpl/bank_multi_activity.go index 2e0fc0c..58bd146 100644 --- a/internal/data/wechatrepoimpl/bank_multi_activity.go +++ b/internal/data/wechatrepoimpl/bank_multi_activity.go @@ -1,9 +1,12 @@ package wechatrepoimpl import ( + "context" + "encoding/json" "errors" "fmt" "github.com/wechatpay-apiv3/wechatpay-go/core" + "net/http" err2 "voucher/api/err" "voucher/internal/biz/bo" "voucher/internal/biz/wechatrepo" @@ -55,3 +58,22 @@ func (w *BankMultiActivityImpl) Order(order *bo.OrderBo) (couponId string, err e return *resp.CouponId, nil } + +func (w *BankMultiActivityImpl) Notify(ctx context.Context, mchId string, headers *http.Header, respBody []byte) (response *bo.WechatVoucherNotifyBo, err error) { + + t, err := w.wx.Get(mchId) + if err != nil { + return + } + + decodeBodyStr, err := t.Notify(ctx, headers, respBody) + if err != nil { + return + } + + if err = json.Unmarshal([]byte(decodeBodyStr), &response); err != nil { + return nil, err + } + + return +} diff --git a/internal/data/wx.go b/internal/data/wx.go index fb9df64..6d268c0 100644 --- a/internal/data/wx.go +++ b/internal/data/wx.go @@ -24,6 +24,14 @@ func NewWx(c *conf.Bootstrap) (*Wx, error) { clients[c.Wechat.MchID] = client + for _, v := range c.WechatSubject { + cli, e := buildWechat(v) + if e != nil { + return nil, e + } + clients[v.MchID] = cli + } + return &Wx{Clients: clients}, nil } @@ -44,6 +52,33 @@ func (this *Wx) Get(mchId string) (*marketing.Marketing, error) { return nil, fmt.Errorf("微信调用client不存在[%s]", mchId) } +func buildWechat(wx *conf.WechatSubject) (*marketing.Marketing, error) { + + dir, err := os.Getwd() + if err != nil { + return nil, fmt.Errorf("商户ID[%s]商户名称[%s]获取目的地址有误[%v]", wx.MchID, wx.Name, err) + } + + filePath := fmt.Sprintf("%s/cert/wechat/%s", dir, wx.MchID) + if !helper.FileExists(filePath) { + panic(fmt.Sprintf("商户ID[%s]商户名称[%s]微信密钥证书信息不存在,请联系技术人员处理", wx.MchID, wx.Name)) + } + + cc, err := utils2.CreateMchConfig( + wx.MchID, // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756 + wx.MchCertificateSerialNumber, // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053 + fmt.Sprintf("%s/%s", filePath, "wechat_private_key.pem"), // 商户API证书私钥文件路径,本地文件路径 + wx.WechatPayPublicKeyID, // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 + fmt.Sprintf("%s/%s", filePath, "pub_key.pem"), // 微信支付公钥文件路径,本地文件路径 + wx.MchApiV3Key, + ) + if err != nil { + return nil, err + } + + return &marketing.Marketing{MchConfig: cc}, nil +} + func buildWx(wx *conf.Wechat) (*marketing.Marketing, error) { dir, err := os.Getwd() @@ -62,6 +97,7 @@ func buildWx(wx *conf.Wechat) (*marketing.Marketing, error) { fmt.Sprintf("%s/%s", filePath, "wechat_private_key.pem"), // 商户API证书私钥文件路径,本地文件路径 wx.WechatPayPublicKeyID, // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 fmt.Sprintf("%s/%s", filePath, "pub_key.pem"), // 微信支付公钥文件路径,本地文件路径 + "", ) if err != nil { return nil, err diff --git a/internal/pkg/wechat/srv/marketing/marketing.go b/internal/pkg/wechat/srv/marketing/marketing.go index 65203e0..71e0894 100644 --- a/internal/pkg/wechat/srv/marketing/marketing.go +++ b/internal/pkg/wechat/srv/marketing/marketing.go @@ -1,7 +1,9 @@ package marketing import ( + "context" "encoding/json" + "net/http" "net/url" "strings" "voucher/internal/pkg/wechat/srv" @@ -39,13 +41,13 @@ func (srv *Marketing) Send(openId string, req *SendReq) (response *SendResp, err } // Query @link https://pay.weixin.qq.com/doc/v3/merchant/4014569864 -func (srv *Marketing) Query(appid, openId, couponId string) (response *SendResp, err error) { +func (srv *Marketing) Query(appId, openId, couponId string) (response *SendResp, err error) { path := strings.Replace(queryPath, "{openid}", url.PathEscape(openId), -1) path = strings.Replace(path, "{coupon_id}", url.PathEscape(couponId), -1) var uv = url.Values{} - uv.Set("appid", appid) + uv.Set("appid", appId) path += "?" + uv.Encode() respBody, err := srv.Request2(utils.Host, utils.MethodGET, path, nil) @@ -59,3 +61,14 @@ func (srv *Marketing) Query(appid, openId, couponId string) (response *SendResp, return response, nil } + +// Notify . +func (srv *Marketing) Notify(_ context.Context, headers *http.Header, respBody []byte) (response string, err error) { + + bizStr, err := srv.GetDecodeBody(headers, respBody) + if err != nil { + return "", err + } + + return bizStr, nil +} diff --git a/internal/pkg/wechat/utils/aes.go b/internal/pkg/wechat/utils/aes.go new file mode 100644 index 0000000..d95fb4c --- /dev/null +++ b/internal/pkg/wechat/utils/aes.go @@ -0,0 +1,87 @@ +package utils + +import ( + "crypto/aes" + "crypto/cipher" + "encoding/base64" + "errors" + "fmt" +) + +const ( + keyLengthByte = 32 // AES-256 密钥长度(字节) + authTagLengthByte = 16 // GCM 认证标签长度(字节) +) + +// AesUtil 用于微信支付 AES-256-GCM 解密的工具类 +type AesUtil struct { + aesKey []byte // 32字节的AES密钥 +} + +// NewAesUtil 创建AesUtil实例,验证密钥长度 +func NewAesUtil(aesKey string) (*AesUtil, error) { + if len(aesKey) != keyLengthByte { + return nil, errors.New("无效的ApiV3Key,长度应为32个字节") + } + return &AesUtil{aesKey: []byte(aesKey)}, nil +} + +// DecryptToString 解密 AEAD_AES_256_GCM 加密的数据 +// associatedData: 附加认证数据 +// nonceStr: 随机数(12字节) +// ciphertext: 加密后的密文(Base64编码) +func (a *AesUtil) DecryptToString(associatedData, nonceStr, ciphertext string) (string, error) { + // 1. Base64解码密文 + cipherBytes, err := base64.StdEncoding.DecodeString(ciphertext) + if err != nil { + return "", fmt.Errorf("密文Base64解码失败: %w", err) + } + + // 2. 验证密文长度(需包含认证标签) + if len(cipherBytes) <= authTagLengthByte { + return "", errors.New("密文长度不足,无法解析认证标签") + } + + // 3. 分离密文和认证标签(GCM模式中,认证标签通常附加在密文末尾) + ctext := cipherBytes[:len(cipherBytes)-authTagLengthByte] + authTag := cipherBytes[len(cipherBytes)-authTagLengthByte:] + + // 4. 初始化AES-GCM加密器 + block, err := aes.NewCipher(a.aesKey) + if err != nil { + return "", fmt.Errorf("创建AES加密器失败: %w", err) + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return "", fmt.Errorf("创建GCM模式失败: %w", err) + } + + // 5. 构建附加数据(GCM的AAD) + additionalData := []byte(associatedData) + + // 6. 解密(GCM模式会自动验证认证标签) + plaintext, err := gcm.Open(nil, []byte(nonceStr), append(ctext, authTag...), additionalData) + if err != nil { + return "", fmt.Errorf("解密失败(可能是密钥错误或数据被篡改): %w", err) + } + + return string(plaintext), nil +} + +type Resource struct { + OriginalType string `json:"original_type"` + Algorithm string `json:"algorithm"` + Ciphertext string `json:"ciphertext"` // 如支付通知中的 encrypted_data + AssociatedData string `json:"associated_data"` // 微信支付回调的附加数据 通常为空字符串或回调相关信息 + Nonce string `json:"nonce"` // 12字节字符串 +} + +type WxNotifyBody struct { + Id string `json:"id"` + CreateTime string `json:"create_time"` + ResourceType string `json:"resource_type"` + EventType string `json:"event_type"` + Summary string `json:"summary"` + Resource Resource `json:"resource"` +} diff --git a/internal/pkg/wechat/utils/wxpay_utility.go b/internal/pkg/wechat/utils/wxpay_utility.go index f488688..15de6d0 100644 --- a/internal/pkg/wechat/utils/wxpay_utility.go +++ b/internal/pkg/wechat/utils/wxpay_utility.go @@ -42,6 +42,8 @@ type MchConfig struct { wechatPayPublicKeyFilePath string privateKey *rsa.PrivateKey wechatPayPublicKey *rsa.PublicKey + + aesKey string } // MchId 商户号 @@ -76,6 +78,7 @@ func CreateMchConfig( privateKeyFilePath string, wechatPayPublicKeyId string, wechatPayPublicKeyFilePath string, + aesKey string, ) (*MchConfig, error) { mchConfig := &MchConfig{ mchId: mchId, @@ -83,6 +86,7 @@ func CreateMchConfig( privateKeyFilePath: privateKeyFilePath, wechatPayPublicKeyId: wechatPayPublicKeyId, wechatPayPublicKeyFilePath: wechatPayPublicKeyFilePath, + aesKey: aesKey, } privateKey, err := LoadPrivateKeyWithPath(mchConfig.privateKeyFilePath) if err != nil { @@ -572,6 +576,35 @@ func (srv *MchConfig) Verify(request *http.Request) (string, error) { return EncryptOAEPWithPublicKey(string(respBody), srv.wechatPayPublicKey) } +func (srv *MchConfig) GetDecodeBody(headers *http.Header, respBody []byte) (string, error) { + + if respBody == nil { + return "", fmt.Errorf("request HttpBody is nil") + } + + err := ValidateResponse( + srv.WechatPayPublicKeyId(), + srv.WechatPayPublicKey(), + headers, + respBody, + ) + if err != nil { + return "", err + } + + var wxNotifyBody WxNotifyBody + if err = json.Unmarshal(respBody, &wxNotifyBody); err != nil { + return "", err + } + + aesUtil, err := NewAesUtil(srv.aesKey) + if err != nil { + return "", err + } + + return aesUtil.DecryptToString(wxNotifyBody.Resource.AssociatedData, wxNotifyBody.Resource.Nonce, wxNotifyBody.Resource.Ciphertext) +} + // BuildSortedQueryString 函数接受一个 map,返回按照字段名排序后的 URL 键值对格式字符串 func BuildSortedQueryString(params map[string]any) string { // 创建一个字符串切片,用于保存所有的键名 diff --git a/internal/pkg/wechat/utils/wxpay_utility_test.go b/internal/pkg/wechat/utils/wxpay_utility_test.go new file mode 100644 index 0000000..fc464bd --- /dev/null +++ b/internal/pkg/wechat/utils/wxpay_utility_test.go @@ -0,0 +1,68 @@ +package utils + +import ( + "crypto/rsa" + "reflect" + "testing" +) + +func TestMchConfig_Request(t *testing.T) { + type fields struct { + mchId string + certificateSerialNo string + privateKeyFilePath string + wechatPayPublicKeyId string + wechatPayPublicKeyFilePath string + privateKey *rsa.PrivateKey + wechatPayPublicKey *rsa.PublicKey + } + type args struct { + host string + method string + path string + reqBody []byte + } + tests := []struct { + name string + fields fields + args args + wantResponse []byte + wantErr bool + }{ + { + name: "test", + fields: fields{ + mchId: "1652322442", + certificateSerialNo: "certificateSerialNo", + privateKeyFilePath: "privateKeyFilePath", + wechatPayPublicKeyId: "wechatPayPublicKeyId", + wechatPayPublicKeyFilePath: "wechatPayPublicKeyFilePath", + }, + args: args{ + host: "https://api.mch.weixin.qq.com", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + srv := &MchConfig{ + mchId: tt.fields.mchId, + certificateSerialNo: tt.fields.certificateSerialNo, + privateKeyFilePath: tt.fields.privateKeyFilePath, + wechatPayPublicKeyId: tt.fields.wechatPayPublicKeyId, + wechatPayPublicKeyFilePath: tt.fields.wechatPayPublicKeyFilePath, + privateKey: tt.fields.privateKey, + wechatPayPublicKey: tt.fields.wechatPayPublicKey, + } + + gotResponse, err := srv.Request(tt.args.host, tt.args.method, tt.args.path, tt.args.reqBody) + if (err != nil) != tt.wantErr { + t.Errorf("Request() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(gotResponse, tt.wantResponse) { + t.Errorf("Request() gotResponse = %v, want %v", gotResponse, tt.wantResponse) + } + }) + } +} diff --git a/internal/server/http.go b/internal/server/http.go index 0941fe6..d6e9084 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -27,6 +27,7 @@ func NewHTTPServer( accessLogger *log2.AccessLogger, cmb *service.CmbService, tripartiteService *service.TripartiteService, + notifyService *service.NotifyService, ) *http.Server { //构建 server srv := buildHTTPServer(c, accessLogger, log) @@ -39,6 +40,11 @@ func NewHTTPServer( // 启星(启星-蓝色兄弟立减金代配) /voucher/qixing/v1/notify srv.Route("/voucher/").POST("qixing/v1/notify", tripartiteService.QiXingNotify) + // 通知url必须为公网可访问的URL,必须为HTTPS,不能携带参数 + // https://域名.cn/voucher/v1/notify/123456 123456为微信主体商户号 + // https://gateway.dev.cdlsxd.cn/voucher/v1/notify/123456 123456为微信主体商户号 测试环境 + // https://voucher.86698.cn/voucher/v1/notify/123456 123456为微信主体商户号 正式环境 + srv.Route("/voucher/").POST("/notify/v1/{mch_id}", notifyService.Notify) // 订单通知重试 -- 不健全 srv.Route("/voucher/").POST("orderNotifyRetry", cmb.OrderNotifyRetry) diff --git a/internal/service/notify.go b/internal/service/notify.go new file mode 100644 index 0000000..c34ac9f --- /dev/null +++ b/internal/service/notify.go @@ -0,0 +1,79 @@ +package service + +import ( + "fmt" + "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/transport/http" + "io" + http2 "net/http" + "voucher/internal/biz" + "voucher/internal/pkg/helper" +) + +type NotifyService struct { + WechatBiz *biz.WechatBiz + VoucherBiz *biz.VoucherBiz +} + +func NewNotifyService(wechatBiz *biz.WechatBiz, VoucherBiz *biz.VoucherBiz) *NotifyService { + return &NotifyService{WechatBiz: wechatBiz, VoucherBiz: VoucherBiz} +} + +// Notify https://pay.weixin.qq.com/doc/v3/merchant/4012285250 +func (srv *NotifyService) Notify(ctx http.Context) error { + + mchId := ctx.Vars().Get("mch_id") + if mchId == "" { + log.Errorf("微信回调通知,mchId参数不能为空") + return ctx.JSON(http2.StatusNetworkAuthenticationRequired, map[string]string{ + "code": "FAIL", + "message": "微信回调通知,mchId参数不能为空", + }) + } + + headers := ctx.Request().Header + if headers == nil { + log.Errorf("微信回调通知[%s],headers参数不能为空", mchId) + return ctx.JSON(http2.StatusNetworkAuthenticationRequired, map[string]string{ + "code": "FAIL", + "message": "微信回调通知,headers参数不能为空", + }) + } + + bodyBytes, err := io.ReadAll(ctx.Request().Body) + if err != nil { + log.Errorf("微信回调通知[%s],读取响应体失败: %v", mchId, err) + return fmt.Errorf("读取响应体失败: %w", err) + } + + if bodyBytes == nil { + log.Errorf("微信回调通知[%s],响应体不能为空", mchId) + return ctx.JSON(http2.StatusNetworkAuthenticationRequired, map[string]string{ + "code": "FAIL", + "message": "微信回调通知,响应体不能为空", + }) + } + + log.Warnf("微信回调通知[%s],headers:%+v,body:%s", mchId, headers, string(bodyBytes)) + + response, err := srv.WechatBiz.CallBack(ctx, mchId, &headers, bodyBytes) + if err != nil { + return ctx.JSON(http2.StatusNetworkAuthenticationRequired, map[string]string{ + "code": "FAIL", + "message": err.Error(), + }) + } + + ip := helper.GetClientIP(ctx) + + if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, "WECHAT_NOTIFY", response); err != nil { + + log.Errorf("微信回调通知[%s],headers:%+v,body:%s,err:%s", mchId, headers, string(bodyBytes), err.Error()) + return ctx.JSON(http2.StatusBadRequest, map[string]string{ + "code": "FAIL", + "message": err.Error(), + }) + } + + return ctx.JSON(http2.StatusOK, nil) +} diff --git a/internal/service/provider_set.go b/internal/service/provider_set.go index fd9e579..220ae5c 100644 --- a/internal/service/provider_set.go +++ b/internal/service/provider_set.go @@ -9,4 +9,5 @@ var ProviderSetService = wire.NewSet( NewVoucherService, NewCmbService, NewTripartiteService, + NewNotifyService, ) diff --git a/internal/service/voucher.go b/internal/service/voucher.go index b13c6d4..fddf7d1 100644 --- a/internal/service/voucher.go +++ b/internal/service/voucher.go @@ -133,5 +133,5 @@ func (v *VoucherService) WechatUseNotifyConsumer(ctx context.Context, tag, msg s return err } - return v.VoucherBiz.WechatNotifyConsumer(ctx, tag, req) + return v.VoucherBiz.WechatNotifyConsumer(ctx, "127.0.0.1", tag, req) } diff --git a/test/bank_multi_activity.go b/test/bank_multi_activity.go index 8edd220..84c0d2f 100644 --- a/test/bank_multi_activity.go +++ b/test/bank_multi_activity.go @@ -36,6 +36,7 @@ func marketing() *marketing2.Marketing { fmt.Sprintf("%s/%s", filePath, "wechat_private_key.pem"), // 商户API证书私钥文件路径,本地文件路径 wechatPayPublicKeyId, // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 fmt.Sprintf("%s/%s", filePath, "pub_key.pem"), // 微信支付公钥文件路径,本地文件路径 + "", ) if err != nil { panic(err) @@ -67,6 +68,37 @@ func marketingFJxw() *marketing2.Marketing { fmt.Sprintf("%s/%s", filePath, "wechat_private_key.pem"), // 商户API证书私钥文件路径,本地文件路径 wechatPayPublicKeyId, // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 fmt.Sprintf("%s/%s", filePath, "pub_key.pem"), // 微信支付公钥文件路径,本地文件路径 + "", + ) + if err != nil { + panic(err) + } + + return &marketing2.Marketing{MchConfig: c} +} + +func marketingFJLF() *marketing2.Marketing { + + dir, err := os.Getwd() + if err != nil { + panic(err) + } + + parentDir := filepath.Dir(dir) + + mchId := "1100040695" + wechatPayPublicKeyId := " PUB_KEY_ID_0111000406952026032500382251001000" + certificateSerialNo := "46712853869DB0EDAA9B4DF97DADEECD4CCDC85B" + + filePath := fmt.Sprintf("%s/cert/wechat/%s", parentDir, mchId) + + c, err := utils.CreateMchConfig( + mchId, // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756 + certificateSerialNo, // 商户API证书序列号,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013053053 + fmt.Sprintf("%s/%s", filePath, "wechat_private_key.pem"), // 商户API证书私钥文件路径,本地文件路径 + wechatPayPublicKeyId, // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 + fmt.Sprintf("%s/%s", filePath, "pub_key.pem"), // 微信支付公钥文件路径,本地文件路径 + "", ) if err != nil { panic(err) @@ -112,4 +144,5 @@ func MarketingSend() { func MarketingQuery(appId, openId, couponId string) (response *marketing2.SendResp, err error) { return marketingFJxw().Query(appId, openId, couponId) + //return marketingFJLF().Query(appId, openId, couponId) } diff --git a/test/bank_multi_activity_test.go b/test/bank_multi_activity_test.go index 057fcca..453983b 100644 --- a/test/bank_multi_activity_test.go +++ b/test/bank_multi_activity_test.go @@ -1,8 +1,11 @@ package test import ( + "context" "encoding/base64" "encoding/json" + "fmt" + "net/http" "testing" "time" "voucher/internal/biz/bo" @@ -90,3 +93,67 @@ func Test_QixingNotifyData(t *testing.T) { // {"content":"base64(微信通知json对象数据)","timestamp":1765447477945,"ciphertext":"md5(base64(微信通知json对象数据)+key)"} //t.Logf(`{"content":"%s","timestamp":122345677890,"ciphertext":"%s"}`, content, ciphertext) } + +func Test_MarketingNotify(t *testing.T) { + + header := `{ + "Content-Type": [ + "application/json" + ], + "Wechatpay-Nonce": [ + "dF8R9izUJnPBjVLa2cAcCaa7j6QUgitl" + ], + "Wechatpay-Serial": [ + "PUB_KEY_ID_0116523224422025061800192371001800" + ], + "Wechatpay-Signature": [ + "SoXIiTRTr6jofXXxGlfO+wyf1IzXFXcsfvEU2EggQfRKFu+8h3TT6QMQ8zIf8dpkkTPexB/3igGiATrR3uZY4ZeOpRrhIFHSJj0Ala0Ri2Nt4zk+MuBQnhybSYJ4Cn3/sHC4i2HFoOSil7OqlSr79hjod3h0tjYVQLtZ4+Cjp0IeMNB4p5qmIuERuhtfRqcyqXik9/uYNYxw8/Wkf1mMnTsBxyXK3iHAoinXNrEiqCCrQHCfnORMYosr7l+Ox8v9u1c8FFt+rt09vKssVCqYaZ/XRala3mjslDRiluFKSuqb7/JO3AxQjBK6M0iSZOlnmiXSIAq+UxJg4cem6wHi+g==" + ], + "Wechatpay-Signature-Type": [ + "WECHATPAY2-SHA256-RSA2048" + ], + "Wechatpay-Timestamp": [ + "1753690220" + ], + "X-Forwarded-For": [ + "121.51.58.168" + ], + "X-Forwarded-Proto": [ + "https" + ], + "X-Real-Ip": [ + "121.51.58.168" + ] + }` + body := `{ + "id": "fd06376a-3e1b-5516-81f8-9b69cf1ba416", + "create_time": "2025-07-28T16:10:15+08:00", + "resource_type": "encrypt-resource", + "event_type": "MCHTRANSFER.BILL.FINISHED", + "summary": "商家转账单据终态通知", + "resource": { + "original_type": "mch_payment", + "algorithm": "AEAD_AES_256_GCM", + "ciphertext": "XJBIhrHgbe9NR5q/jLYmZKdT/3xuKm2x7EFu3T52Hj2hjPzarRSA2HCsGTxGojfD+CFyJHIULlL2adqLijAjpi3B6TaYKY4LqhtJ/RYSQtYNxYvBpWX1yLOWe8luJbWxmQvKZxIekFs8lGVgkPBUw0IfEAvJ6jHAGCcgxLIqxgOf6UtGUqxCCNp/V3xy8zCiHB0Mvlw8eXCTuG+ZESJIXvloVGNS79R6iNeqk4kNKRSaV86MNh1KQlmoBxZ4yEshD/vIlMulU3xEc+mM25y8vUS4Ot6pxEpUdUyjwcb9QTwTTnZzm6i+VWYymcItAVBQrvsKBMmqWnPtNXG8++13k3DeO1LyVKURmnWXXT1mImmGx/teN/1xPV5y6nChu/HTbcJGDQy2twuq6TPFbbYlTjZH047z/ZtozJNvGNeh", + "associated_data": "mch_payment", + "nonce": "YN3eW5H8mxLs" + } + }` + + fmt.Print(header) + fmt.Print(body) + + httpHeaders := make(http.Header) + if err := json.Unmarshal([]byte(header), &httpHeaders); err != nil { + fmt.Printf("headers Unmarshal err: %+v\n", err) + return + } + + bizContent, err := marketingFJLF().Notify(context.Background(), &httpHeaders, []byte(body)) + if err != nil { + fmt.Printf("notify err: %+v\n", err) + return + } + + fmt.Printf("bizContent: %+v\n", bizContent) +} diff --git a/test/coupon.go b/test/coupon.go index f63f57e..a258dda 100644 --- a/test/coupon.go +++ b/test/coupon.go @@ -24,6 +24,7 @@ var bc = &conf.Bootstrap{ }, } +// bc2 Callback{NotifyUrl:https://nsall.86698.cn/wechatPay/coupon_notify/fjxingwang, Mchid:1652465541} var bc2 = &conf.Bootstrap{ Wechat: &conf.Wechat{ MchID: "1652465541", // notifyUrl https://nsall.86698.cn/wechatPay/coupon_notify/fjxingwang @@ -33,6 +34,15 @@ var bc2 = &conf.Bootstrap{ }, } +var bcFJLF = &conf.Bootstrap{ + Wechat: &conf.Wechat{ + MchID: "1100040695", + MchCertificateSerialNumber: "46712853869DB0EDAA9B4DF97DADEECD4CCDC85B", + WechatPayPublicKeyID: "PUB_KEY_ID_0111000406952026032500382251001000", + Name: "福建峦峰", + }, +} + func SendCoupon() { ctx := context.Background() @@ -223,7 +233,7 @@ func WxResp(wxResp *cashcoupons.Stock) (reps *do.WxResp) { return req } -func QueryCallback() { +func QueryCallback(bc *conf.Bootstrap) { ctx := context.Background() @@ -235,9 +245,9 @@ func QueryCallback() { parentDir := filepath.Dir(dir) server := data.Server{ - MchID: bc2.Wechat.MchID, - MchCertificateSerialNumber: bc2.Wechat.MchCertificateSerialNumber, - WechatPayPublicKeyID: bc2.Wechat.WechatPayPublicKeyID, + MchID: bc.Wechat.MchID, + MchCertificateSerialNumber: bc.Wechat.MchCertificateSerialNumber, + WechatPayPublicKeyID: bc.Wechat.WechatPayPublicKeyID, Dir: parentDir, } client, err := data.GetClient(ctx, server) @@ -249,7 +259,46 @@ func QueryCallback() { svc := cashcoupons.CallBackUrlApiService{Client: client} response, _, err := svc.QueryCallback(ctx, cashcoupons.QueryCallbackRequest{ - Mchid: core.String("1652465541"), + Mchid: core.String(bc.Wechat.MchID), + }) + if err != nil { + fmt.Println(err) + return + } + + fmt.Printf("\nresp:%+v\n", response) + return +} + +func SetCallback(bc *conf.Bootstrap) { + + ctx := context.Background() + + dir, err := os.Getwd() + if err != nil { + fmt.Printf("os.Getwd() error = %v", err) + return + } + parentDir := filepath.Dir(dir) + + server := data.Server{ + MchID: bc.Wechat.MchID, + MchCertificateSerialNumber: bc.Wechat.MchCertificateSerialNumber, + WechatPayPublicKeyID: bc.Wechat.WechatPayPublicKeyID, + Dir: parentDir, + } + client, err := data.GetClient(ctx, server) + if err != nil { + fmt.Println(err) + return + } + + svc := cashcoupons.CallBackUrlApiService{Client: client} + + response, _, err := svc.SetCallback(ctx, cashcoupons.SetCallbackRequest{ + Mchid: core.String(bc.Wechat.MchID), + NotifyUrl: core.String("https://gateway.dev.cdlsxd.cn/voucher/v1/notify/" + bc.Wechat.MchID), + Switch: core.Bool(true), }) if err != nil { fmt.Println(err) diff --git a/test/coupon_test.go b/test/coupon_test.go index 61daa6f..2edf776 100644 --- a/test/coupon_test.go +++ b/test/coupon_test.go @@ -60,7 +60,23 @@ func Test_QueryCallback(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - QueryCallback() + QueryCallback(bcFJLF) + }) + } +} + +func Test_SetCallback(t *testing.T) { + tests := []struct { + name string + }{ + { + name: "设置券核销通知地址", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + SetCallback(bcFJLF) }) } } From 813dfe5ef0397eed71490f145c981f544517b57b Mon Sep 17 00:00:00 2001 From: ziming Date: Wed, 25 Mar 2026 14:13:38 +0800 Subject: [PATCH 087/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/repo/order.go | 1 - internal/biz/wechat_notify.go | 27 +-------------------------- internal/data/repoimpl/order.go | 16 ---------------- 3 files changed, 1 insertion(+), 43 deletions(-) diff --git a/internal/biz/repo/order.go b/internal/biz/repo/order.go index 633ff9a..62a2a3d 100644 --- a/internal/biz/repo/order.go +++ b/internal/biz/repo/order.go @@ -19,7 +19,6 @@ type OrderRepo interface { GetByOutBizNo(ctx context.Context, t vo.OrderType, outBizNo string) (*bo.OrderBo, error) GetByOrderNo(ctx context.Context, orderNo string) (*bo.OrderBo, error) GetByCouponId(ctx context.Context, merchantNo, batchNo, voucherNo string) (*bo.OrderBo, error) - GetByTransactionId(ctx context.Context, stockCreatorMchId, stockID, transactionId string) (*bo.OrderBo, error) Create(ctx context.Context, req *bo.OrderBo) (*bo.OrderBo, error) GetByID(ctx context.Context, id uint64) (*bo.OrderBo, error) Ing(ctx context.Context, id uint64) error diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index 2ddc5bb..cf19609 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -2,9 +2,7 @@ package biz import ( "context" - "errors" "fmt" - "gorm.io/gorm" errPb "voucher/api/err" "voucher/internal/biz/bo" "voucher/internal/biz/vo" @@ -48,30 +46,7 @@ func (this *VoucherBiz) getOrder(ctx context.Context, req *bo.WechatVoucherNotif order, err := this.OrderRepo.GetByCouponId(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID, req.PlainText.CouponID) if err != nil { - - if !errors.Is(err, gorm.ErrRecordNotFound) { - return nil, err - } - - order, err = this.OrderRepo.GetByTransactionId(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID, req.PlainText.ConsumeInformation.TransactionID) - - if err != nil { - - if errors.Is(err, gorm.ErrRecordNotFound) { - - return nil, fmt.Errorf("微信回调消费,订单不存在,StockCreatorMchid:%s,StockID:%s,CouponID:%s,TransactionID:%s,CreateTime:%s", - req.PlainText.StockCreatorMchid, - req.PlainText.StockID, - req.PlainText.CouponID, - req.PlainText.ConsumeInformation.TransactionID, - req.PlainText.CreateTime, - ) - } - - return nil, err - } - - return order, nil + return nil, fmt.Errorf("订单查询错误 error: %v", err) } return order, nil diff --git a/internal/data/repoimpl/order.go b/internal/data/repoimpl/order.go index 8fa2c54..b42c4e1 100644 --- a/internal/data/repoimpl/order.go +++ b/internal/data/repoimpl/order.go @@ -372,22 +372,6 @@ func (p *OrderRepoImpl) GetByCouponId(ctx context.Context, merchantNo, batchNo, return p.ToBo(info), nil } -func (this *OrderRepoImpl) GetByTransactionId(ctx context.Context, stockCreatorMchId, stockID, transactionId string) (*bo.OrderBo, error) { - row := &model.Order{} - - tx := this.DB(ctx).Where(model.Order{MerchantNo: stockCreatorMchId, BatchNo: stockID, TransactionId: transactionId}).First(&row) - - if tx.Error != nil { - return nil, tx.Error - } - - if tx.RowsAffected == 0 { - return nil, gorm.ErrRecordNotFound - } - - return this.ToBo(row), nil -} - func (p *OrderRepoImpl) Ing(ctx context.Context, id uint64) error { now := time.Now() From ba4c95c7a6eb4952ae42cc00b1d5e0709a5a6163 Mon Sep 17 00:00:00 2001 From: ziming Date: Wed, 25 Mar 2026 14:22:35 +0800 Subject: [PATCH 088/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/server/http.go | 4 ++-- internal/service/notify.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/server/http.go b/internal/server/http.go index d6e9084..dbe207f 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -41,10 +41,10 @@ func NewHTTPServer( // 启星(启星-蓝色兄弟立减金代配) /voucher/qixing/v1/notify srv.Route("/voucher/").POST("qixing/v1/notify", tripartiteService.QiXingNotify) // 通知url必须为公网可访问的URL,必须为HTTPS,不能携带参数 - // https://域名.cn/voucher/v1/notify/123456 123456为微信主体商户号 + // https://域名.cn/voucher/v1/notify/123456 123456为微信主体商户号 /voucher/v1/notify/1100040695 // https://gateway.dev.cdlsxd.cn/voucher/v1/notify/123456 123456为微信主体商户号 测试环境 // https://voucher.86698.cn/voucher/v1/notify/123456 123456为微信主体商户号 正式环境 - srv.Route("/voucher/").POST("/notify/v1/{mch_id}", notifyService.Notify) + srv.Route("/voucher/").POST("/v1/notify/{mch_id}", notifyService.Notify) // 订单通知重试 -- 不健全 srv.Route("/voucher/").POST("orderNotifyRetry", cmb.OrderNotifyRetry) diff --git a/internal/service/notify.go b/internal/service/notify.go index c34ac9f..38610bc 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -46,7 +46,7 @@ func (srv *NotifyService) Notify(ctx http.Context) error { return fmt.Errorf("读取响应体失败: %w", err) } - if bodyBytes == nil { + if len(bodyBytes) == 0 { log.Errorf("微信回调通知[%s],响应体不能为空", mchId) return ctx.JSON(http2.StatusNetworkAuthenticationRequired, map[string]string{ "code": "FAIL", From d34403b012e19e12b184a509345ac4535c8f58e9 Mon Sep 17 00:00:00 2001 From: ziming Date: Wed, 25 Mar 2026 15:39:40 +0800 Subject: [PATCH 089/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/coupon.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/coupon.go b/test/coupon.go index a258dda..a2ca471 100644 --- a/test/coupon.go +++ b/test/coupon.go @@ -34,6 +34,8 @@ var bc2 = &conf.Bootstrap{ }, } +// bcFJLF Callback{NotifyUrl:https://gateway.dev.cdlsxd.cn/voucher/v1/notify/1100040695, Mchid:1100040695} +// /voucher/v1/notify/1100040695 var bcFJLF = &conf.Bootstrap{ Wechat: &conf.Wechat{ MchID: "1100040695", From 9c8f64a35a0408b1c7d5e6d9acbb43e823123b9b Mon Sep 17 00:00:00 2001 From: ziming Date: Wed, 25 Mar 2026 15:50:54 +0800 Subject: [PATCH 090/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/register_tag.go | 15 +++++++++++++-- internal/server/http.go | 1 + internal/service/notify.go | 4 ++-- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/internal/biz/register_tag.go b/internal/biz/register_tag.go index aa27147..5a4c902 100644 --- a/internal/biz/register_tag.go +++ b/internal/biz/register_tag.go @@ -25,8 +25,10 @@ func (this *VoucherBiz) RegisterTag(ctx context.Context, id int32) error { return err } - if err = this.registerNotifyTag(ctx, stock.MchId, stock.BatchNo); err != nil { - return err + if this.IsNotifyRegisterTag(stock) { + if err = this.registerNotifyTag(ctx, stock.MchId, stock.BatchNo); err != nil { + return err + } } _, err = this.ProductRepo.GetByProductNo(ctx, stock.ProductNo) @@ -34,6 +36,15 @@ func (this *VoucherBiz) RegisterTag(ctx context.Context, id int32) error { return err } +func (this *VoucherBiz) IsNotifyRegisterTag(product *bo.ProductBo) bool { + for _, subject := range this.bc.WechatSubject { + if subject.MchID == product.MchId { + return false + } + } + return true +} + func (this *VoucherBiz) registerNotifyTag(ctx context.Context, stockCreatorMchID, stockID string) error { cl := vo.WechatNotifyRegisterTagCacheLockKey.BuildCache([]string{this.bc.WechatNotifyMQ.Tag, stockCreatorMchID, stockID}) diff --git a/internal/server/http.go b/internal/server/http.go index dbe207f..cf919ee 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -46,6 +46,7 @@ func NewHTTPServer( // https://voucher.86698.cn/voucher/v1/notify/123456 123456为微信主体商户号 正式环境 srv.Route("/voucher/").POST("/v1/notify/{mch_id}", notifyService.Notify) + // ---脚本-- // 订单通知重试 -- 不健全 srv.Route("/voucher/").POST("orderNotifyRetry", cmb.OrderNotifyRetry) // 重试订单通知,查询微信状态再通知下游招行 diff --git a/internal/service/notify.go b/internal/service/notify.go index 38610bc..a347b71 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -54,7 +54,7 @@ func (srv *NotifyService) Notify(ctx http.Context) error { }) } - log.Warnf("微信回调通知[%s],headers:%+v,body:%s", mchId, headers, string(bodyBytes)) + log.Warnf("微信回调通知[%s],数据,headers:%+v,body:%s", mchId, headers, string(bodyBytes)) response, err := srv.WechatBiz.CallBack(ctx, mchId, &headers, bodyBytes) if err != nil { @@ -68,7 +68,7 @@ func (srv *NotifyService) Notify(ctx http.Context) error { if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, "WECHAT_NOTIFY", response); err != nil { - log.Errorf("微信回调通知[%s],headers:%+v,body:%s,err:%s", mchId, headers, string(bodyBytes), err.Error()) + log.Errorf("微信回调通知[%s],处理失败,headers:%+v,body:%s,err:%s", mchId, headers, string(bodyBytes), err.Error()) return ctx.JSON(http2.StatusBadRequest, map[string]string{ "code": "FAIL", "message": err.Error(), From 5ba27d7f7b970bb06287df8160e5d0c62c893ce2 Mon Sep 17 00:00:00 2001 From: ziming Date: Wed, 25 Mar 2026 17:10:27 +0800 Subject: [PATCH 091/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/pkg/request/request_test.go | 40 ++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/internal/pkg/request/request_test.go b/internal/pkg/request/request_test.go index 4f13710..eef583f 100644 --- a/internal/pkg/request/request_test.go +++ b/internal/pkg/request/request_test.go @@ -2,6 +2,8 @@ package request import ( "context" + "encoding/json" + "fmt" "net/http" "net/url" "testing" @@ -48,6 +50,7 @@ func Test_RequestHeaders(t *testing.T) { } func Test_RequestStatusCode(t *testing.T) { + uri := "http://example.com/api/update" body := []byte("update data") @@ -64,3 +67,40 @@ func Test_RequestStatusCode(t *testing.T) { t.Logf("响应体:", string(respBody)) t.Logf("响应头:", respHeader) } + +func Test_WxNotifyRequest(t *testing.T) { + + uri := "https://gateway.dev.cdlsxd.cn/voucher/v1/notify/1100040695" + + body := []byte(`{"id":"fd06376a-3e1b-5516-81f8-9b69cf1ba416","create_time":"2025-07-28T16:10:15+08:00","resource_type":"encrypt-resource","event_type":"MCHTRANSFER.BILL.FINISHED","summary":"商家转账单据终态通知","resource":{"original_type":"mch_payment","algorithm":"AEAD_AES_256_GCM","ciphertext":"XJBIhrHgbe9NR5q/jLYmZKdT/3xuKm2x7EFu3T52Hj2hjPzarRSA2HCsGTxGojfD+CFyJHIULlL2adqLijAjpi3B6TaYKY4LqhtJ/RYSQtYNxYvBpWX1yLOWe8luJbWxmQvKZxIekFs8lGVgkPBUw0IfEAvJ6jHAGCcgxLIqxgOf6UtGUqxCCNp/V3xy8zCiHB0Mvlw8eXCTuG+ZESJIXvloVGNS79R6iNeqk4kNKRSaV86MNh1KQlmoBxZ4yEshD/vIlMulU3xEc+mM25y8vUS4Ot6pxEpUdUyjwcb9QTwTTnZzm6i+VWYymcItAVBQrvsKBMmqWnPtNXG8++13k3DeO1LyVKURmnWXXT1mImmGx/teN/1xPV5y6nChu/HTbcJGDQy2twuq6TPFbbYlTjZH047z/ZtozJNvGNeh","associated_data":"mch_payment","nonce":"YN3eW5H8mxLs"}}`) + + hds := `{"Accept":["*/*"],"Cache-Control":["no-cache"],"Connection":["close"],"Content-Length":["775"],"Content-Type":["application/json"],"Pragma":["no-cache"],"User-Agent":["Mozilla/4.0"],"Wechatpay-Nonce":["dF8R9izUJnPBjVLa2cAcCaa7j6QUgitl"],"Wechatpay-Serial":["PUB_KEY_ID_0116523224422025061800192371001800"],"Wechatpay-Signature":["SoXIiTRTr6jofXXxGlfO+wyf1IzXFXcsfvEU2EggQfRKFu+8h3TT6QMQ8zIf8dpkkTPexB/3igGiATrR3uZY4ZeOpRrhIFHSJj0Ala0Ri2Nt4zk+MuBQnhybSYJ4Cn3/sHC4i2HFoOSil7OqlSr79hjod3h0tjYVQLtZ4+Cjp0IeMNB4p5qmIuERuhtfRqcyqXik9/uYNYxw8/Wkf1mMnTsBxyXK3iHAoinXNrEiqCCrQHCfnORMYosr7l+Ox8v9u1c8FFt+rt09vKssVCqYaZ/XRala3mjslDRiluFKSuqb7/JO3AxQjBK6M0iSZOlnmiXSIAq+UxJg4cem6wHi+g=="],"Wechatpay-Signature-Type":["WECHATPAY2-SHA256-RSA2048"],"Wechatpay-Timestamp":["1753690220"],"X-Forwarded-For":["121.51.58.168"],"X-Forwarded-Proto":["https"],"X-Real-Ip":["121.51.58.168"]}` + + var headerMap http.Header + if err := json.Unmarshal([]byte(hds), &headerMap); err != nil { + t.Error(fmt.Sprintf("解析 headers 失败: %v", err)) + return + } + + hc := &http.Client{ + Timeout: 10 * time.Second, + Transport: &http.Transport{ + MaxIdleConns: 1, // 最大空闲连接数 + MaxIdleConnsPerHost: 1, // 每个主机的最大空闲连接数 + IdleConnTimeout: 10 * time.Second, // 空闲连接超时时间 + }, + } + + isSuccess := func(code int) bool { + return code == http.StatusOK || code == http.StatusCreated + } + + respHeader, respBody, err := Post(context.Background(), uri, body, WithHttpClient(hc), WithStatusCodeFunc(isSuccess)) + if err != nil { + t.Error(err) + return + } + + t.Logf("响应体:%s", string(respBody)) + t.Logf("响应头:%v", respHeader) +} From 7c851ddb1b4f7abf2a75565cc2dab3aeb6114d39 Mon Sep 17 00:00:00 2001 From: ziming Date: Wed, 25 Mar 2026 17:29:00 +0800 Subject: [PATCH 092/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/wechat_notify.go | 6 +++--- internal/service/notify.go | 2 +- internal/service/voucher.go | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index cf19609..ac73bc1 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -9,9 +9,9 @@ import ( "voucher/internal/pkg/lock" ) -func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, ip, tag string, req *bo.WechatVoucherNotifyBo) error { +func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, ip string, req *bo.WechatVoucherNotifyBo) error { - c := vo.WechatNotifyConsumeLockKey.BuildCache([]string{tag, req.PlainText.StockID, req.PlainText.CouponID}) + c := vo.WechatNotifyConsumeLockKey.BuildCache([]string{req.PlainText.StockCreatorMchid, req.PlainText.StockID, req.PlainText.CouponID}) return lock.NewMutex(this.rdb.Rdb, c.TTL).Lock(ctx, c.Key, func(ctx context.Context) error { @@ -21,7 +21,7 @@ func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, ip, tag string } if order.ActivityId != "" { - return this.MultiBiz.Run(ctx, ip, "lsxd_"+req.PlainText.StockCreatorMchid, req, order) + return this.MultiBiz.Run(ctx, ip, req.PlainText.StockCreatorMchid, req, order) } if req.PlainText.Status.IsSended() { diff --git a/internal/service/notify.go b/internal/service/notify.go index a347b71..01dfb5b 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -66,7 +66,7 @@ func (srv *NotifyService) Notify(ctx http.Context) error { ip := helper.GetClientIP(ctx) - if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, "WECHAT_NOTIFY", response); err != nil { + if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, response); err != nil { log.Errorf("微信回调通知[%s],处理失败,headers:%+v,body:%s,err:%s", mchId, headers, string(bodyBytes), err.Error()) return ctx.JSON(http2.StatusBadRequest, map[string]string{ diff --git a/internal/service/voucher.go b/internal/service/voucher.go index fddf7d1..07b3211 100644 --- a/internal/service/voucher.go +++ b/internal/service/voucher.go @@ -133,5 +133,5 @@ func (v *VoucherService) WechatUseNotifyConsumer(ctx context.Context, tag, msg s return err } - return v.VoucherBiz.WechatNotifyConsumer(ctx, "127.0.0.1", tag, req) + return v.VoucherBiz.WechatNotifyConsumer(ctx, "127.0.0.1", req) } From cf9ecc4486757041f54e69c17034c4217aa41792 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 10:14:59 +0800 Subject: [PATCH 093/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/wechat_notify.go | 7 +++++++ internal/service/notify.go | 2 ++ test/coupon.go | 11 ++++++----- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index ac73bc1..18c0f8f 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -21,6 +21,9 @@ func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, ip string, req } if order.ActivityId != "" { + if err = req.Validate(); err != nil { + return fmt.Errorf("multi validate req error: %v", err) + } return this.MultiBiz.Run(ctx, ip, req.PlainText.StockCreatorMchid, req, order) } @@ -43,6 +46,10 @@ func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, ip string, req func (this *VoucherBiz) getOrder(ctx context.Context, req *bo.WechatVoucherNotifyBo) (*bo.OrderBo, error) { + if req.PlainText.StockCreatorMchid == "" || req.PlainText.StockID == "" || req.PlainText.CouponID == "" { + return nil, fmt.Errorf("订单查询参数错误") + } + order, err := this.OrderRepo.GetByCouponId(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID, req.PlainText.CouponID) if err != nil { diff --git a/internal/service/notify.go b/internal/service/notify.go index 01dfb5b..9c35c63 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -66,6 +66,8 @@ func (srv *NotifyService) Notify(ctx http.Context) error { ip := helper.GetClientIP(ctx) + log.Warnf("微信回调通知[%s],解析数据,response:%+v", mchId, response) + if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, response); err != nil { log.Errorf("微信回调通知[%s],处理失败,headers:%+v,body:%s,err:%s", mchId, headers, string(bodyBytes), err.Error()) diff --git a/test/coupon.go b/test/coupon.go index a2ca471..8cccda8 100644 --- a/test/coupon.go +++ b/test/coupon.go @@ -24,8 +24,8 @@ var bc = &conf.Bootstrap{ }, } -// bc2 Callback{NotifyUrl:https://nsall.86698.cn/wechatPay/coupon_notify/fjxingwang, Mchid:1652465541} -var bc2 = &conf.Bootstrap{ +// bcFJXW Callback{NotifyUrl:https://nsall.86698.cn/wechatPay/coupon_notify/fjxingwang, Mchid:1652465541} +var bcFJXW = &conf.Bootstrap{ Wechat: &conf.Wechat{ MchID: "1652465541", // notifyUrl https://nsall.86698.cn/wechatPay/coupon_notify/fjxingwang MchCertificateSerialNumber: "1E3F2CE013203BA9C3DEFC5782FCD3329C3DAC1C", @@ -169,8 +169,8 @@ func QueryProduct() { } req := cashcoupons.QueryStockRequest{ - StockId: core.String("21502886"), - StockCreatorMchid: core.String("1652465541"), + StockId: core.String("21923564"), + StockCreatorMchid: core.String("1100040695"), } svc := cashcoupons.StockApiService{Client: client} @@ -195,7 +195,8 @@ func QueryProduct() { fmt.Printf("\n剩余库存:%d", availableStock) fmt.Printf("\n剩余预算:%d", availableStock*couponAmount) - fmt.Printf("\nWxResp:%+v", WxResp(resp)) + str, _ := json.Marshal(WxResp(resp)) + fmt.Printf("\nWxResp:%+v", string(str)) return } From d5460e9415f2b44ef0cb8848e733ecb71fe9b492 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 10:44:29 +0800 Subject: [PATCH 094/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wechatrepoimpl/bank_multi_activity.go | 14 ++++++++++-- .../pkg/wechat/srv/marketing/marketing.go | 8 +++---- internal/pkg/wechat/utils/aes_test.go | 22 +++++++++++++++++++ internal/pkg/wechat/utils/wxpay_utility.go | 17 +++++++++----- 4 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 internal/pkg/wechat/utils/aes_test.go diff --git a/internal/data/wechatrepoimpl/bank_multi_activity.go b/internal/data/wechatrepoimpl/bank_multi_activity.go index 58bd146..f0c9b00 100644 --- a/internal/data/wechatrepoimpl/bank_multi_activity.go +++ b/internal/data/wechatrepoimpl/bank_multi_activity.go @@ -66,14 +66,24 @@ func (w *BankMultiActivityImpl) Notify(ctx context.Context, mchId string, header return } - decodeBodyStr, err := t.Notify(ctx, headers, respBody) + body, decodeBodyStr, err := t.Notify(ctx, headers, respBody) if err != nil { return } + var plainText bo.PlainText if err = json.Unmarshal([]byte(decodeBodyStr), &response); err != nil { return nil, err } - return + return &bo.WechatVoucherNotifyBo{ + ID: body.Id, + CreateTime: body.CreateTime, + ResourceType: body.ResourceType, + EventType: body.EventType, + Summary: body.Summary, + //OriginalType: body.OriginalType, + //AssociatedData: body.AssociatedData, + PlainText: plainText, + }, nil } diff --git a/internal/pkg/wechat/srv/marketing/marketing.go b/internal/pkg/wechat/srv/marketing/marketing.go index 71e0894..362776a 100644 --- a/internal/pkg/wechat/srv/marketing/marketing.go +++ b/internal/pkg/wechat/srv/marketing/marketing.go @@ -63,12 +63,12 @@ func (srv *Marketing) Query(appId, openId, couponId string) (response *SendResp, } // Notify . -func (srv *Marketing) Notify(_ context.Context, headers *http.Header, respBody []byte) (response string, err error) { +func (srv *Marketing) Notify(_ context.Context, headers *http.Header, respBody []byte) (body *utils.WxNotifyBody, response string, err error) { - bizStr, err := srv.GetDecodeBody(headers, respBody) + wxNotifyBody, bizStr, err := srv.GetDecodeBody(headers, respBody) if err != nil { - return "", err + return nil, "", err } - return bizStr, nil + return wxNotifyBody, bizStr, nil } diff --git a/internal/pkg/wechat/utils/aes_test.go b/internal/pkg/wechat/utils/aes_test.go new file mode 100644 index 0000000..7a4e4de --- /dev/null +++ b/internal/pkg/wechat/utils/aes_test.go @@ -0,0 +1,22 @@ +package utils + +import ( + "testing" +) + +func TestAes(t *testing.T) { + + aesUtil, err := NewAesUtil("d9af70585b18ae206d981548c766563f") + if err != nil { + t.Errorf("NewAesUtil() error = %v", err) + return + } + + gotResponse, err := aesUtil.DecryptToString("coupon", "pV96KsdLrSAd", "IfJmKJnBYezruwmk+IA+ENQBn13oWiLR1YnYi3bhYaqYreaXwFcFWbnnQjWfh8RDF36S1v+H1eLj0OtiQVDveLJ9vOcCrtr0M8bkV6NpRN7amqcf02vg+xLcs6UiZDedkN353nMnVVY1SAyuJj0AjKTUJrMhPNtxzdPpL/A3bMVCHrw4sqUItAOsDWVyJ5Kb09BEbdNOesmFkCc+Pcqmr554UHXzjQxzYimMpdbZ8OCovEy6JY1jnbxyiduDI7XBiTvxLr5CzLGlxNk1tIHUSrrDnRzUCpFJzNETgcrF3JWvQVAPThelGnTLN/TT3/pyM7/Kz60YNGaSbJTNKOtxbLXopk72x5hlt45fgrueP+RsnVQWmjULJ0AVEVmPNtlHTYRw7WV0cGwwrF7CCSDifYPed9QZPU1+awVtnGb3If53l109HojJiJB7Y0C/ZPX/vH/KLmtUx+10YKIFOo3vYnxEBV5lv4D5ZqXn4gOOtufVH/URYqgRqnzNFqJV6sW2qP6K9AgBhe/BO4AoeDDHG00XP89XFbeHmR2zZ019jBapq92YkvDVtQc2oLr9MWPoHJUhkmApwB6AmOK7ldWJ14P/Fws3/zBzCxVSlQsgLoDyBKm461ZnA6k09c+hffhTutLt89HYv+pqA1nukdTGuCWqPqUgx0AsK5i1CngrBQZJwc7+ylpakpOrX8oulyuA3So65hgMuqgl5dgkR0e8nvjjv7dVl0aRkqTZt3VL7BB1xwGqbvO3aTEuqqPrFQKLpnaY5Gz64dgg6huQFOBnZww=") + if err != nil { + t.Errorf("DecryptToString() error = %v", err) + return + } + + t.Log(gotResponse) +} diff --git a/internal/pkg/wechat/utils/wxpay_utility.go b/internal/pkg/wechat/utils/wxpay_utility.go index 15de6d0..95ba98f 100644 --- a/internal/pkg/wechat/utils/wxpay_utility.go +++ b/internal/pkg/wechat/utils/wxpay_utility.go @@ -576,10 +576,10 @@ func (srv *MchConfig) Verify(request *http.Request) (string, error) { return EncryptOAEPWithPublicKey(string(respBody), srv.wechatPayPublicKey) } -func (srv *MchConfig) GetDecodeBody(headers *http.Header, respBody []byte) (string, error) { +func (srv *MchConfig) GetDecodeBody(headers *http.Header, respBody []byte) (*WxNotifyBody, string, error) { if respBody == nil { - return "", fmt.Errorf("request HttpBody is nil") + return nil, "", fmt.Errorf("request HttpBody is nil") } err := ValidateResponse( @@ -589,20 +589,25 @@ func (srv *MchConfig) GetDecodeBody(headers *http.Header, respBody []byte) (stri respBody, ) if err != nil { - return "", err + return nil, "", err } var wxNotifyBody WxNotifyBody if err = json.Unmarshal(respBody, &wxNotifyBody); err != nil { - return "", err + return nil, "", err } aesUtil, err := NewAesUtil(srv.aesKey) if err != nil { - return "", err + return nil, "", err } - return aesUtil.DecryptToString(wxNotifyBody.Resource.AssociatedData, wxNotifyBody.Resource.Nonce, wxNotifyBody.Resource.Ciphertext) + decryptedText, err := aesUtil.DecryptToString(wxNotifyBody.Resource.AssociatedData, wxNotifyBody.Resource.Nonce, wxNotifyBody.Resource.Ciphertext) + if err != nil { + return nil, "", err + } + + return &wxNotifyBody, decryptedText, nil } // BuildSortedQueryString 函数接受一个 map,返回按照字段名排序后的 URL 键值对格式字符串 From 2735d8d71dfda0cbd068e1fed84f9d98681f0ad4 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 10:52:22 +0800 Subject: [PATCH 095/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/pkg/wechat/utils/aes_test.go | 2 +- internal/service/notify.go | 6 ++++-- test/bank_multi_activity_test.go | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/internal/pkg/wechat/utils/aes_test.go b/internal/pkg/wechat/utils/aes_test.go index 7a4e4de..21f4d12 100644 --- a/internal/pkg/wechat/utils/aes_test.go +++ b/internal/pkg/wechat/utils/aes_test.go @@ -12,7 +12,7 @@ func TestAes(t *testing.T) { return } - gotResponse, err := aesUtil.DecryptToString("coupon", "pV96KsdLrSAd", "IfJmKJnBYezruwmk+IA+ENQBn13oWiLR1YnYi3bhYaqYreaXwFcFWbnnQjWfh8RDF36S1v+H1eLj0OtiQVDveLJ9vOcCrtr0M8bkV6NpRN7amqcf02vg+xLcs6UiZDedkN353nMnVVY1SAyuJj0AjKTUJrMhPNtxzdPpL/A3bMVCHrw4sqUItAOsDWVyJ5Kb09BEbdNOesmFkCc+Pcqmr554UHXzjQxzYimMpdbZ8OCovEy6JY1jnbxyiduDI7XBiTvxLr5CzLGlxNk1tIHUSrrDnRzUCpFJzNETgcrF3JWvQVAPThelGnTLN/TT3/pyM7/Kz60YNGaSbJTNKOtxbLXopk72x5hlt45fgrueP+RsnVQWmjULJ0AVEVmPNtlHTYRw7WV0cGwwrF7CCSDifYPed9QZPU1+awVtnGb3If53l109HojJiJB7Y0C/ZPX/vH/KLmtUx+10YKIFOo3vYnxEBV5lv4D5ZqXn4gOOtufVH/URYqgRqnzNFqJV6sW2qP6K9AgBhe/BO4AoeDDHG00XP89XFbeHmR2zZ019jBapq92YkvDVtQc2oLr9MWPoHJUhkmApwB6AmOK7ldWJ14P/Fws3/zBzCxVSlQsgLoDyBKm461ZnA6k09c+hffhTutLt89HYv+pqA1nukdTGuCWqPqUgx0AsK5i1CngrBQZJwc7+ylpakpOrX8oulyuA3So65hgMuqgl5dgkR0e8nvjjv7dVl0aRkqTZt3VL7BB1xwGqbvO3aTEuqqPrFQKLpnaY5Gz64dgg6huQFOBnZww=") + gotResponse, err := aesUtil.DecryptToString("coupon", "EGvd12Nf2Z1X", "+sUnGECvoHvsFE0ZnTT1ij/b8TKUxYS9jRh3gUSZbsO0WXEgXnwOvfOQjugnqx58lmYafa4BEoJ08c8La0HJpzZUthd6OEkcYlELcQOfqr72sikfFa5izbsNHh+hqd0fmUmpioyCre/BcrDneFqrOShrcLqUdb7FugI362Q1wgCgVJ1DqbGFvQuLHSHZ+UG0HY7MF7lt5r1w72tklHuvbxdfJZ2Vaj5muRUUpZIsZDZ76pqDlsTepxt5upWeVOBMLHjXCMEIJzcV0jF5tFgn7GTea/DORmRLEpY03uMiAyTYYaVbww1S0b96VuoTjVAAf0+oeiLtn+ZoWO1ZL5+Rbz1GTxivluoD+WVn/V5WUYNYnftiQ0LZcigxXIp+DYJOnuoLMDdWQCih4bElJGZnFuJIWpkS73Dn3HfbAx8lg0vq882PY/tk0bOXH6yrWGT9DO6ZHnPNABy4pb6xLRJtvmflSDhYc5Fa7qn0ySRAIK8e4C+z7aeKhzhWpNe2kGyjKKQqN92j9kMjsbfDqOSPRVB3FIxOvbzrJyR3erkN2WYRzTCZ6U7Qbgg/+tdWPdYNhAMrVbdmp2J1/daetCmmrd2HIIB6HqhsHYQMu0/NxQVPLGXuHzHY9fuNiYczST6KobLOFQ0fEO2dx6AFNImwHxHbdSHpchUqcE6sdjfoHNgSW7Jv4Fclm8GDqQmu4l6/ZhJrpn+I4ePijo7U/Ewh4KrCBHiw0JEttcZuBDqMM1Q9rmI4trcfekT2wy7HGgK9qetuOHMC5ENfO/U1gpa4wIU=") if err != nil { t.Errorf("DecryptToString() error = %v", err) return diff --git a/internal/service/notify.go b/internal/service/notify.go index 9c35c63..2dacb6d 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -1,6 +1,7 @@ package service import ( + "encoding/json" "fmt" "github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/transport/http" @@ -54,7 +55,8 @@ func (srv *NotifyService) Notify(ctx http.Context) error { }) } - log.Warnf("微信回调通知[%s],数据,headers:%+v,body:%s", mchId, headers, string(bodyBytes)) + headerJson, _ := json.Marshal(headers) + log.Warnf("微信回调通知[%s],数据,headers:%s,body:%s", mchId, string(headerJson), string(bodyBytes)) response, err := srv.WechatBiz.CallBack(ctx, mchId, &headers, bodyBytes) if err != nil { @@ -70,7 +72,7 @@ func (srv *NotifyService) Notify(ctx http.Context) error { if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, response); err != nil { - log.Errorf("微信回调通知[%s],处理失败,headers:%+v,body:%s,err:%s", mchId, headers, string(bodyBytes), err.Error()) + log.Errorf("微信回调通知[%s],处理失败,headers:%s,body:%s,err:%s", mchId, headerJson, string(bodyBytes), err.Error()) return ctx.JSON(http2.StatusBadRequest, map[string]string{ "code": "FAIL", "message": err.Error(), diff --git a/test/bank_multi_activity_test.go b/test/bank_multi_activity_test.go index 453983b..8b39e4c 100644 --- a/test/bank_multi_activity_test.go +++ b/test/bank_multi_activity_test.go @@ -149,7 +149,7 @@ func Test_MarketingNotify(t *testing.T) { return } - bizContent, err := marketingFJLF().Notify(context.Background(), &httpHeaders, []byte(body)) + _, bizContent, err := marketingFJLF().Notify(context.Background(), &httpHeaders, []byte(body)) if err != nil { fmt.Printf("notify err: %+v\n", err) return From 6a06e1fed47362d65c0579e081922dabcf50d666 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 10:56:15 +0800 Subject: [PATCH 096/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data/wechatrepoimpl/bank_multi_activity.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/data/wechatrepoimpl/bank_multi_activity.go b/internal/data/wechatrepoimpl/bank_multi_activity.go index f0c9b00..a18a6bc 100644 --- a/internal/data/wechatrepoimpl/bank_multi_activity.go +++ b/internal/data/wechatrepoimpl/bank_multi_activity.go @@ -77,13 +77,13 @@ func (w *BankMultiActivityImpl) Notify(ctx context.Context, mchId string, header } return &bo.WechatVoucherNotifyBo{ - ID: body.Id, - CreateTime: body.CreateTime, - ResourceType: body.ResourceType, - EventType: body.EventType, - Summary: body.Summary, - //OriginalType: body.OriginalType, - //AssociatedData: body.AssociatedData, - PlainText: plainText, + ID: body.Id, + CreateTime: body.CreateTime, + ResourceType: body.ResourceType, + EventType: body.EventType, + Summary: body.Summary, + OriginalType: body.Resource.OriginalType, + AssociatedData: body.Resource.AssociatedData, + PlainText: plainText, }, nil } From 5051e2e744712e8b4c7b8c7278e3e56f912eb4df Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 10:57:16 +0800 Subject: [PATCH 097/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/notify.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/internal/service/notify.go b/internal/service/notify.go index 2dacb6d..4425565 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -55,11 +55,11 @@ func (srv *NotifyService) Notify(ctx http.Context) error { }) } - headerJson, _ := json.Marshal(headers) - log.Warnf("微信回调通知[%s],数据,headers:%s,body:%s", mchId, string(headerJson), string(bodyBytes)) - response, err := srv.WechatBiz.CallBack(ctx, mchId, &headers, bodyBytes) if err != nil { + headerJson, _ := json.Marshal(headers) + log.Errorf("微信回调通知[%s],CallBack处理失败,headers:%s,body:%s,err:%s", mchId, headerJson, string(bodyBytes), err.Error()) + return ctx.JSON(http2.StatusNetworkAuthenticationRequired, map[string]string{ "code": "FAIL", "message": err.Error(), @@ -72,7 +72,9 @@ func (srv *NotifyService) Notify(ctx http.Context) error { if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, response); err != nil { - log.Errorf("微信回调通知[%s],处理失败,headers:%s,body:%s,err:%s", mchId, headerJson, string(bodyBytes), err.Error()) + headerJson, _ := json.Marshal(headers) + log.Errorf("微信回调通知[%s],Consumer处理失败,headers:%s,body:%s,err:%s", mchId, headerJson, string(bodyBytes), err.Error()) + return ctx.JSON(http2.StatusBadRequest, map[string]string{ "code": "FAIL", "message": err.Error(), From 129b849b02564c9ddfbd913e665d8295e1aa25fe Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 10:57:57 +0800 Subject: [PATCH 098/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/notify.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/service/notify.go b/internal/service/notify.go index 4425565..e56c03c 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -68,12 +68,10 @@ func (srv *NotifyService) Notify(ctx http.Context) error { ip := helper.GetClientIP(ctx) - log.Warnf("微信回调通知[%s],解析数据,response:%+v", mchId, response) - if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, response); err != nil { headerJson, _ := json.Marshal(headers) - log.Errorf("微信回调通知[%s],Consumer处理失败,headers:%s,body:%s,err:%s", mchId, headerJson, string(bodyBytes), err.Error()) + log.Errorf("微信回调通知[%s],Consumer处理失败,headers:%s,body:%s,解析数据:%+v,err:%s", mchId, headerJson, string(bodyBytes), response, err.Error()) return ctx.JSON(http2.StatusBadRequest, map[string]string{ "code": "FAIL", From 6e57c2afe5ff37f8bb831b33661b1a3ca20034aa Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 10:59:24 +0800 Subject: [PATCH 099/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/notify.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/notify.go b/internal/service/notify.go index e56c03c..2222ece 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -71,7 +71,7 @@ func (srv *NotifyService) Notify(ctx http.Context) error { if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, response); err != nil { headerJson, _ := json.Marshal(headers) - log.Errorf("微信回调通知[%s],Consumer处理失败,headers:%s,body:%s,解析数据:%+v,err:%s", mchId, headerJson, string(bodyBytes), response, err.Error()) + log.Errorf("微信回调通知[%s],Consumer处理失败,headers:%s,body:%s\n解析数据:%+v,err:%s", mchId, headerJson, string(bodyBytes), response, err.Error()) return ctx.JSON(http2.StatusBadRequest, map[string]string{ "code": "FAIL", From 00e637d3838ba1b3e495f159567e0d02f02f71f2 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 11:17:06 +0800 Subject: [PATCH 100/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/pkg/wechat/utils/wxpay_utility.go | 15 +++-- test/bank_multi_activity.go | 5 +- test/bank_multi_activity_test.go | 73 +++++++++------------- 3 files changed, 40 insertions(+), 53 deletions(-) diff --git a/internal/pkg/wechat/utils/wxpay_utility.go b/internal/pkg/wechat/utils/wxpay_utility.go index 95ba98f..1089bbe 100644 --- a/internal/pkg/wechat/utils/wxpay_utility.go +++ b/internal/pkg/wechat/utils/wxpay_utility.go @@ -19,7 +19,6 @@ import ( "net/url" "os" "sort" - "strconv" "strings" "time" ) @@ -297,13 +296,13 @@ func ValidateResponse( nonce := headers.Get(WechatPayNonce) // 拒绝过期请求 - timestamp, err := strconv.ParseInt(timestampStr, 10, 64) - if err != nil { - return fmt.Errorf("invalid timestamp: %v", err) - } - if time.Now().Sub(time.Unix(timestamp, 0)) > 5*time.Minute { - return errors.New("invalid timestamp") - } + //timestamp, err := strconv.ParseInt(timestampStr, 10, 64) + //if err != nil { + // return fmt.Errorf("invalid timestamp: %v", err) + //} + //if time.Now().Sub(time.Unix(timestamp, 0)) > 5*time.Minute { + // return errors.New("invalid timestamp") + //} if serialNo != wechatpayPublicKeyId { return fmt.Errorf( diff --git a/test/bank_multi_activity.go b/test/bank_multi_activity.go index 84c0d2f..1c137f4 100644 --- a/test/bank_multi_activity.go +++ b/test/bank_multi_activity.go @@ -87,10 +87,11 @@ func marketingFJLF() *marketing2.Marketing { parentDir := filepath.Dir(dir) mchId := "1100040695" - wechatPayPublicKeyId := " PUB_KEY_ID_0111000406952026032500382251001000" + wechatPayPublicKeyId := "PUB_KEY_ID_0111000406952026032500382251001000" certificateSerialNo := "46712853869DB0EDAA9B4DF97DADEECD4CCDC85B" filePath := fmt.Sprintf("%s/cert/wechat/%s", parentDir, mchId) + fmt.Printf("filePath: %s\n", filePath) c, err := utils.CreateMchConfig( mchId, // 商户号,是由微信支付系统生成并分配给每个商户的唯一标识符,商户号获取方式参考 https://pay.weixin.qq.com/doc/v3/merchant/4013070756 @@ -98,7 +99,7 @@ func marketingFJLF() *marketing2.Marketing { fmt.Sprintf("%s/%s", filePath, "wechat_private_key.pem"), // 商户API证书私钥文件路径,本地文件路径 wechatPayPublicKeyId, // 微信支付公钥ID,如何获取请参考 https://pay.weixin.qq.com/doc/v3/merchant/4013038816 fmt.Sprintf("%s/%s", filePath, "pub_key.pem"), // 微信支付公钥文件路径,本地文件路径 - "", + "d9af70585b18ae206d981548c766563f", ) if err != nil { panic(err) diff --git a/test/bank_multi_activity_test.go b/test/bank_multi_activity_test.go index 8b39e4c..4537df9 100644 --- a/test/bank_multi_activity_test.go +++ b/test/bank_multi_activity_test.go @@ -97,51 +97,38 @@ func Test_QixingNotifyData(t *testing.T) { func Test_MarketingNotify(t *testing.T) { header := `{ - "Content-Type": [ - "application/json" - ], - "Wechatpay-Nonce": [ - "dF8R9izUJnPBjVLa2cAcCaa7j6QUgitl" - ], - "Wechatpay-Serial": [ - "PUB_KEY_ID_0116523224422025061800192371001800" - ], - "Wechatpay-Signature": [ - "SoXIiTRTr6jofXXxGlfO+wyf1IzXFXcsfvEU2EggQfRKFu+8h3TT6QMQ8zIf8dpkkTPexB/3igGiATrR3uZY4ZeOpRrhIFHSJj0Ala0Ri2Nt4zk+MuBQnhybSYJ4Cn3/sHC4i2HFoOSil7OqlSr79hjod3h0tjYVQLtZ4+Cjp0IeMNB4p5qmIuERuhtfRqcyqXik9/uYNYxw8/Wkf1mMnTsBxyXK3iHAoinXNrEiqCCrQHCfnORMYosr7l+Ox8v9u1c8FFt+rt09vKssVCqYaZ/XRala3mjslDRiluFKSuqb7/JO3AxQjBK6M0iSZOlnmiXSIAq+UxJg4cem6wHi+g==" - ], - "Wechatpay-Signature-Type": [ - "WECHATPAY2-SHA256-RSA2048" - ], - "Wechatpay-Timestamp": [ - "1753690220" - ], - "X-Forwarded-For": [ - "121.51.58.168" - ], - "X-Forwarded-Proto": [ - "https" - ], - "X-Real-Ip": [ - "121.51.58.168" - ] - }` + "Content-Type": [ + "application/json" + ], + "Wechatpay-Nonce": [ + "H3Bs5sCbMJZOBLA4w3S6YbN4cCcO9i00" + ], + "Wechatpay-Serial": [ + "PUB_KEY_ID_0111000406952026032500382251001000" + ], + "Wechatpay-Signature": [ + "Kk5uQqZA3pyOV1cfJpRDNEghsG4vE0gBM4XIncJGgsuyG6SNNMbmXUy9TaBRoqylE5EoIYWXaXWOXn7gHW8rOK6bDkiJW2zoZUI6lRrPZSN8Mm1qaIiB42NsfExA+J6H3VqlsUtfyDiXJdsMRe2Y6pwGhj/GDJpb74PlsyaGOuZUfp/Z1Fy4gFZ2hhBx9MVIdIy0aGpU7D096HaDf30YJmdqHX0Cy59bPI9paaOX39kHt0H0WYpOeQEAsCHtVJTXF7betIBAPT1HO0PVZDS9ShO19AhMbislvPRxMEsZ6QYA2TVdyU6qegzkc7vaRiRjDla2V7YSzrmT745OTvPu1g==" + ], + "Wechatpay-Signature-Type": [ + "WECHATPAY2-SHA256-RSA2048" + ], + "Wechatpay-Timestamp": [ + "1774490961" + ] +}` body := `{ - "id": "fd06376a-3e1b-5516-81f8-9b69cf1ba416", - "create_time": "2025-07-28T16:10:15+08:00", + "id": "56b4937c-8f32-52e0-880a-828c869fa2c0", + "create_time": "2026-03-26T10:09:21+08:00", "resource_type": "encrypt-resource", - "event_type": "MCHTRANSFER.BILL.FINISHED", - "summary": "商家转账单据终态通知", + "event_type": "COUPON.USE", + "summary": "代金券核销通知", "resource": { - "original_type": "mch_payment", - "algorithm": "AEAD_AES_256_GCM", - "ciphertext": "XJBIhrHgbe9NR5q/jLYmZKdT/3xuKm2x7EFu3T52Hj2hjPzarRSA2HCsGTxGojfD+CFyJHIULlL2adqLijAjpi3B6TaYKY4LqhtJ/RYSQtYNxYvBpWX1yLOWe8luJbWxmQvKZxIekFs8lGVgkPBUw0IfEAvJ6jHAGCcgxLIqxgOf6UtGUqxCCNp/V3xy8zCiHB0Mvlw8eXCTuG+ZESJIXvloVGNS79R6iNeqk4kNKRSaV86MNh1KQlmoBxZ4yEshD/vIlMulU3xEc+mM25y8vUS4Ot6pxEpUdUyjwcb9QTwTTnZzm6i+VWYymcItAVBQrvsKBMmqWnPtNXG8++13k3DeO1LyVKURmnWXXT1mImmGx/teN/1xPV5y6nChu/HTbcJGDQy2twuq6TPFbbYlTjZH047z/ZtozJNvGNeh", - "associated_data": "mch_payment", - "nonce": "YN3eW5H8mxLs" - } - }` - - fmt.Print(header) - fmt.Print(body) + "original_type": "coupon", + "algorithm": "AEAD_AES_256_GCM", + "ciphertext": "IfJmKJnBYezruwmk+IA+ENQBn13oWiLR1YnYi3bhYaqYreaXwFcFWbnnQjWfh8RDF36S1v+H1eLj0OtiQVDveLJ9vOcCrtr0M8bkV6NpRN7amqcf02vg+xLcs6UiZDedkN353nMnVVY1SAyuJj0AjKTUJrMhPNtxzdPpL/A3bMVCHrw4sqUItAOsDWVyJ5Kb09BEbdNOesmFkCc+Pcqmr554UHXzjQxzYimMpdbZ8OCovEy6JY1jnbxyiduDI7XBiTvxLr5CzLGlxNk1tIHUSrrDnRzUCpFJzNETgcrF3JWvQVAPThelGnTLN/TT3/pyM7/Kz60YNGaSbJTNKOtxbLXopk72x5hlt45fgrueP+RsnVQWmjULJ0AVEVmPNtlHTYRw7WV0cGwwrF7CCSDifYPed9QZPU1+awVtnGb3If53l109HojJiJB7Y0C/ZPX/vH/KLmtUx+10YKIFOo3vYnxEBV5lv4D5ZqXn4gOOtufVH/URYqgRqnzNFqJV6sW2qP6K9AgBhe/BO4AoeDDHG00XP89XFbeHmR2zZ019jBapq92YkvDVtQc2oLr9MWPoHJUhkmApwB6AmOK7ldWJ14P/Fws3/zBzCxVSlQsgLoDyBKm461ZnA6k09c+hffhTutLt89HYv+pqA1nukdTGuCWqPqUgx0AsK5i1CngrBQZJwc7+ylpakpOrX8oulyuA3So65hgMuqgl5dgkR0e8nvjjv7dVl0aRkqTZt3VL7BB1xwGqbvO3aTEuqqPrFQKLpnaY5Gz64dgg6huQFOBnZww=", + "associated_data": "coupon", + "nonce": "pV96KsdLrSAd" + }` httpHeaders := make(http.Header) if err := json.Unmarshal([]byte(header), &httpHeaders); err != nil { @@ -151,7 +138,7 @@ func Test_MarketingNotify(t *testing.T) { _, bizContent, err := marketingFJLF().Notify(context.Background(), &httpHeaders, []byte(body)) if err != nil { - fmt.Printf("notify err: %+v\n", err) + t.Errorf("notify err: %+v\n", err) return } From 7565c48cd9e494b031be0e35fd130babf1484479 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 11:17:56 +0800 Subject: [PATCH 101/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/notify.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/notify.go b/internal/service/notify.go index 2222ece..8ce8f18 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -71,7 +71,7 @@ func (srv *NotifyService) Notify(ctx http.Context) error { if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, response); err != nil { headerJson, _ := json.Marshal(headers) - log.Errorf("微信回调通知[%s],Consumer处理失败,headers:%s,body:%s\n解析数据:%+v,err:%s", mchId, headerJson, string(bodyBytes), response, err.Error()) + log.Errorf("微信回调通知[%s],Consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), response) return ctx.JSON(http2.StatusBadRequest, map[string]string{ "code": "FAIL", From 5db078e26a69810bc5d74c488749ddacfdf75a20 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 11:27:08 +0800 Subject: [PATCH 102/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../wechatrepoimpl/bank_multi_activity.go | 8 +-- test/bank_multi_activity_test.go | 57 +++++++------------ 2 files changed, 26 insertions(+), 39 deletions(-) diff --git a/internal/data/wechatrepoimpl/bank_multi_activity.go b/internal/data/wechatrepoimpl/bank_multi_activity.go index a18a6bc..b651f8f 100644 --- a/internal/data/wechatrepoimpl/bank_multi_activity.go +++ b/internal/data/wechatrepoimpl/bank_multi_activity.go @@ -59,20 +59,20 @@ func (w *BankMultiActivityImpl) Order(order *bo.OrderBo) (couponId string, err e return *resp.CouponId, nil } -func (w *BankMultiActivityImpl) Notify(ctx context.Context, mchId string, headers *http.Header, respBody []byte) (response *bo.WechatVoucherNotifyBo, err error) { +func (w *BankMultiActivityImpl) Notify(ctx context.Context, mchId string, headers *http.Header, respBody []byte) (*bo.WechatVoucherNotifyBo, error) { t, err := w.wx.Get(mchId) if err != nil { - return + return nil, err } body, decodeBodyStr, err := t.Notify(ctx, headers, respBody) if err != nil { - return + return nil, err } var plainText bo.PlainText - if err = json.Unmarshal([]byte(decodeBodyStr), &response); err != nil { + if err = json.Unmarshal([]byte(decodeBodyStr), &plainText); err != nil { return nil, err } diff --git a/test/bank_multi_activity_test.go b/test/bank_multi_activity_test.go index 4537df9..9ce5368 100644 --- a/test/bank_multi_activity_test.go +++ b/test/bank_multi_activity_test.go @@ -96,39 +96,8 @@ func Test_QixingNotifyData(t *testing.T) { func Test_MarketingNotify(t *testing.T) { - header := `{ - "Content-Type": [ - "application/json" - ], - "Wechatpay-Nonce": [ - "H3Bs5sCbMJZOBLA4w3S6YbN4cCcO9i00" - ], - "Wechatpay-Serial": [ - "PUB_KEY_ID_0111000406952026032500382251001000" - ], - "Wechatpay-Signature": [ - "Kk5uQqZA3pyOV1cfJpRDNEghsG4vE0gBM4XIncJGgsuyG6SNNMbmXUy9TaBRoqylE5EoIYWXaXWOXn7gHW8rOK6bDkiJW2zoZUI6lRrPZSN8Mm1qaIiB42NsfExA+J6H3VqlsUtfyDiXJdsMRe2Y6pwGhj/GDJpb74PlsyaGOuZUfp/Z1Fy4gFZ2hhBx9MVIdIy0aGpU7D096HaDf30YJmdqHX0Cy59bPI9paaOX39kHt0H0WYpOeQEAsCHtVJTXF7betIBAPT1HO0PVZDS9ShO19AhMbislvPRxMEsZ6QYA2TVdyU6qegzkc7vaRiRjDla2V7YSzrmT745OTvPu1g==" - ], - "Wechatpay-Signature-Type": [ - "WECHATPAY2-SHA256-RSA2048" - ], - "Wechatpay-Timestamp": [ - "1774490961" - ] -}` - body := `{ - "id": "56b4937c-8f32-52e0-880a-828c869fa2c0", - "create_time": "2026-03-26T10:09:21+08:00", - "resource_type": "encrypt-resource", - "event_type": "COUPON.USE", - "summary": "代金券核销通知", - "resource": { - "original_type": "coupon", - "algorithm": "AEAD_AES_256_GCM", - "ciphertext": "IfJmKJnBYezruwmk+IA+ENQBn13oWiLR1YnYi3bhYaqYreaXwFcFWbnnQjWfh8RDF36S1v+H1eLj0OtiQVDveLJ9vOcCrtr0M8bkV6NpRN7amqcf02vg+xLcs6UiZDedkN353nMnVVY1SAyuJj0AjKTUJrMhPNtxzdPpL/A3bMVCHrw4sqUItAOsDWVyJ5Kb09BEbdNOesmFkCc+Pcqmr554UHXzjQxzYimMpdbZ8OCovEy6JY1jnbxyiduDI7XBiTvxLr5CzLGlxNk1tIHUSrrDnRzUCpFJzNETgcrF3JWvQVAPThelGnTLN/TT3/pyM7/Kz60YNGaSbJTNKOtxbLXopk72x5hlt45fgrueP+RsnVQWmjULJ0AVEVmPNtlHTYRw7WV0cGwwrF7CCSDifYPed9QZPU1+awVtnGb3If53l109HojJiJB7Y0C/ZPX/vH/KLmtUx+10YKIFOo3vYnxEBV5lv4D5ZqXn4gOOtufVH/URYqgRqnzNFqJV6sW2qP6K9AgBhe/BO4AoeDDHG00XP89XFbeHmR2zZ019jBapq92YkvDVtQc2oLr9MWPoHJUhkmApwB6AmOK7ldWJ14P/Fws3/zBzCxVSlQsgLoDyBKm461ZnA6k09c+hffhTutLt89HYv+pqA1nukdTGuCWqPqUgx0AsK5i1CngrBQZJwc7+ylpakpOrX8oulyuA3So65hgMuqgl5dgkR0e8nvjjv7dVl0aRkqTZt3VL7BB1xwGqbvO3aTEuqqPrFQKLpnaY5Gz64dgg6huQFOBnZww=", - "associated_data": "coupon", - "nonce": "pV96KsdLrSAd" - }` + header := `{"Accept":["*/*"], "Cache-Control":["no-cache"], "Connection":["close"], "Content-Length":["1109"], "Content-Type":["application/json"], "Pragma":["no-cache"], "User-Agent":["Mozilla/4.0"], "Wechatpay-Nonce":["N61AnMoOlDsi6WnCV1xgasslXt6Hqndb"], "Wechatpay-Serial":["PUB_KEY_ID_0111000406952026032500382251001000"], "Wechatpay-Signature":["skDPjPLpIway2ll3J+o3FYnF+3Z75PuNge92LaxDDWHu8V6OXWXHUPLaJilxg/UidBcaP5JKza9t2JIgNtdRXfjPEehi7Ufn5yhPR9EUg4T1PFhkri/N8x1bvZ2oDemsTDHNNxr2PrPla2K5EcNRe6MfVpLKLp/xxX5UXfII8UHjI50jDSSoJQpSZZS0jbm5yfAeokbus0aD7835JKWksLGIuM94s5TYV8fCNL1RVab/ArLJ6UsTf0mjPEVWMdT3TePwdtVFZhFqGxlTFnR2LYSuLNflxQiE7cgtBK76L1+lMit7KlARy7aPSsOpn3DAl1sivnSvP6hX5ljOXnjv6g=="], "Wechatpay-Signature-Type":["WECHATPAY2-SHA256-RSA2048"], "Wechatpay-Timestamp":["1774495201"], "X-Forwarded-For":["121.51.58.172"], "X-Real-Ip":["121.51.58.172"]}` + bodyStr := `{"id":"2f6dba61-17d2-554c-99f1-c2527bac8013","create_time":"2026-03-26T11:14:56+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","resource":{"original_type":"coupon","algorithm":"AEAD_AES_256_GCM","ciphertext":"fr2/gKAby7C+R8Yl/pcWLQEa+RYFDmi2LGSaZtghMtMWs+6z5XolFtNTs65JArRUjptKR4ATn0uAp+Yi2S2ly6+jV07VnUmEBk7ijPXHgkZ53VkNSKgYIC6pOumNcA3MdZxVHPRYawvsluat4m0X7/BqW3qjR9L9DYq4uXtc4pXBC2lcSmtcIENU5lHRmSU83iLp9Gp+LZ3MGaF8ExMc+ZkQcj7RRruiXqWaICeY7c0ebXQJtulytT1FSMksb5nfaf0MmElO+9fOwH1naOrdvSSQ4tglThpfEHuIZ2xaT7DwcBDYvmye+vg+fgnxE66sskz5fwdVtgiGdNSWzA8CZrP/4oXUpbNpjpY2r0GJs5etpCU5hqimz0S1zCLEOtgl/LQ3GCZA1AzZeRcIferbJotC1toJD3tALyO89VSbwcGr8kk6e1HAGyXhoIt5RC3sFnbUdHQLf01MbNhJg13p1YbqInLI3ktjlst4N+gGXmCHHzHlpeOHgdF2uvrWTE/XTDlfumGKBDLFkSOu62pldQfLKVU++BPvvqaRXK9m4g5F87dwOJQJyltm3LYwKfpg1vIMw6ch1Nvrj79+xQs0Xa32Ds8omjI3GmMyly/BMvWJkFpTivrgUnXXMLfzDIFLRB5cR4dGQkJWFBY/qkjHu3yvTqcJuiaUqaHFKjhJJt1WpVMWaj4WlsbOb/oI3eTroxzynNOhgmYWMrM47OSfHJ1MhGU3c18Zk89zyY8miVe/CaPhwwmBnIgeYoBzj5ic36HXJN1Qkup1FYAOadWWXwc=","associated_data":"coupon","nonce":"I4U3QlKjYHlc"}}` httpHeaders := make(http.Header) if err := json.Unmarshal([]byte(header), &httpHeaders); err != nil { @@ -136,11 +105,29 @@ func Test_MarketingNotify(t *testing.T) { return } - _, bizContent, err := marketingFJLF().Notify(context.Background(), &httpHeaders, []byte(body)) + body, bizContent, err := marketingFJLF().Notify(context.Background(), &httpHeaders, []byte(bodyStr)) if err != nil { t.Errorf("notify err: %+v\n", err) return } - fmt.Printf("bizContent: %+v\n", bizContent) + var plainText bo.PlainText + if err = json.Unmarshal([]byte(bizContent), &plainText); err != nil { + t.Errorf("Unmarshal err: %+v\n", err) + return + } + + notifyBo := bo.WechatVoucherNotifyBo{ + ID: body.Id, + CreateTime: body.CreateTime, + ResourceType: body.ResourceType, + EventType: body.EventType, + Summary: body.Summary, + OriginalType: body.Resource.OriginalType, + AssociatedData: body.Resource.AssociatedData, + PlainText: plainText, + } + + fmt.Printf("bizContent: %s\n", bizContent) + fmt.Printf("notifyBo: %+v\n", notifyBo) } From 4ec1809383a003256126e64947613f049c88fd83 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 11:27:34 +0800 Subject: [PATCH 103/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/data/wechatrepoimpl/bank_multi_activity.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/data/wechatrepoimpl/bank_multi_activity.go b/internal/data/wechatrepoimpl/bank_multi_activity.go index b651f8f..660a6bb 100644 --- a/internal/data/wechatrepoimpl/bank_multi_activity.go +++ b/internal/data/wechatrepoimpl/bank_multi_activity.go @@ -73,7 +73,7 @@ func (w *BankMultiActivityImpl) Notify(ctx context.Context, mchId string, header var plainText bo.PlainText if err = json.Unmarshal([]byte(decodeBodyStr), &plainText); err != nil { - return nil, err + return nil, fmt.Errorf("plainText json.Unmarshal error: %v", err) } return &bo.WechatVoucherNotifyBo{ From 64c7fbecd2864b6a8aa0a16dd8996f31bd9e4ac6 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 11:28:33 +0800 Subject: [PATCH 104/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/notify.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/notify.go b/internal/service/notify.go index 8ce8f18..02dd75d 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -71,7 +71,7 @@ func (srv *NotifyService) Notify(ctx http.Context) error { if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, response); err != nil { headerJson, _ := json.Marshal(headers) - log.Errorf("微信回调通知[%s],Consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), response) + log.Errorf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), response) return ctx.JSON(http2.StatusBadRequest, map[string]string{ "code": "FAIL", From 6aaf9f3e127d6a2252c2f478806127eeb4aa6f38 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 11:30:55 +0800 Subject: [PATCH 105/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/wechat_notify.go | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index 18c0f8f..7907bbd 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -17,6 +17,10 @@ func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, ip string, req order, err := this.getOrder(ctx, req) if err != nil { + // 系统订单不存在 + //if errors.Is(err, gorm.ErrRecordNotFound) { + // return nil + //} return err } @@ -53,7 +57,7 @@ func (this *VoucherBiz) getOrder(ctx context.Context, req *bo.WechatVoucherNotif order, err := this.OrderRepo.GetByCouponId(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID, req.PlainText.CouponID) if err != nil { - return nil, fmt.Errorf("订单查询错误 error: %v", err) + return nil, fmt.Errorf("订单查询错误 error: %w", err) } return order, nil From a7f41d1c2798d657f0e2b64c69d65c66a3aaddee Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 11:31:55 +0800 Subject: [PATCH 106/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/wechat_notify.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index 7907bbd..b64d7fe 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -2,7 +2,9 @@ package biz import ( "context" + "errors" "fmt" + "gorm.io/gorm" errPb "voucher/api/err" "voucher/internal/biz/bo" "voucher/internal/biz/vo" @@ -18,9 +20,9 @@ func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, ip string, req order, err := this.getOrder(ctx, req) if err != nil { // 系统订单不存在 - //if errors.Is(err, gorm.ErrRecordNotFound) { - // return nil - //} + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil + } return err } From 9c465d1f525308d3dfc74a69db6f8730d9ef4bd4 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 11:33:43 +0800 Subject: [PATCH 107/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/wechat_notify.go | 7 ------- internal/service/notify.go | 7 +++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index b64d7fe..89d33e8 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -2,9 +2,7 @@ package biz import ( "context" - "errors" "fmt" - "gorm.io/gorm" errPb "voucher/api/err" "voucher/internal/biz/bo" "voucher/internal/biz/vo" @@ -19,10 +17,6 @@ func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, ip string, req order, err := this.getOrder(ctx, req) if err != nil { - // 系统订单不存在 - if errors.Is(err, gorm.ErrRecordNotFound) { - return nil - } return err } @@ -57,7 +51,6 @@ func (this *VoucherBiz) getOrder(ctx context.Context, req *bo.WechatVoucherNotif } order, err := this.OrderRepo.GetByCouponId(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID, req.PlainText.CouponID) - if err != nil { return nil, fmt.Errorf("订单查询错误 error: %w", err) } diff --git a/internal/service/notify.go b/internal/service/notify.go index 02dd75d..b338ded 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -2,9 +2,11 @@ package service import ( "encoding/json" + "errors" "fmt" "github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/transport/http" + "gorm.io/gorm" "io" http2 "net/http" "voucher/internal/biz" @@ -73,6 +75,11 @@ func (srv *NotifyService) Notify(ctx http.Context) error { headerJson, _ := json.Marshal(headers) log.Errorf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), response) + // 系统订单不存在 + if errors.Is(err, gorm.ErrRecordNotFound) { + return ctx.JSON(http2.StatusOK, nil) + } + return ctx.JSON(http2.StatusBadRequest, map[string]string{ "code": "FAIL", "message": err.Error(), From 2ec6b6291269b550fd3223a1d470e201d1d088a8 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 13:44:05 +0800 Subject: [PATCH 108/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/repo/product.go | 1 + internal/biz/wechat_notify.go | 6 ++++++ internal/data/repoimpl/product.go | 24 +++++++++++++++++++----- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/internal/biz/repo/product.go b/internal/biz/repo/product.go index 1194a81..fbf75f2 100644 --- a/internal/biz/repo/product.go +++ b/internal/biz/repo/product.go @@ -10,6 +10,7 @@ type ProductRepo interface { GetById(ctx context.Context, id int32) (*bo.ProductBo, error) FindWarningBudget(ctx context.Context, fun func(ctx context.Context, rows []*bo.ProductBo) error) error GetByBatchNo(ctx context.Context, batchNo string) (*bo.ProductBo, error) + GetByMchStockId(ctx context.Context, mchId, stockId string) (*bo.ProductBo, error) GetByProductNo(ctx context.Context, productNo string) (*bo.ProductBo, error) UpdateByWxResp(ctx context.Context, id int32, req *do.WxResp) error } diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index 89d33e8..874207e 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -11,6 +11,12 @@ import ( func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, ip string, req *bo.WechatVoucherNotifyBo) error { + // 商品数据量较少,先查询商品是否存在,过滤多余的通知信息 + _, err := this.ProductRepo.GetByMchStockId(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID) + if err != nil { + return fmt.Errorf("商品查询错误 error: %w", err) + } + c := vo.WechatNotifyConsumeLockKey.BuildCache([]string{req.PlainText.StockCreatorMchid, req.PlainText.StockID, req.PlainText.CouponID}) return lock.NewMutex(this.rdb.Rdb, c.TTL).Lock(ctx, c.Key, func(ctx context.Context) error { diff --git a/internal/data/repoimpl/product.go b/internal/data/repoimpl/product.go index bad4723..d1510b4 100644 --- a/internal/data/repoimpl/product.go +++ b/internal/data/repoimpl/product.go @@ -104,14 +104,28 @@ func (r *ProductRepoImpl) GetByBatchNo(ctx context.Context, batchNo string) (*bo tx := db.Where(model.Product{BatchNo: batchNo}).First(&item) if tx.Error != nil { - if errors.Is(tx.Error, gorm.ErrRecordNotFound) { - return nil, err2.ErrorDbNotFound("商品数据不存在") - } - return nil, fmt.Errorf("product db fail %w", tx.Error) + return nil, tx.Error } if tx.RowsAffected == 0 { - return nil, err2.ErrorDbNotFound("商品数据不存在") + return nil, gorm.ErrRecordNotFound + } + + return r.ToBo(item), nil +} + +func (r *ProductRepoImpl) GetByMchStockId(ctx context.Context, mchId, stockId string) (*bo.ProductBo, error) { + + var item *model.Product + + tx := r.db.DB(ctx).Where(model.Product{MchId: mchId, BatchNo: stockId}).First(&item) + + if tx.Error != nil { + return nil, tx.Error + } + + if tx.RowsAffected == 0 { + return nil, gorm.ErrRecordNotFound } return r.ToBo(item), nil From 3f3bcbab8e5dc0c3608e6fa2f6e0659ea7c704f8 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 13:44:41 +0800 Subject: [PATCH 109/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/notify.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/service/notify.go b/internal/service/notify.go index b338ded..6a8a412 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -75,7 +75,6 @@ func (srv *NotifyService) Notify(ctx http.Context) error { headerJson, _ := json.Marshal(headers) log.Errorf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), response) - // 系统订单不存在 if errors.Is(err, gorm.ErrRecordNotFound) { return ctx.JSON(http2.StatusOK, nil) } From 886d140cebd6c1f85e0a27136546c968ec86520d Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 13:46:36 +0800 Subject: [PATCH 110/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/notify.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/service/notify.go b/internal/service/notify.go index 6a8a412..c8f7429 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -57,7 +57,7 @@ func (srv *NotifyService) Notify(ctx http.Context) error { }) } - response, err := srv.WechatBiz.CallBack(ctx, mchId, &headers, bodyBytes) + bizData, err := srv.WechatBiz.CallBack(ctx, mchId, &headers, bodyBytes) if err != nil { headerJson, _ := json.Marshal(headers) log.Errorf("微信回调通知[%s],CallBack处理失败,headers:%s,body:%s,err:%s", mchId, headerJson, string(bodyBytes), err.Error()) @@ -70,10 +70,10 @@ func (srv *NotifyService) Notify(ctx http.Context) error { ip := helper.GetClientIP(ctx) - if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, response); err != nil { + if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, bizData); err != nil { headerJson, _ := json.Marshal(headers) - log.Errorf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), response) + log.Errorf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), bizData) if errors.Is(err, gorm.ErrRecordNotFound) { return ctx.JSON(http2.StatusOK, nil) From cfe1973bf9c9dd6f58d1a01e0fbddbea9a317e6f Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 13:57:09 +0800 Subject: [PATCH 111/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/wechat_notify.go | 5 +++++ internal/service/notify.go | 6 ------ 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index 874207e..4641c65 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -2,7 +2,9 @@ package biz import ( "context" + "errors" "fmt" + "gorm.io/gorm" errPb "voucher/api/err" "voucher/internal/biz/bo" "voucher/internal/biz/vo" @@ -14,6 +16,9 @@ func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, ip string, req // 商品数据量较少,先查询商品是否存在,过滤多余的通知信息 _, err := this.ProductRepo.GetByMchStockId(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID) if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil + } return fmt.Errorf("商品查询错误 error: %w", err) } diff --git a/internal/service/notify.go b/internal/service/notify.go index c8f7429..956e0c3 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -2,11 +2,9 @@ package service import ( "encoding/json" - "errors" "fmt" "github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/transport/http" - "gorm.io/gorm" "io" http2 "net/http" "voucher/internal/biz" @@ -75,10 +73,6 @@ func (srv *NotifyService) Notify(ctx http.Context) error { headerJson, _ := json.Marshal(headers) log.Errorf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), bizData) - if errors.Is(err, gorm.ErrRecordNotFound) { - return ctx.JSON(http2.StatusOK, nil) - } - return ctx.JSON(http2.StatusBadRequest, map[string]string{ "code": "FAIL", "message": err.Error(), From 8cf3c66cb354897196954d9a8aecc3b55b8ac333 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 14:00:55 +0800 Subject: [PATCH 112/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/wechat_notify.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index 4641c65..94d576c 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -13,6 +13,10 @@ import ( func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, ip string, req *bo.WechatVoucherNotifyBo) error { + if req.PlainText.StockCreatorMchid == "" || req.PlainText.StockID == "" || req.PlainText.CouponID == "" { + return fmt.Errorf("回调必要信息不能为空") + } + // 商品数据量较少,先查询商品是否存在,过滤多余的通知信息 _, err := this.ProductRepo.GetByMchStockId(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID) if err != nil { @@ -57,10 +61,6 @@ func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, ip string, req func (this *VoucherBiz) getOrder(ctx context.Context, req *bo.WechatVoucherNotifyBo) (*bo.OrderBo, error) { - if req.PlainText.StockCreatorMchid == "" || req.PlainText.StockID == "" || req.PlainText.CouponID == "" { - return nil, fmt.Errorf("订单查询参数错误") - } - order, err := this.OrderRepo.GetByCouponId(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID, req.PlainText.CouponID) if err != nil { return nil, fmt.Errorf("订单查询错误 error: %w", err) From 2e364f03327390db1201dff9fe0f438b2ec77694 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 14:03:03 +0800 Subject: [PATCH 113/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/wechat_notify.go | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index 94d576c..50b650e 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -2,9 +2,7 @@ package biz import ( "context" - "errors" "fmt" - "gorm.io/gorm" errPb "voucher/api/err" "voucher/internal/biz/bo" "voucher/internal/biz/vo" @@ -20,9 +18,9 @@ func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, ip string, req // 商品数据量较少,先查询商品是否存在,过滤多余的通知信息 _, err := this.ProductRepo.GetByMchStockId(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID) if err != nil { - if errors.Is(err, gorm.ErrRecordNotFound) { - return nil - } + //if errors.Is(err, gorm.ErrRecordNotFound) { + // return nil + //} return fmt.Errorf("商品查询错误 error: %w", err) } From c77e23af8a6092e8a8bd1b4f74813df7c4e31acb Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 14:09:15 +0800 Subject: [PATCH 114/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/notify.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/internal/service/notify.go b/internal/service/notify.go index 956e0c3..c8f7429 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -2,9 +2,11 @@ package service import ( "encoding/json" + "errors" "fmt" "github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/transport/http" + "gorm.io/gorm" "io" http2 "net/http" "voucher/internal/biz" @@ -73,6 +75,10 @@ func (srv *NotifyService) Notify(ctx http.Context) error { headerJson, _ := json.Marshal(headers) log.Errorf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), bizData) + if errors.Is(err, gorm.ErrRecordNotFound) { + return ctx.JSON(http2.StatusOK, nil) + } + return ctx.JSON(http2.StatusBadRequest, map[string]string{ "code": "FAIL", "message": err.Error(), From ce8d6ed988b20ec81b47f86fa8c4e89a5c76cc59 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 14:15:12 +0800 Subject: [PATCH 115/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/server/wechat_consumer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/server/wechat_consumer.go b/internal/server/wechat_consumer.go index 5428942..c1241ef 100644 --- a/internal/server/wechat_consumer.go +++ b/internal/server/wechat_consumer.go @@ -150,7 +150,7 @@ func (w *WechatNotifyConsumer) processMessage(msg mq_http_sdk.ConsumeMessageEntr } }() - log.Warnf("微信回调消费接收消息成功 messageId:%s, messageTag:%s, message:%s", msg.MessageId, msg.MessageTag, msg.MessageBody) + //log.Warnf("微信回调消费接收消息成功 messageId:%s, messageTag:%s, message:%s", msg.MessageId, msg.MessageTag, msg.MessageBody) ctx := context.Background() if err := w.voucherService.WechatUseNotifyConsumer(ctx, msg.MessageTag, msg.MessageBody); err != nil { From c89a6406b6f5503669a2cfe0e81a24b82ae3112a Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 14:54:45 +0800 Subject: [PATCH 116/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/notify.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/service/notify.go b/internal/service/notify.go index c8f7429..18347db 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -68,11 +68,14 @@ func (srv *NotifyService) Notify(ctx http.Context) error { }) } + headerJson, _ := json.Marshal(headers) + + log.Warnf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), bizData) + ip := helper.GetClientIP(ctx) if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, bizData); err != nil { - headerJson, _ := json.Marshal(headers) log.Errorf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), bizData) if errors.Is(err, gorm.ErrRecordNotFound) { From 0d5259c9b0ee2ff5e81479ed87420358d2be4399 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 14:54:54 +0800 Subject: [PATCH 117/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/notify.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/notify.go b/internal/service/notify.go index 18347db..c227934 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -76,7 +76,7 @@ func (srv *NotifyService) Notify(ctx http.Context) error { if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, bizData); err != nil { - log.Errorf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), bizData) + //log.Errorf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), bizData) if errors.Is(err, gorm.ErrRecordNotFound) { return ctx.JSON(http2.StatusOK, nil) From 417e813b84d52a8fe0b4083d2dcc5b0bec383463 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 15:05:18 +0800 Subject: [PATCH 118/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/notify.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/internal/service/notify.go b/internal/service/notify.go index c227934..999e14d 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -68,15 +68,11 @@ func (srv *NotifyService) Notify(ctx http.Context) error { }) } - headerJson, _ := json.Marshal(headers) - - log.Warnf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), bizData) - ip := helper.GetClientIP(ctx) if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, bizData); err != nil { - - //log.Errorf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), bizData) + headerJson, _ := json.Marshal(headers) + log.Errorf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), bizData) if errors.Is(err, gorm.ErrRecordNotFound) { return ctx.JSON(http2.StatusOK, nil) From f34a27fbf255a7bfb0d96b105aaa52793433abca Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 15:13:02 +0800 Subject: [PATCH 119/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pkg/wechat/srv/marketing/marketing.go | 8 ++++-- internal/pkg/wechat/utils/wxpay_utility.go | 25 +++++++++++++------ 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/internal/pkg/wechat/srv/marketing/marketing.go b/internal/pkg/wechat/srv/marketing/marketing.go index 362776a..ea67fd5 100644 --- a/internal/pkg/wechat/srv/marketing/marketing.go +++ b/internal/pkg/wechat/srv/marketing/marketing.go @@ -62,10 +62,14 @@ func (srv *Marketing) Query(appId, openId, couponId string) (response *SendResp, return response, nil } -// Notify . func (srv *Marketing) Notify(_ context.Context, headers *http.Header, respBody []byte) (body *utils.WxNotifyBody, response string, err error) { - wxNotifyBody, bizStr, err := srv.GetDecodeBody(headers, respBody) + wxNotifyBody, err := srv.GetNotifyBody(headers, respBody) + if err != nil { + return nil, "", err + } + + bizStr, err := srv.DecodeBody(wxNotifyBody) if err != nil { return nil, "", err } diff --git a/internal/pkg/wechat/utils/wxpay_utility.go b/internal/pkg/wechat/utils/wxpay_utility.go index 1089bbe..d8718af 100644 --- a/internal/pkg/wechat/utils/wxpay_utility.go +++ b/internal/pkg/wechat/utils/wxpay_utility.go @@ -575,10 +575,10 @@ func (srv *MchConfig) Verify(request *http.Request) (string, error) { return EncryptOAEPWithPublicKey(string(respBody), srv.wechatPayPublicKey) } -func (srv *MchConfig) GetDecodeBody(headers *http.Header, respBody []byte) (*WxNotifyBody, string, error) { +func (srv *MchConfig) GetNotifyBody(headers *http.Header, respBody []byte) (*WxNotifyBody, error) { if respBody == nil { - return nil, "", fmt.Errorf("request HttpBody is nil") + return nil, fmt.Errorf("request HttpBody is nil") } err := ValidateResponse( @@ -588,25 +588,34 @@ func (srv *MchConfig) GetDecodeBody(headers *http.Header, respBody []byte) (*WxN respBody, ) if err != nil { - return nil, "", err + return nil, err } var wxNotifyBody WxNotifyBody if err = json.Unmarshal(respBody, &wxNotifyBody); err != nil { - return nil, "", err + return nil, err } + return &wxNotifyBody, nil +} + +func (srv *MchConfig) DecodeBody(wxNotifyBody *WxNotifyBody) (string, error) { + aesUtil, err := NewAesUtil(srv.aesKey) if err != nil { - return nil, "", err + return "", err } - decryptedText, err := aesUtil.DecryptToString(wxNotifyBody.Resource.AssociatedData, wxNotifyBody.Resource.Nonce, wxNotifyBody.Resource.Ciphertext) + decryptedText, err := aesUtil.DecryptToString( + wxNotifyBody.Resource.AssociatedData, + wxNotifyBody.Resource.Nonce, + wxNotifyBody.Resource.Ciphertext, + ) if err != nil { - return nil, "", err + return "", err } - return &wxNotifyBody, decryptedText, nil + return decryptedText, nil } // BuildSortedQueryString 函数接受一个 map,返回按照字段名排序后的 URL 键值对格式字符串 From 3c4b932f03abd0c587c9c783dfcc04b11726c356 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 15:14:36 +0800 Subject: [PATCH 120/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/notify.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/service/notify.go b/internal/service/notify.go index 999e14d..360bd66 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -57,10 +57,14 @@ func (srv *NotifyService) Notify(ctx http.Context) error { }) } + headerJson, _ := json.Marshal(headers) + log.Warn("微信回调通知[%s],CallBack处理失败,headers:%s,body:%s", mchId, headerJson, string(bodyBytes)) + bizData, err := srv.WechatBiz.CallBack(ctx, mchId, &headers, bodyBytes) if err != nil { - headerJson, _ := json.Marshal(headers) - log.Errorf("微信回调通知[%s],CallBack处理失败,headers:%s,body:%s,err:%s", mchId, headerJson, string(bodyBytes), err.Error()) + + //log.Errorf("微信回调通知[%s],CallBack处理失败,headers:%s,body:%s,err:%s", mchId, headerJson, string(bodyBytes), err.Error()) + log.Errorf("微信回调通知[%s],CallBack处理失败,:%s", mchId, err.Error()) return ctx.JSON(http2.StatusNetworkAuthenticationRequired, map[string]string{ "code": "FAIL", From b4d0688bfc4d7c346b192aa3cf1c9208f94892f2 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 15:17:56 +0800 Subject: [PATCH 121/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/notify.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/notify.go b/internal/service/notify.go index 360bd66..f46a0f3 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -58,7 +58,7 @@ func (srv *NotifyService) Notify(ctx http.Context) error { } headerJson, _ := json.Marshal(headers) - log.Warn("微信回调通知[%s],CallBack处理失败,headers:%s,body:%s", mchId, headerJson, string(bodyBytes)) + log.Warnf("微信回调通知[%s],CallBack处理失败,headers:%s,body:%s", mchId, headerJson, string(bodyBytes)) bizData, err := srv.WechatBiz.CallBack(ctx, mchId, &headers, bodyBytes) if err != nil { From 8f290eb0c2715b2e2f16b1b47ef23060a466fcd3 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 15:20:25 +0800 Subject: [PATCH 122/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/notify.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/internal/service/notify.go b/internal/service/notify.go index f46a0f3..3ad1ea4 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -62,10 +62,7 @@ func (srv *NotifyService) Notify(ctx http.Context) error { bizData, err := srv.WechatBiz.CallBack(ctx, mchId, &headers, bodyBytes) if err != nil { - - //log.Errorf("微信回调通知[%s],CallBack处理失败,headers:%s,body:%s,err:%s", mchId, headerJson, string(bodyBytes), err.Error()) - log.Errorf("微信回调通知[%s],CallBack处理失败,:%s", mchId, err.Error()) - + log.Errorf("微信回调通知[%s],CallBack处理失败,headers:%s,body:%s,err:%s", mchId, headerJson, string(bodyBytes), err.Error()) return ctx.JSON(http2.StatusNetworkAuthenticationRequired, map[string]string{ "code": "FAIL", "message": err.Error(), @@ -75,8 +72,8 @@ func (srv *NotifyService) Notify(ctx http.Context) error { ip := helper.GetClientIP(ctx) if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, bizData); err != nil { - headerJson, _ := json.Marshal(headers) - log.Errorf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), bizData) + //log.Errorf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), bizData) + log.Errorf("微信回调通知[%s],consumer处理失败[%s-%s]", mchId, bizData.PlainText.CouponID, err.Error()) if errors.Is(err, gorm.ErrRecordNotFound) { return ctx.JSON(http2.StatusOK, nil) From 4be755c113bdd24151cdbd9841c7716798c8f3cb Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 26 Mar 2026 16:07:47 +0800 Subject: [PATCH 123/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/notify.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/service/notify.go b/internal/service/notify.go index 3ad1ea4..ab00723 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -73,7 +73,7 @@ func (srv *NotifyService) Notify(ctx http.Context) error { if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, bizData); err != nil { //log.Errorf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), bizData) - log.Errorf("微信回调通知[%s],consumer处理失败[%s-%s]", mchId, bizData.PlainText.CouponID, err.Error()) + log.Errorf("微信回调通知[%s],consumer处理失败[%s-%s-%s]", mchId, bizData.PlainText.StockID, bizData.PlainText.CouponID, err.Error()) if errors.Is(err, gorm.ErrRecordNotFound) { return ctx.JSON(http2.StatusOK, nil) From f1e813584f062ddefcf60b8eb059699d6a62f686 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 27 Mar 2026 10:38:07 +0800 Subject: [PATCH 124/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/service/notify.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/internal/service/notify.go b/internal/service/notify.go index ab00723..4d903e3 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -57,12 +57,10 @@ func (srv *NotifyService) Notify(ctx http.Context) error { }) } - headerJson, _ := json.Marshal(headers) - log.Warnf("微信回调通知[%s],CallBack处理失败,headers:%s,body:%s", mchId, headerJson, string(bodyBytes)) - bizData, err := srv.WechatBiz.CallBack(ctx, mchId, &headers, bodyBytes) if err != nil { - log.Errorf("微信回调通知[%s],CallBack处理失败,headers:%s,body:%s,err:%s", mchId, headerJson, string(bodyBytes), err.Error()) + headerJson, _ := json.Marshal(headers) + log.Errorf("微信回调通知[%s],callBack处理失败:%s\nheaders:%s\nbody:%s", mchId, err.Error(), headerJson, string(bodyBytes)) return ctx.JSON(http2.StatusNetworkAuthenticationRequired, map[string]string{ "code": "FAIL", "message": err.Error(), @@ -72,8 +70,8 @@ func (srv *NotifyService) Notify(ctx http.Context) error { ip := helper.GetClientIP(ctx) if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, bizData); err != nil { - //log.Errorf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), bizData) - log.Errorf("微信回调通知[%s],consumer处理失败[%s-%s-%s]", mchId, bizData.PlainText.StockID, bizData.PlainText.CouponID, err.Error()) + headerJson, _ := json.Marshal(headers) + log.Errorf("微信回调通知[%s],consumer处理失败:%s\nheaders:%s\nbody:%s\n解析数据:%+v", mchId, err.Error(), headerJson, string(bodyBytes), bizData) if errors.Is(err, gorm.ErrRecordNotFound) { return ctx.JSON(http2.StatusOK, nil) From 7e8ef4cd2b01b337bcc1ecffb2108f4dd5a10119 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 27 Mar 2026 11:35:59 +0800 Subject: [PATCH 125/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/wechat.go | 10 ++++ .../biz/wechatrepo/bank_multi_activity.go | 1 + .../wechatrepoimpl/bank_multi_activity.go | 34 ++++++++++++++ internal/server/http.go | 1 + internal/service/notify.go | 46 +++++++++++++++++++ 5 files changed, 92 insertions(+) diff --git a/internal/biz/wechat.go b/internal/biz/wechat.go index e9761df..f3db1ad 100644 --- a/internal/biz/wechat.go +++ b/internal/biz/wechat.go @@ -24,3 +24,13 @@ func (biz *WechatBiz) CallBack(ctx context.Context, mchId string, headers *http. return response, nil } + +func (biz *WechatBiz) DecodeBody(ctx context.Context, mchId string, respBody []byte) (*bo.WechatVoucherNotifyBo, error) { + + response, err := biz.BankMultiActivityRepo.DecodeBody(ctx, mchId, respBody) + if err != nil { + return nil, err + } + + return response, nil +} diff --git a/internal/biz/wechatrepo/bank_multi_activity.go b/internal/biz/wechatrepo/bank_multi_activity.go index 626102e..7dcf05d 100644 --- a/internal/biz/wechatrepo/bank_multi_activity.go +++ b/internal/biz/wechatrepo/bank_multi_activity.go @@ -9,4 +9,5 @@ import ( type BankMultiActivityRepo interface { Order(order *bo.OrderBo) (couponId string, err error) Notify(ctx context.Context, mchId string, headers *http.Header, respBody []byte) (response *bo.WechatVoucherNotifyBo, err error) + DecodeBody(ctx context.Context, mchId string, respBody []byte) (*bo.WechatVoucherNotifyBo, error) } diff --git a/internal/data/wechatrepoimpl/bank_multi_activity.go b/internal/data/wechatrepoimpl/bank_multi_activity.go index 660a6bb..3767aef 100644 --- a/internal/data/wechatrepoimpl/bank_multi_activity.go +++ b/internal/data/wechatrepoimpl/bank_multi_activity.go @@ -87,3 +87,37 @@ func (w *BankMultiActivityImpl) Notify(ctx context.Context, mchId string, header PlainText: plainText, }, nil } + +func (w *BankMultiActivityImpl) DecodeBody(ctx context.Context, mchId string, respBody []byte) (*bo.WechatVoucherNotifyBo, error) { + + t, err := w.wx.Get(mchId) + if err != nil { + return nil, err + } + + var body utils.WxNotifyBody + if err = json.Unmarshal(respBody, &body); err != nil { + return nil, err + } + + decryptedText, err := t.DecodeBody(&body) + if err != nil { + return nil, err + } + + var plainText bo.PlainText + if err = json.Unmarshal([]byte(decryptedText), &plainText); err != nil { + return nil, fmt.Errorf("plainText json.Unmarshal error: %v", err) + } + + return &bo.WechatVoucherNotifyBo{ + ID: body.Id, + CreateTime: body.CreateTime, + ResourceType: body.ResourceType, + EventType: body.EventType, + Summary: body.Summary, + OriginalType: body.Resource.OriginalType, + AssociatedData: body.Resource.AssociatedData, + PlainText: plainText, + }, nil +} diff --git a/internal/server/http.go b/internal/server/http.go index cf919ee..41abba8 100644 --- a/internal/server/http.go +++ b/internal/server/http.go @@ -45,6 +45,7 @@ func NewHTTPServer( // https://gateway.dev.cdlsxd.cn/voucher/v1/notify/123456 123456为微信主体商户号 测试环境 // https://voucher.86698.cn/voucher/v1/notify/123456 123456为微信主体商户号 正式环境 srv.Route("/voucher/").POST("/v1/notify/{mch_id}", notifyService.Notify) + srv.Route("/voucher/").POST("/v1/notifyMock", notifyService.NotifyMock) // ---脚本-- // 订单通知重试 -- 不健全 diff --git a/internal/service/notify.go b/internal/service/notify.go index 4d903e3..be8eb91 100644 --- a/internal/service/notify.go +++ b/internal/service/notify.go @@ -10,6 +10,7 @@ import ( "io" http2 "net/http" "voucher/internal/biz" + "voucher/internal/biz/bo" "voucher/internal/pkg/helper" ) @@ -85,3 +86,48 @@ func (srv *NotifyService) Notify(ctx http.Context) error { return ctx.JSON(http2.StatusOK, nil) } + +// NotifyMock 模拟微信回调通知,用于测试 +func (srv *NotifyService) NotifyMock(ctx http.Context) error { + + bodyBytes, err := io.ReadAll(ctx.Request().Body) + if err != nil { + log.Errorf("微信回调通知,读取响应体失败: %v", err) + return fmt.Errorf("读取响应体失败: %w", err) + } + + if len(bodyBytes) == 0 { + log.Errorf("微信回调通知,响应体不能为空") + return ctx.JSON(http2.StatusOK, map[string]string{ + "code": "FAIL", + "message": "微信回调通知,响应体不能为空", + }) + } + + var bizData *bo.WechatVoucherNotifyBo + err = json.Unmarshal(bodyBytes, &bizData) + if err != nil { + log.Errorf("微信回调通知,解析响应体失败: %v", err) + return ctx.JSON(http2.StatusOK, map[string]string{ + "code": "FAIL", + "message": "微信回调通知,解析响应体失败", + }) + } + + ip := helper.GetClientIP(ctx) + + if err = srv.VoucherBiz.WechatNotifyConsumer(ctx, ip, bizData); err != nil { + log.Errorf("微信回调通知,consumer处理失败:%s\nbody:%s\n解析数据:%+v", err.Error(), string(bodyBytes), bizData) + + if errors.Is(err, gorm.ErrRecordNotFound) { + return ctx.JSON(http2.StatusOK, nil) + } + + return ctx.JSON(http2.StatusOK, map[string]string{ + "code": "FAIL", + "message": err.Error(), + }) + } + + return ctx.JSON(http2.StatusOK, nil) +} From d308d56e791f541217bee5cc49305277d8a516f8 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 27 Mar 2026 11:55:37 +0800 Subject: [PATCH 126/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 9 ++++++++- internal/pkg/helper/utils_test.go | 16 +++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 9869e38..23d764d 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -228,7 +228,14 @@ func (biz *MultiBiz) nlCreate(ctx context.Context, req *bo.WechatVoucherNotifyBo b, _ := json.Marshal(request) nl.Request = string(b) - return biz.MultiNotifyLogRepo.Create(ctx, nl) + res, err := biz.MultiNotifyLogRepo.Create(ctx, nl) + if err != nil { + return nil, fmt.Errorf("创建通知日志错误 error: %v", err) + } + // 创建出来的核销时间精度有差距,重新赋值返回出去 + res.ConsumeTime = mnd.ConsumeTime + + return res, nil } func (biz *MultiBiz) bizContent(nl *bo.MultiNotifyLogBo, order *bo.OrderBo) (string, error) { diff --git a/internal/pkg/helper/utils_test.go b/internal/pkg/helper/utils_test.go index 2e963ab..42278ee 100644 --- a/internal/pkg/helper/utils_test.go +++ b/internal/pkg/helper/utils_test.go @@ -88,5 +88,19 @@ func TestLength(t *testing.T) { }` s := len(jsonStr) t.Log(s) - +} + +func Test_Time(t *testing.T) { + + strTime := "2026-03-27 10:33:20" + tt, _ := time.Parse("2006-01-02 15:04:05", strTime) + t.Log(tt.Format("2006-01-02 15:04:05.000")) + + //for i := 0; i < 20; i++ { + // formatTime := time.Now().Format("2006-01-02 15:04:05.999") + // t.Log(formatTime) + // formatTime2 := time.Now().Format("2006-01-02 15:04:05.000") + // t.Log(formatTime2) + // time.Sleep(1 * time.Second) + //} } From 965474a340b4ac0e501f2a8736b9895e5cffec76 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 27 Mar 2026 14:47:37 +0800 Subject: [PATCH 127/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/coupon.go | 59 ++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/test/coupon.go b/test/coupon.go index 8cccda8..b930d62 100644 --- a/test/coupon.go +++ b/test/coupon.go @@ -10,7 +10,8 @@ import ( "os" "path/filepath" "time" - "voucher/internal/biz/do" + v1 "voucher/api/v1" + "voucher/internal/biz/vo" "voucher/internal/conf" "voucher/internal/data" ) @@ -169,7 +170,7 @@ func QueryProduct() { } req := cashcoupons.QueryStockRequest{ - StockId: core.String("21923564"), + StockId: core.String("21928191"), StockCreatorMchid: core.String("1100040695"), } @@ -180,8 +181,8 @@ func QueryProduct() { return } - j, _ := json.Marshal(resp) - fmt.Printf("\nresp:%s\n", string(j)) + originData, _ := json.Marshal(resp) + fmt.Printf("\nOriginData:%s\n", string(originData)) availableStock := *resp.StockUseRule.MaxCoupons - *resp.DistributedCoupons couponAmount := *resp.StockUseRule.FixedNormalCoupon.CouponAmount / 100 @@ -195,45 +196,51 @@ func QueryProduct() { fmt.Printf("\n剩余库存:%d", availableStock) fmt.Printf("\n剩余预算:%d", availableStock*couponAmount) - str, _ := json.Marshal(WxResp(resp)) - fmt.Printf("\nWxResp:%+v", string(str)) + WxResp(resp) return } -func WxResp(wxResp *cashcoupons.Stock) (reps *do.WxResp) { +func WxResp(wxResp *cashcoupons.Stock) { - availableStock := *wxResp.StockUseRule.MaxCoupons - *wxResp.DistributedCoupons - couponAmount := *wxResp.StockUseRule.FixedNormalCoupon.CouponAmount / 100 - - remainingBudget := availableStock * couponAmount - - stockUsageRate := float64(*wxResp.DistributedCoupons) / float64(*wxResp.StockUseRule.MaxCoupons) * 100 - - req := &do.WxResp{ - Amount: couponAmount, - AllBudget: *wxResp.StockUseRule.MaxAmount / 100, - AllStock: *wxResp.StockUseRule.MaxCoupons, - UsedStock: *wxResp.DistributedCoupons, - UsedBudget: *wxResp.DistributedCoupons * couponAmount, - AvailableStock: availableStock, - AvailableBudget: remainingBudget, - StockUsageRate: stockUsageRate, + xx := &v1.CmbQueryProductReply{ + RespCode: vo.CmbResponseStatusSuccess.GetValue(), + RespMsg: "成功", + ActivityName: *wxResp.StockName, + ActivityId: "CMB" + *wxResp.StockId, + Amount: "", + MinAmount: "", + AvailableType: "", + AvailableDays: "", // 动态有效期天数 + StartTime: "", + EndTime: "", + AvailableStock: "", + Detail: *wxResp.Description, } inputFormat := time.RFC3339 if wxResp.AvailableBeginTime != nil { + availableBeginTime, _ := time.Parse(inputFormat, *wxResp.AvailableBeginTime) - req.StartTime = &availableBeginTime + xx.StartTime = availableBeginTime.Format("2006-01-02 15:04:05.000") + xx.SaleStartTime = xx.StartTime } if wxResp.AvailableEndTime != nil { availableEndTime, _ := time.Parse(inputFormat, *wxResp.AvailableEndTime) - req.EndTime = &availableEndTime + xx.EndTime = availableEndTime.Format("2006-01-02 15:04:05.000") + xx.SaleEndTime = xx.EndTime } - return req + xx.Amount = fmt.Sprintf("%d", *wxResp.StockUseRule.FixedNormalCoupon.CouponAmount) + xx.MinAmount = fmt.Sprintf("%d", *wxResp.StockUseRule.FixedNormalCoupon.TransactionMinimum) + + availableStock := *wxResp.StockUseRule.MaxCoupons - *wxResp.DistributedCoupons + xx.AvailableStock = fmt.Sprintf("%d", availableStock) + + cmbData, _ := json.Marshal(xx) + fmt.Printf("\nCMB[%s]", string(cmbData)) } func QueryCallback(bc *conf.Bootstrap) { From 1622904903cb58c620b45cb3b52a53dad21f7115 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 27 Mar 2026 14:53:48 +0800 Subject: [PATCH 128/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/bank_multi_activity_test.go | 3 ++- test/coupon.go | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test/bank_multi_activity_test.go b/test/bank_multi_activity_test.go index 06dc017..ff4e81f 100644 --- a/test/bank_multi_activity_test.go +++ b/test/bank_multi_activity_test.go @@ -4,11 +4,12 @@ import ( "context" "encoding/base64" "encoding/json" + "errors" "fmt" "net/http" - "errors" "testing" "time" + "voucher/internal/biz/bo" "voucher/internal/biz/businesserr" "voucher/internal/pkg/helper" "voucher/internal/pkg/supplier/qixing" diff --git a/test/coupon.go b/test/coupon.go index 1adb2c2..4b30b75 100644 --- a/test/coupon.go +++ b/test/coupon.go @@ -11,9 +11,8 @@ import ( "path/filepath" "time" v1 "voucher/api/v1" - "voucher/internal/biz/vo" "voucher/internal/biz/businesserr" - "voucher/internal/biz/do" + "voucher/internal/biz/vo" "voucher/internal/conf" "voucher/internal/data" ) From 5cf0459b39240997d898e4a28e4ca7b50dfe6cfd Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 27 Mar 2026 14:54:38 +0800 Subject: [PATCH 129/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/coupon_test.go | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/test/coupon_test.go b/test/coupon_test.go index 495f1ba..e6b38d9 100644 --- a/test/coupon_test.go +++ b/test/coupon_test.go @@ -97,34 +97,3 @@ func Test_SetCallback(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) -} From 5263744d6be3b701db3ea24b4635a56b82fa2220 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 27 Mar 2026 16:05:39 +0800 Subject: [PATCH 130/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/pkg/request/request_test.go | 23 ++++++++++++----------- test/bank_multi_activity_test.go | 7 ++++--- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/internal/pkg/request/request_test.go b/internal/pkg/request/request_test.go index eef583f..f1a405f 100644 --- a/internal/pkg/request/request_test.go +++ b/internal/pkg/request/request_test.go @@ -26,8 +26,8 @@ func Test_Get(t *testing.T) { return } - t.Logf("响应体:", string(respBody)) - t.Logf("响应头:", respHeader) + t.Logf("响应体:%s", string(respBody)) + t.Logf("响应头:%+v", respHeader) } func Test_RequestHeaders(t *testing.T) { @@ -45,8 +45,8 @@ func Test_RequestHeaders(t *testing.T) { return } - t.Logf("响应体:", string(respBody)) - t.Logf("响应头:", respHeader) + t.Logf("响应体:%s", string(respBody)) + t.Logf("响应头:%+v", respHeader) } func Test_RequestStatusCode(t *testing.T) { @@ -64,26 +64,27 @@ func Test_RequestStatusCode(t *testing.T) { return } - t.Logf("响应体:", string(respBody)) - t.Logf("响应头:", respHeader) + t.Logf("响应体:%s", string(respBody)) + t.Logf("响应头:%+v", respHeader) } func Test_WxNotifyRequest(t *testing.T) { uri := "https://gateway.dev.cdlsxd.cn/voucher/v1/notify/1100040695" + //uri := "https://voucher.86698.cn/voucher/v1/notify/1100040695" - body := []byte(`{"id":"fd06376a-3e1b-5516-81f8-9b69cf1ba416","create_time":"2025-07-28T16:10:15+08:00","resource_type":"encrypt-resource","event_type":"MCHTRANSFER.BILL.FINISHED","summary":"商家转账单据终态通知","resource":{"original_type":"mch_payment","algorithm":"AEAD_AES_256_GCM","ciphertext":"XJBIhrHgbe9NR5q/jLYmZKdT/3xuKm2x7EFu3T52Hj2hjPzarRSA2HCsGTxGojfD+CFyJHIULlL2adqLijAjpi3B6TaYKY4LqhtJ/RYSQtYNxYvBpWX1yLOWe8luJbWxmQvKZxIekFs8lGVgkPBUw0IfEAvJ6jHAGCcgxLIqxgOf6UtGUqxCCNp/V3xy8zCiHB0Mvlw8eXCTuG+ZESJIXvloVGNS79R6iNeqk4kNKRSaV86MNh1KQlmoBxZ4yEshD/vIlMulU3xEc+mM25y8vUS4Ot6pxEpUdUyjwcb9QTwTTnZzm6i+VWYymcItAVBQrvsKBMmqWnPtNXG8++13k3DeO1LyVKURmnWXXT1mImmGx/teN/1xPV5y6nChu/HTbcJGDQy2twuq6TPFbbYlTjZH047z/ZtozJNvGNeh","associated_data":"mch_payment","nonce":"YN3eW5H8mxLs"}}`) + bodyBytes := []byte(`{"id":"0e92e862-66b0-55a3-b1da-0e54a820b923","create_time":"2026-03-27T14:20:12+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","resource":{"original_type":"coupon","algorithm":"AEAD_AES_256_GCM","ciphertext":"LFFqFfGIu1uHYbKwqxND29QS7sH54U1k9uj+W5ecKf0ZzBT8YzikcSqa6W0iIcpnlQE0AJkDxs/nfJQ/pcRJajwy+4JzE8fzrMmhn1vgvUiXt+EyvJ8mPJwfVMtg8YtMuQBUUPxIyc5ACZVawGDVlwG9nxFYCGF0jZAtzKdwZw2MMmGFTCJri6kCE60hic5Yn3b9iPaCwmXdFlVKA6NozMEp2uNoQYYzy1+OEAfyXHyspz0IQnhUFcVuxfnGbK4zJQYUZ8yGhg3YfNFTtDW0vRTBwkNeR8BWy/LXSKMZJ7sC+SaX7eASd0QHXA/UPWpItCQGRDeZHUXWEQw6yB3GpHfKRTlO5H7VINLVe9pj+XWnXMzDEqDShpUnCP3cgf7TBYykopXPKXfI/bekTxy6ZWbikQH1Ts9lc/AMqd3NK7H5AmQPNld6DkBkt1jgI/DP5tUJeyQyYX39fX0pJCFpy94AQhXyPXLKT2VQHmnX3qnaQyztgY7GfvavtmgTNFunKcnqXtgU65wpg156iTGhHyoG01msdoUMjynY7pbdUHM1O/ljsizysqiGBvYaDfhx4Mcf8Fd3kT5bbuelM2DLg7nycz9ZjPrsKWz47eNG2U3hixk6yVxrBpAsO4QM7/ngB1+5Z7Pv06nSoP7jnFxIMv9iwPn5Y+O5tb1JfO/2su7UTtDcY25iJBqiKJvwgtzy9IB1hR781PPNzHVDT9ORUfJVVlrkGgaOn7aMDyzv3DYYUkK0fTENAReCa7D/wrajM6U6Gu8Z1/TfKkJ9BdwbEqI=","associated_data":"coupon","nonce":"b2AW4NUIk2AX"}}`) - hds := `{"Accept":["*/*"],"Cache-Control":["no-cache"],"Connection":["close"],"Content-Length":["775"],"Content-Type":["application/json"],"Pragma":["no-cache"],"User-Agent":["Mozilla/4.0"],"Wechatpay-Nonce":["dF8R9izUJnPBjVLa2cAcCaa7j6QUgitl"],"Wechatpay-Serial":["PUB_KEY_ID_0116523224422025061800192371001800"],"Wechatpay-Signature":["SoXIiTRTr6jofXXxGlfO+wyf1IzXFXcsfvEU2EggQfRKFu+8h3TT6QMQ8zIf8dpkkTPexB/3igGiATrR3uZY4ZeOpRrhIFHSJj0Ala0Ri2Nt4zk+MuBQnhybSYJ4Cn3/sHC4i2HFoOSil7OqlSr79hjod3h0tjYVQLtZ4+Cjp0IeMNB4p5qmIuERuhtfRqcyqXik9/uYNYxw8/Wkf1mMnTsBxyXK3iHAoinXNrEiqCCrQHCfnORMYosr7l+Ox8v9u1c8FFt+rt09vKssVCqYaZ/XRala3mjslDRiluFKSuqb7/JO3AxQjBK6M0iSZOlnmiXSIAq+UxJg4cem6wHi+g=="],"Wechatpay-Signature-Type":["WECHATPAY2-SHA256-RSA2048"],"Wechatpay-Timestamp":["1753690220"],"X-Forwarded-For":["121.51.58.168"],"X-Forwarded-Proto":["https"],"X-Real-Ip":["121.51.58.168"]}` + header := `{"Accept":["*/*"],"Cache-Control":["no-cache"],"Connection":["close"],"Content-Length":["1109"],"Content-Type":["application/json"],"Pragma":["no-cache"],"User-Agent":["Mozilla/4.0"],"Wechatpay-Nonce":["OaVm4rYgmQnOzu9dra2OM9J560D5uuLh"],"Wechatpay-Serial":["PUB_KEY_ID_0111000406952026032500382251001000"],"Wechatpay-Signature":["QUnj2WwPfh3nAycuwQ/7zFVjhSHObnFfmfLnPx/4UXjgFlkY1REyCHrBHrJ1lDpUOodiY5qsGg/7CQ6aMlXZe24xsLOBjwOgZr0JGmsRisrwnQL+jy6ar8pAf1fABv+e/lCLO+UZtLXrv1GCQix51GLerkELEVTQkn6qzT4aVyL1Ukte6hk+r3HlWOQC6OM3cpxyr8XhVilGWaElRCgPwHI2XYLGjH6QWtcRnCSMESLFGOwI7t9GbxgGLptEelfmVpBHdRFTDWa8rWJI7b9ArI//vMawkJUE7tW5nkqF5sWMydKUQ5ZDLf5oI447zic/LOljZ4SA7I/aTN4MlpulDA=="],"Wechatpay-Signature-Type":["WECHATPAY2-SHA256-RSA2048"],"Wechatpay-Timestamp":["1774592412"],"X-Forwarded-For":["121.51.58.169"],"X-Real-Ip":["121.51.58.169"]}` var headerMap http.Header - if err := json.Unmarshal([]byte(hds), &headerMap); err != nil { + if err := json.Unmarshal([]byte(header), &headerMap); err != nil { t.Error(fmt.Sprintf("解析 headers 失败: %v", err)) return } hc := &http.Client{ - Timeout: 10 * time.Second, + Timeout: 5 * time.Second, Transport: &http.Transport{ MaxIdleConns: 1, // 最大空闲连接数 MaxIdleConnsPerHost: 1, // 每个主机的最大空闲连接数 @@ -95,7 +96,7 @@ func Test_WxNotifyRequest(t *testing.T) { return code == http.StatusOK || code == http.StatusCreated } - respHeader, respBody, err := Post(context.Background(), uri, body, WithHttpClient(hc), WithStatusCodeFunc(isSuccess)) + respHeader, respBody, err := Post(context.Background(), uri, bodyBytes, WithHttpClient(hc), WithStatusCodeFunc(isSuccess)) if err != nil { t.Error(err) return diff --git a/test/bank_multi_activity_test.go b/test/bank_multi_activity_test.go index 9ce5368..c3fea03 100644 --- a/test/bank_multi_activity_test.go +++ b/test/bank_multi_activity_test.go @@ -96,8 +96,9 @@ func Test_QixingNotifyData(t *testing.T) { func Test_MarketingNotify(t *testing.T) { - header := `{"Accept":["*/*"], "Cache-Control":["no-cache"], "Connection":["close"], "Content-Length":["1109"], "Content-Type":["application/json"], "Pragma":["no-cache"], "User-Agent":["Mozilla/4.0"], "Wechatpay-Nonce":["N61AnMoOlDsi6WnCV1xgasslXt6Hqndb"], "Wechatpay-Serial":["PUB_KEY_ID_0111000406952026032500382251001000"], "Wechatpay-Signature":["skDPjPLpIway2ll3J+o3FYnF+3Z75PuNge92LaxDDWHu8V6OXWXHUPLaJilxg/UidBcaP5JKza9t2JIgNtdRXfjPEehi7Ufn5yhPR9EUg4T1PFhkri/N8x1bvZ2oDemsTDHNNxr2PrPla2K5EcNRe6MfVpLKLp/xxX5UXfII8UHjI50jDSSoJQpSZZS0jbm5yfAeokbus0aD7835JKWksLGIuM94s5TYV8fCNL1RVab/ArLJ6UsTf0mjPEVWMdT3TePwdtVFZhFqGxlTFnR2LYSuLNflxQiE7cgtBK76L1+lMit7KlARy7aPSsOpn3DAl1sivnSvP6hX5ljOXnjv6g=="], "Wechatpay-Signature-Type":["WECHATPAY2-SHA256-RSA2048"], "Wechatpay-Timestamp":["1774495201"], "X-Forwarded-For":["121.51.58.172"], "X-Real-Ip":["121.51.58.172"]}` - bodyStr := `{"id":"2f6dba61-17d2-554c-99f1-c2527bac8013","create_time":"2026-03-26T11:14:56+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","resource":{"original_type":"coupon","algorithm":"AEAD_AES_256_GCM","ciphertext":"fr2/gKAby7C+R8Yl/pcWLQEa+RYFDmi2LGSaZtghMtMWs+6z5XolFtNTs65JArRUjptKR4ATn0uAp+Yi2S2ly6+jV07VnUmEBk7ijPXHgkZ53VkNSKgYIC6pOumNcA3MdZxVHPRYawvsluat4m0X7/BqW3qjR9L9DYq4uXtc4pXBC2lcSmtcIENU5lHRmSU83iLp9Gp+LZ3MGaF8ExMc+ZkQcj7RRruiXqWaICeY7c0ebXQJtulytT1FSMksb5nfaf0MmElO+9fOwH1naOrdvSSQ4tglThpfEHuIZ2xaT7DwcBDYvmye+vg+fgnxE66sskz5fwdVtgiGdNSWzA8CZrP/4oXUpbNpjpY2r0GJs5etpCU5hqimz0S1zCLEOtgl/LQ3GCZA1AzZeRcIferbJotC1toJD3tALyO89VSbwcGr8kk6e1HAGyXhoIt5RC3sFnbUdHQLf01MbNhJg13p1YbqInLI3ktjlst4N+gGXmCHHzHlpeOHgdF2uvrWTE/XTDlfumGKBDLFkSOu62pldQfLKVU++BPvvqaRXK9m4g5F87dwOJQJyltm3LYwKfpg1vIMw6ch1Nvrj79+xQs0Xa32Ds8omjI3GmMyly/BMvWJkFpTivrgUnXXMLfzDIFLRB5cR4dGQkJWFBY/qkjHu3yvTqcJuiaUqaHFKjhJJt1WpVMWaj4WlsbOb/oI3eTroxzynNOhgmYWMrM47OSfHJ1MhGU3c18Zk89zyY8miVe/CaPhwwmBnIgeYoBzj5ic36HXJN1Qkup1FYAOadWWXwc=","associated_data":"coupon","nonce":"I4U3QlKjYHlc"}}` + bodyBytes := []byte(`{"id":"0e92e862-66b0-55a3-b1da-0e54a820b923","create_time":"2026-03-27T14:20:12+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","resource":{"original_type":"coupon","algorithm":"AEAD_AES_256_GCM","ciphertext":"LFFqFfGIu1uHYbKwqxND29QS7sH54U1k9uj+W5ecKf0ZzBT8YzikcSqa6W0iIcpnlQE0AJkDxs/nfJQ/pcRJajwy+4JzE8fzrMmhn1vgvUiXt+EyvJ8mPJwfVMtg8YtMuQBUUPxIyc5ACZVawGDVlwG9nxFYCGF0jZAtzKdwZw2MMmGFTCJri6kCE60hic5Yn3b9iPaCwmXdFlVKA6NozMEp2uNoQYYzy1+OEAfyXHyspz0IQnhUFcVuxfnGbK4zJQYUZ8yGhg3YfNFTtDW0vRTBwkNeR8BWy/LXSKMZJ7sC+SaX7eASd0QHXA/UPWpItCQGRDeZHUXWEQw6yB3GpHfKRTlO5H7VINLVe9pj+XWnXMzDEqDShpUnCP3cgf7TBYykopXPKXfI/bekTxy6ZWbikQH1Ts9lc/AMqd3NK7H5AmQPNld6DkBkt1jgI/DP5tUJeyQyYX39fX0pJCFpy94AQhXyPXLKT2VQHmnX3qnaQyztgY7GfvavtmgTNFunKcnqXtgU65wpg156iTGhHyoG01msdoUMjynY7pbdUHM1O/ljsizysqiGBvYaDfhx4Mcf8Fd3kT5bbuelM2DLg7nycz9ZjPrsKWz47eNG2U3hixk6yVxrBpAsO4QM7/ngB1+5Z7Pv06nSoP7jnFxIMv9iwPn5Y+O5tb1JfO/2su7UTtDcY25iJBqiKJvwgtzy9IB1hR781PPNzHVDT9ORUfJVVlrkGgaOn7aMDyzv3DYYUkK0fTENAReCa7D/wrajM6U6Gu8Z1/TfKkJ9BdwbEqI=","associated_data":"coupon","nonce":"b2AW4NUIk2AX"}}`) + + header := `{"Accept":["*/*"],"Cache-Control":["no-cache"],"Connection":["close"],"Content-Length":["1109"],"Content-Type":["application/json"],"Pragma":["no-cache"],"User-Agent":["Mozilla/4.0"],"Wechatpay-Nonce":["OaVm4rYgmQnOzu9dra2OM9J560D5uuLh"],"Wechatpay-Serial":["PUB_KEY_ID_0111000406952026032500382251001000"],"Wechatpay-Signature":["QUnj2WwPfh3nAycuwQ/7zFVjhSHObnFfmfLnPx/4UXjgFlkY1REyCHrBHrJ1lDpUOodiY5qsGg/7CQ6aMlXZe24xsLOBjwOgZr0JGmsRisrwnQL+jy6ar8pAf1fABv+e/lCLO+UZtLXrv1GCQix51GLerkELEVTQkn6qzT4aVyL1Ukte6hk+r3HlWOQC6OM3cpxyr8XhVilGWaElRCgPwHI2XYLGjH6QWtcRnCSMESLFGOwI7t9GbxgGLptEelfmVpBHdRFTDWa8rWJI7b9ArI//vMawkJUE7tW5nkqF5sWMydKUQ5ZDLf5oI447zic/LOljZ4SA7I/aTN4MlpulDA=="],"Wechatpay-Signature-Type":["WECHATPAY2-SHA256-RSA2048"],"Wechatpay-Timestamp":["1774592412"],"X-Forwarded-For":["121.51.58.169"],"X-Real-Ip":["121.51.58.169"]}` httpHeaders := make(http.Header) if err := json.Unmarshal([]byte(header), &httpHeaders); err != nil { @@ -105,7 +106,7 @@ func Test_MarketingNotify(t *testing.T) { return } - body, bizContent, err := marketingFJLF().Notify(context.Background(), &httpHeaders, []byte(bodyStr)) + body, bizContent, err := marketingFJLF().Notify(context.Background(), &httpHeaders, bodyBytes) if err != nil { t.Errorf("notify err: %+v\n", err) return From 2ceb9ed9d0e712dfed9ee781a382626749a307e9 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 27 Mar 2026 16:10:29 +0800 Subject: [PATCH 131/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/businesserr/err.go | 3 +++ internal/biz/order.go | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/internal/biz/businesserr/err.go b/internal/biz/businesserr/err.go index 3bd9943..dcc3558 100644 --- a/internal/biz/businesserr/err.go +++ b/internal/biz/businesserr/err.go @@ -13,6 +13,9 @@ func (e *BusinessErr) Error() string { } var ( + BatchNotSetStartedError = &BusinessErr{Code: ErrCode("400"), Message: "批次开始时间未设置"} + BatchSetEndedError = &BusinessErr{Code: ErrCode("400"), Message: "批次已结束时间未设置"} + BatchNotStartedError = &BusinessErr{Code: ErrCode("400"), Message: "批次未开始"} BatchEndedError = &BusinessErr{Code: ErrCode("400"), Message: "批次已结束"} ) diff --git a/internal/biz/order.go b/internal/biz/order.go index 1d8c3c3..c08c612 100644 --- a/internal/biz/order.go +++ b/internal/biz/order.go @@ -34,6 +34,13 @@ func (this *VoucherBiz) CmbOrder(ctx context.Context, req *bo.OrderCreateReqBo) return nil, product, err3 } + if product.StartTime == nil { + return nil, product, businesserr.BatchNotSetStartedError + } + if product.EndTime == nil { + return nil, product, businesserr.BatchSetEndedError + } + nowTime := time.Now() if nowTime.Before(*product.StartTime) { return nil, product, businesserr.BatchNotStartedError From 737216ee0533f01df414ae5b41f4538c7df7f375 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 27 Mar 2026 16:14:58 +0800 Subject: [PATCH 132/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/pkg/request/request_test.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/pkg/request/request_test.go b/internal/pkg/request/request_test.go index f1a405f..7394914 100644 --- a/internal/pkg/request/request_test.go +++ b/internal/pkg/request/request_test.go @@ -70,15 +70,15 @@ func Test_RequestStatusCode(t *testing.T) { func Test_WxNotifyRequest(t *testing.T) { - uri := "https://gateway.dev.cdlsxd.cn/voucher/v1/notify/1100040695" - //uri := "https://voucher.86698.cn/voucher/v1/notify/1100040695" + //uri := "https://gateway.dev.cdlsxd.cn/voucher/v1/notify/1100040695" + uri := "https://voucher.86698.cn/voucher/v1/notify/1100040695" bodyBytes := []byte(`{"id":"0e92e862-66b0-55a3-b1da-0e54a820b923","create_time":"2026-03-27T14:20:12+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","resource":{"original_type":"coupon","algorithm":"AEAD_AES_256_GCM","ciphertext":"LFFqFfGIu1uHYbKwqxND29QS7sH54U1k9uj+W5ecKf0ZzBT8YzikcSqa6W0iIcpnlQE0AJkDxs/nfJQ/pcRJajwy+4JzE8fzrMmhn1vgvUiXt+EyvJ8mPJwfVMtg8YtMuQBUUPxIyc5ACZVawGDVlwG9nxFYCGF0jZAtzKdwZw2MMmGFTCJri6kCE60hic5Yn3b9iPaCwmXdFlVKA6NozMEp2uNoQYYzy1+OEAfyXHyspz0IQnhUFcVuxfnGbK4zJQYUZ8yGhg3YfNFTtDW0vRTBwkNeR8BWy/LXSKMZJ7sC+SaX7eASd0QHXA/UPWpItCQGRDeZHUXWEQw6yB3GpHfKRTlO5H7VINLVe9pj+XWnXMzDEqDShpUnCP3cgf7TBYykopXPKXfI/bekTxy6ZWbikQH1Ts9lc/AMqd3NK7H5AmQPNld6DkBkt1jgI/DP5tUJeyQyYX39fX0pJCFpy94AQhXyPXLKT2VQHmnX3qnaQyztgY7GfvavtmgTNFunKcnqXtgU65wpg156iTGhHyoG01msdoUMjynY7pbdUHM1O/ljsizysqiGBvYaDfhx4Mcf8Fd3kT5bbuelM2DLg7nycz9ZjPrsKWz47eNG2U3hixk6yVxrBpAsO4QM7/ngB1+5Z7Pv06nSoP7jnFxIMv9iwPn5Y+O5tb1JfO/2su7UTtDcY25iJBqiKJvwgtzy9IB1hR781PPNzHVDT9ORUfJVVlrkGgaOn7aMDyzv3DYYUkK0fTENAReCa7D/wrajM6U6Gu8Z1/TfKkJ9BdwbEqI=","associated_data":"coupon","nonce":"b2AW4NUIk2AX"}}`) - header := `{"Accept":["*/*"],"Cache-Control":["no-cache"],"Connection":["close"],"Content-Length":["1109"],"Content-Type":["application/json"],"Pragma":["no-cache"],"User-Agent":["Mozilla/4.0"],"Wechatpay-Nonce":["OaVm4rYgmQnOzu9dra2OM9J560D5uuLh"],"Wechatpay-Serial":["PUB_KEY_ID_0111000406952026032500382251001000"],"Wechatpay-Signature":["QUnj2WwPfh3nAycuwQ/7zFVjhSHObnFfmfLnPx/4UXjgFlkY1REyCHrBHrJ1lDpUOodiY5qsGg/7CQ6aMlXZe24xsLOBjwOgZr0JGmsRisrwnQL+jy6ar8pAf1fABv+e/lCLO+UZtLXrv1GCQix51GLerkELEVTQkn6qzT4aVyL1Ukte6hk+r3HlWOQC6OM3cpxyr8XhVilGWaElRCgPwHI2XYLGjH6QWtcRnCSMESLFGOwI7t9GbxgGLptEelfmVpBHdRFTDWa8rWJI7b9ArI//vMawkJUE7tW5nkqF5sWMydKUQ5ZDLf5oI447zic/LOljZ4SA7I/aTN4MlpulDA=="],"Wechatpay-Signature-Type":["WECHATPAY2-SHA256-RSA2048"],"Wechatpay-Timestamp":["1774592412"],"X-Forwarded-For":["121.51.58.169"],"X-Real-Ip":["121.51.58.169"]}` + header := []byte(`{"Accept":["*/*"],"Cache-Control":["no-cache"],"Connection":["close"],"Content-Length":["1109"],"Content-Type":["application/json"],"Pragma":["no-cache"],"User-Agent":["Mozilla/4.0"],"Wechatpay-Nonce":["OaVm4rYgmQnOzu9dra2OM9J560D5uuLh"],"Wechatpay-Serial":["PUB_KEY_ID_0111000406952026032500382251001000"],"Wechatpay-Signature":["QUnj2WwPfh3nAycuwQ/7zFVjhSHObnFfmfLnPx/4UXjgFlkY1REyCHrBHrJ1lDpUOodiY5qsGg/7CQ6aMlXZe24xsLOBjwOgZr0JGmsRisrwnQL+jy6ar8pAf1fABv+e/lCLO+UZtLXrv1GCQix51GLerkELEVTQkn6qzT4aVyL1Ukte6hk+r3HlWOQC6OM3cpxyr8XhVilGWaElRCgPwHI2XYLGjH6QWtcRnCSMESLFGOwI7t9GbxgGLptEelfmVpBHdRFTDWa8rWJI7b9ArI//vMawkJUE7tW5nkqF5sWMydKUQ5ZDLf5oI447zic/LOljZ4SA7I/aTN4MlpulDA=="],"Wechatpay-Signature-Type":["WECHATPAY2-SHA256-RSA2048"],"Wechatpay-Timestamp":["1774592412"],"X-Forwarded-For":["121.51.58.169"],"X-Real-Ip":["121.51.58.169"]}`) var headerMap http.Header - if err := json.Unmarshal([]byte(header), &headerMap); err != nil { + if err := json.Unmarshal(header, &headerMap); err != nil { t.Error(fmt.Sprintf("解析 headers 失败: %v", err)) return } @@ -96,7 +96,7 @@ func Test_WxNotifyRequest(t *testing.T) { return code == http.StatusOK || code == http.StatusCreated } - respHeader, respBody, err := Post(context.Background(), uri, bodyBytes, WithHttpClient(hc), WithStatusCodeFunc(isSuccess)) + respHeader, respBody, err := Post(context.Background(), uri, bodyBytes, WithHttpClient(hc), WithStatusCodeFunc(isSuccess), WithHeaders(headerMap)) if err != nil { t.Error(err) return From 0e1ed3b4261d10df8539c5ba1edab2c29a2173d7 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 27 Mar 2026 16:16:08 +0800 Subject: [PATCH 133/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- test/coupon.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/test/coupon.go b/test/coupon.go index 4b30b75..0c42d4c 100644 --- a/test/coupon.go +++ b/test/coupon.go @@ -312,8 +312,9 @@ func SetCallback(bc *conf.Bootstrap) { svc := cashcoupons.CallBackUrlApiService{Client: client} response, _, err := svc.SetCallback(ctx, cashcoupons.SetCallbackRequest{ - Mchid: core.String(bc.Wechat.MchID), - NotifyUrl: core.String("https://gateway.dev.cdlsxd.cn/voucher/v1/notify/" + bc.Wechat.MchID), + Mchid: core.String(bc.Wechat.MchID), + //NotifyUrl: core.String("https://gateway.dev.cdlsxd.cn/voucher/v1/notify/" + bc.Wechat.MchID), + NotifyUrl: core.String("https://voucher.86698.cn/voucher/v1/notify/" + bc.Wechat.MchID), Switch: core.Bool(true), }) if err != nil { From be2adefb91591b79e41980c239700f6049f59ae2 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 27 Mar 2026 16:47:13 +0800 Subject: [PATCH 134/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/register_tag.go | 1 + internal/biz/repo/product.go | 1 + internal/data/repoimpl/product.go | 9 +++++++++ 3 files changed, 11 insertions(+) diff --git a/internal/biz/register_tag.go b/internal/biz/register_tag.go index 5a4c902..54ffb52 100644 --- a/internal/biz/register_tag.go +++ b/internal/biz/register_tag.go @@ -31,6 +31,7 @@ func (this *VoucherBiz) RegisterTag(ctx context.Context, id int32) error { } } + _ = this.ProductRepo.DelCacheByProductNo(ctx, stock.ProductNo) _, err = this.ProductRepo.GetByProductNo(ctx, stock.ProductNo) return err diff --git a/internal/biz/repo/product.go b/internal/biz/repo/product.go index fbf75f2..ef01d26 100644 --- a/internal/biz/repo/product.go +++ b/internal/biz/repo/product.go @@ -12,5 +12,6 @@ type ProductRepo interface { GetByBatchNo(ctx context.Context, batchNo string) (*bo.ProductBo, error) GetByMchStockId(ctx context.Context, mchId, stockId string) (*bo.ProductBo, error) GetByProductNo(ctx context.Context, productNo string) (*bo.ProductBo, error) + DelCacheByProductNo(ctx context.Context, productNo string) error UpdateByWxResp(ctx context.Context, id int32, req *do.WxResp) error } diff --git a/internal/data/repoimpl/product.go b/internal/data/repoimpl/product.go index d1510b4..93e3592 100644 --- a/internal/data/repoimpl/product.go +++ b/internal/data/repoimpl/product.go @@ -131,6 +131,15 @@ func (r *ProductRepoImpl) GetByMchStockId(ctx context.Context, mchId, stockId st return r.ToBo(item), nil } +func (r *ProductRepoImpl) DelCacheByProductNo(ctx context.Context, productNo string) error { + + c := vo.ProductQueryKey.BuildCache([]string{productNo}) + + _, _ = r.rdb.Rdb.Del(ctx, c.Key).Result() + + return nil +} + func (r *ProductRepoImpl) GetByProductNo(ctx context.Context, productNo string) (*bo.ProductBo, error) { c := vo.ProductQueryKey.BuildCache([]string{productNo}) From d24e7671e08b4b3e037e2b83c1cd4943a232fc8f Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 27 Mar 2026 17:07:12 +0800 Subject: [PATCH 135/144] =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BB=E4=BD=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/wechat_notify.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index fe900a3..9ce0b06 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -78,6 +78,7 @@ func (this *VoucherBiz) notifyUsed(ctx context.Context, order *bo.OrderBo, req * return err } + order.LastUseTime = &req.PlainText.ConsumeInformation.ConsumeTime return this.notify(ctx, order) } From 2c7d2581f7f2a6aec4c0fd2e53594b7a456c5b3b Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 27 Mar 2026 17:07:53 +0800 Subject: [PATCH 136/144] =?UTF-8?q?=E6=94=B9=E9=80=A0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/wechat_notify.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index 9ce0b06..489c521 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -74,7 +74,13 @@ func (this *VoucherBiz) notifyUsed(ctx context.Context, order *bo.OrderBo, req * return nil } - if err := this.OrderRepo.NotifyUsed(ctx, order.ID, req.PlainText.ConsumeInformation.TransactionID, req.PlainText.ConsumeInformation.ConsumeTime); err != nil { + err := this.OrderRepo.NotifyUsed( + ctx, + order.ID, + req.PlainText.ConsumeInformation.TransactionID, + req.PlainText.ConsumeInformation.ConsumeTime, + ) + if err != nil { return err } From 9f768f037c916a0d5e5794fdc4a25c177a8a5bd7 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 27 Mar 2026 17:09:40 +0800 Subject: [PATCH 137/144] =?UTF-8?q?=E6=94=B9=E9=80=A0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/wechat_notify.go | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index 489c521..0c3206c 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -122,16 +122,17 @@ func (this *VoucherBiz) notify(ctx context.Context, order *bo.OrderBo) error { return nil // 多笔立减活动,不做通知(?不知道有没有核销通知,多笔核销情况未知) } - return this.cmbNotify(ctx, order.ID) + return this.cmbNotify(ctx, order) } -func (this *VoucherBiz) cmbNotify(ctx context.Context, orderId uint64) error { +func (this *VoucherBiz) cmbNotify(ctx context.Context, orderReq *bo.OrderBo) error { - order, err := this.OrderRepo.GetByID(ctx, orderId) + order, err := this.OrderRepo.GetByID(ctx, orderReq.ID) if err != nil { return err } + order.LastUseTime = orderReq.LastUseTime if orderNotify, err2 := this.Cmb.Notify(ctx, order); err != nil { if !errPb.IsNeedRetryNotify(err2) { From ee9203f7d659e5dcd9b5f9cdcf71677d6c54ad47 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 27 Mar 2026 17:54:10 +0800 Subject: [PATCH 138/144] =?UTF-8?q?=E6=94=B9=E9=80=A0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/bo/multi_notify_log_bo.go | 1 + internal/biz/multi.go | 37 ++++++++++----------- internal/data/model/multi_notify_log.gen.go | 1 + internal/data/repoimpl/multi_notify_log.go | 1 + 4 files changed, 20 insertions(+), 20 deletions(-) diff --git a/internal/biz/bo/multi_notify_log_bo.go b/internal/biz/bo/multi_notify_log_bo.go index 1fb27c9..5c41e58 100644 --- a/internal/biz/bo/multi_notify_log_bo.go +++ b/internal/biz/bo/multi_notify_log_bo.go @@ -21,6 +21,7 @@ type MultiNotifyLogBo struct { TransactionID string RequestURL string RequestStatus int32 + OriginReq string Request string Response string OrderCreateTime *time.Time diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 23d764d..74b615c 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -141,12 +141,12 @@ func (biz *MultiBiz) RetryRunByMultiNotifyDataId(ctx context.Context, multiNotif func (biz *MultiBiz) run(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) error { - nl, err := biz.nlCreate(ctx, req, mnd, order) + nl, request, err := biz.nlCreate(ctx, req, mnd, order) if err != nil { return fmt.Errorf("创建通知日志错误 error: %v", err) } - if err = biz.Request(ctx, mnd, nl, order); err != nil { + if err = biz.Request(ctx, mnd, nl, request); err != nil { return fmt.Errorf("请求错误 error: %v", err) } @@ -195,10 +195,10 @@ func (biz *MultiBiz) mndCreate(ctx context.Context, ip, source string, req *bo.W }) } -func (biz *MultiBiz) nlCreate(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) (*bo.MultiNotifyLogBo, error) { +func (biz *MultiBiz) nlCreate(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) (*bo.MultiNotifyLogBo, *v1.CmbRequest, error) { if biz.bc.Cmb.MultiNotifyUrl == "" { - return nil, fmt.Errorf("CMB多笔立减金通知地址为空") + return nil, nil, fmt.Errorf("CMB多笔立减金通知地址为空") } nl := &bo.MultiNotifyLogBo{ @@ -220,22 +220,23 @@ func (biz *MultiBiz) nlCreate(ctx context.Context, req *bo.WechatVoucherNotifyBo CouponCreateTime: &req.PlainText.CreateTime, } - request, err := biz.GetRequest(ctx, nl, order) + request, cmbRequestBo, err := biz.GetRequest(ctx, nl, order) if err != nil { - return nil, err + return nil, nil, err } b, _ := json.Marshal(request) + nl.OriginReq = cmbRequestBo.BizContent nl.Request = string(b) res, err := biz.MultiNotifyLogRepo.Create(ctx, nl) if err != nil { - return nil, fmt.Errorf("创建通知日志错误 error: %v", err) + return nil, nil, fmt.Errorf("创建通知日志错误 error: %v", err) } // 创建出来的核销时间精度有差距,重新赋值返回出去 res.ConsumeTime = mnd.ConsumeTime - return res, nil + return res, request, nil } func (biz *MultiBiz) bizContent(nl *bo.MultiNotifyLogBo, order *bo.OrderBo) (string, error) { @@ -268,25 +269,26 @@ func (biz *MultiBiz) bizContent(nl *bo.MultiNotifyLogBo, order *bo.OrderBo) (str return string(bizJsonBytes), nil } -func (biz *MultiBiz) GetRequest(ctx context.Context, nl *bo.MultiNotifyLogBo, order *bo.OrderBo) (*v1.CmbRequest, error) { +func (biz *MultiBiz) GetRequest(ctx context.Context, nl *bo.MultiNotifyLogBo, order *bo.OrderBo) (*v1.CmbRequest, *bo.CmbRequestBo, error) { bizContent, err := biz.bizContent(nl, order) if err != nil { - return nil, err + return nil, nil, err } - request, err := biz.CmbMixRepo.GetRequest(ctx, &bo.CmbRequestBo{ + r := &bo.CmbRequestBo{ FuncName: vo.CmbNotifyFuncNameUpdateCodeStatusForMulti, BizContent: bizContent, - }) + } + request, err := biz.CmbMixRepo.GetRequest(ctx, r) if err != nil { - return nil, err + return nil, nil, err } - return request, nil + return request, r, nil } -func (biz *MultiBiz) Request(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl *bo.MultiNotifyLogBo, order *bo.OrderBo) error { +func (biz *MultiBiz) Request(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl *bo.MultiNotifyLogBo, request *v1.CmbRequest) error { if nl.RequestURL == "" { if err := biz.notifyFail(ctx, nl, "回调通知招行地址为空,不做通知"); err != nil { @@ -296,11 +298,6 @@ func (biz *MultiBiz) Request(ctx context.Context, mmd *bo.MultiNotifyDataBo, nl return nil } - request, err := biz.GetRequest(ctx, nl, order) - if err != nil { - return err - } - reply, err := biz.CmbMixRepo.Request(ctx, request, nl.RequestURL) if err != nil { if err3 := biz.notifyFail(ctx, nl, err.Error()); err3 != nil { diff --git a/internal/data/model/multi_notify_log.gen.go b/internal/data/model/multi_notify_log.gen.go index 8a459e2..627d601 100644 --- a/internal/data/model/multi_notify_log.gen.go +++ b/internal/data/model/multi_notify_log.gen.go @@ -26,6 +26,7 @@ type MultiNotifyLog struct { TransactionID string `gorm:"column:transaction_id;not null;comment:微信支付系统生成的订单号" json:"transaction_id"` // 微信支付系统生成的订单号 RequestURL string `gorm:"column:request_url;not null;comment:请求地址" json:"request_url"` // 请求地址 RequestStatus int32 `gorm:"column:request_status;not null;comment:请求状态" json:"request_status"` // 请求状态 + OriginReq string `gorm:"column:origin_req;not null;comment:请求数据" json:"origin_req"` // 请求数据 Request string `gorm:"column:request;not null;comment:请求数据" json:"request"` // 请求数据 Response string `gorm:"column:response;not null;comment:响应结果" json:"response"` // 响应结果 OrderCreateTime *time.Time `gorm:"column:order_create_time;not null;comment:券收单时间-蓝色兄弟" json:"order_create_time"` // 券收单时间-蓝色兄弟 diff --git a/internal/data/repoimpl/multi_notify_log.go b/internal/data/repoimpl/multi_notify_log.go index 78e5dcf..d1f4cec 100644 --- a/internal/data/repoimpl/multi_notify_log.go +++ b/internal/data/repoimpl/multi_notify_log.go @@ -45,6 +45,7 @@ func (p *MultiNotifyLogRepoImpl) Create(ctx context.Context, req *bo.MultiNotify ConsumeAmount: req.ConsumeAmount, ConsumeTime: req.ConsumeTime, TransactionID: req.TransactionID, + OriginReq: req.OriginReq, Request: req.Request, RequestURL: req.RequestURL, RequestStatus: vo.MultiNotifyLogStatusWait.GetValue(), From b0d22b3ad8bad5fbe093f1a237fa4c69ce07c513 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 27 Mar 2026 18:12:49 +0800 Subject: [PATCH 139/144] =?UTF-8?q?=E6=94=B9=E9=80=A0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 11 ++++++++++- test/bank_multi_activity_test.go | 5 ++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 74b615c..2e4535a 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -53,6 +53,15 @@ func NewMultiBiz( func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.WechatVoucherNotifyBo) error { + // 商品数据量较少,先查询商品是否存在,过滤多余的通知信息 + _, err := biz.ProductRepo.GetByMchStockId(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID) + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil + } + return fmt.Errorf("商品查询错误 error: %w", err) + } + cl := vo.MultiNotifyLockKey.BuildCache([]string{ source, req.PlainText.StockID, @@ -87,7 +96,7 @@ func (biz *MultiBiz) order(ctx context.Context, req *bo.WechatVoucherNotifyBo) ( order, err := biz.OrderRepo.GetByCouponId(ctx, req.PlainText.StockCreatorMchid, req.PlainText.StockID, req.PlainText.CouponID) if err != nil { - return nil, fmt.Errorf("订单查询错误 error: %v", err) + return nil, fmt.Errorf("订单查询错误 error: %w", err) } return order, nil diff --git a/test/bank_multi_activity_test.go b/test/bank_multi_activity_test.go index d2ffc1d..880e641 100644 --- a/test/bank_multi_activity_test.go +++ b/test/bank_multi_activity_test.go @@ -117,9 +117,8 @@ func Test_QixingNotifyData(t *testing.T) { func Test_MarketingNotify(t *testing.T) { - bodyBytes := []byte(`{"id":"0e92e862-66b0-55a3-b1da-0e54a820b923","create_time":"2026-03-27T14:20:12+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","resource":{"original_type":"coupon","algorithm":"AEAD_AES_256_GCM","ciphertext":"LFFqFfGIu1uHYbKwqxND29QS7sH54U1k9uj+W5ecKf0ZzBT8YzikcSqa6W0iIcpnlQE0AJkDxs/nfJQ/pcRJajwy+4JzE8fzrMmhn1vgvUiXt+EyvJ8mPJwfVMtg8YtMuQBUUPxIyc5ACZVawGDVlwG9nxFYCGF0jZAtzKdwZw2MMmGFTCJri6kCE60hic5Yn3b9iPaCwmXdFlVKA6NozMEp2uNoQYYzy1+OEAfyXHyspz0IQnhUFcVuxfnGbK4zJQYUZ8yGhg3YfNFTtDW0vRTBwkNeR8BWy/LXSKMZJ7sC+SaX7eASd0QHXA/UPWpItCQGRDeZHUXWEQw6yB3GpHfKRTlO5H7VINLVe9pj+XWnXMzDEqDShpUnCP3cgf7TBYykopXPKXfI/bekTxy6ZWbikQH1Ts9lc/AMqd3NK7H5AmQPNld6DkBkt1jgI/DP5tUJeyQyYX39fX0pJCFpy94AQhXyPXLKT2VQHmnX3qnaQyztgY7GfvavtmgTNFunKcnqXtgU65wpg156iTGhHyoG01msdoUMjynY7pbdUHM1O/ljsizysqiGBvYaDfhx4Mcf8Fd3kT5bbuelM2DLg7nycz9ZjPrsKWz47eNG2U3hixk6yVxrBpAsO4QM7/ngB1+5Z7Pv06nSoP7jnFxIMv9iwPn5Y+O5tb1JfO/2su7UTtDcY25iJBqiKJvwgtzy9IB1hR781PPNzHVDT9ORUfJVVlrkGgaOn7aMDyzv3DYYUkK0fTENAReCa7D/wrajM6U6Gu8Z1/TfKkJ9BdwbEqI=","associated_data":"coupon","nonce":"b2AW4NUIk2AX"}}`) - - header := `{"Accept":["*/*"],"Cache-Control":["no-cache"],"Connection":["close"],"Content-Length":["1109"],"Content-Type":["application/json"],"Pragma":["no-cache"],"User-Agent":["Mozilla/4.0"],"Wechatpay-Nonce":["OaVm4rYgmQnOzu9dra2OM9J560D5uuLh"],"Wechatpay-Serial":["PUB_KEY_ID_0111000406952026032500382251001000"],"Wechatpay-Signature":["QUnj2WwPfh3nAycuwQ/7zFVjhSHObnFfmfLnPx/4UXjgFlkY1REyCHrBHrJ1lDpUOodiY5qsGg/7CQ6aMlXZe24xsLOBjwOgZr0JGmsRisrwnQL+jy6ar8pAf1fABv+e/lCLO+UZtLXrv1GCQix51GLerkELEVTQkn6qzT4aVyL1Ukte6hk+r3HlWOQC6OM3cpxyr8XhVilGWaElRCgPwHI2XYLGjH6QWtcRnCSMESLFGOwI7t9GbxgGLptEelfmVpBHdRFTDWa8rWJI7b9ArI//vMawkJUE7tW5nkqF5sWMydKUQ5ZDLf5oI447zic/LOljZ4SA7I/aTN4MlpulDA=="],"Wechatpay-Signature-Type":["WECHATPAY2-SHA256-RSA2048"],"Wechatpay-Timestamp":["1774592412"],"X-Forwarded-For":["121.51.58.169"],"X-Real-Ip":["121.51.58.169"]}` + header := `{"Accept":["*/*"],"Cache-Control":["no-cache"],"Connection":["close"],"Content-Length":["1109"],"Content-Type":["application/json"],"Pragma":["no-cache"],"User-Agent":["Mozilla/4.0"],"Wechatpay-Nonce":["9OdaHVZ5OW75Wq0m1oQydpGCb4ONM2Wl"],"Wechatpay-Serial":["PUB_KEY_ID_0111000406952026032500382251001000"],"Wechatpay-Signature":["RtBgwpQebjUFhasnP3816IA0KoekMIYxqOnK79/EElILbDOpIDD1yE2ckpQ8Vb+/JJm15n5iHSQPL2MQ0xI4fDb4doaRiytIZxvr8EhxLGVmtjKmHrerTEIGf0wSoYofeE+RT4n25l06EAPNGVBmr4bd6VIhV7kz0Upmmlld4DhuDomAAjOxETd+Uy8ck2T9Y8kn31LF320DMRXrub7UG/n1PtKMI9XTHo+TRMBwVUvaWMchSVwUoqVzRejg0ogjCNE8YcxkEqx7hbvxiLPFEthGnSlZdg/0iHkCduxsiQSLo30xBTm1NYZbb1ww4nauajqNTowk5+2TeUONcQAW/g=="],"Wechatpay-Signature-Type":["WECHATPAY2-SHA256-RSA2048"],"Wechatpay-Timestamp":["1774579207"],"X-Forwarded-For":["121.51.58.169"],"X-Real-Ip":["121.51.58.169"]}` + bodyBytes := []byte(`{"id":"8285f110-2fd9-5d05-9c0b-86a54be0a6ba","create_time":"2026-03-27T10:40:07+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","resource":{"original_type":"coupon","algorithm":"AEAD_AES_256_GCM","ciphertext":"+64vCJPvAAikNVJ8IPRNWawVz/8rZtzNzrtFSSTxZs0x3YY91wypH7RnTcYdXQo7TMe89eha5+c1Cf/u2It4P/tfVx5F3CkW1epDY9v0dII3nKNHW291Nv+dGrYUsLdnfBK/EydUNrsSpSrleKvHkA5bd0qF3asuZ9uHEWeeCPtKa5+YNJY56sIRsyzJbkgJcnBTlGoeuWeGJzcQ3YCJIkPXYk25QJkGbvJpOrCZNhUVUtJlkuR5IGxiPP/pR55fGG2+J6akuUp9oKXxeXDlHnPTTVLfj14I2WeEp7E/+l6vqHQiFwtN5M9AY7362IdqxS27v227NoW41si5RINCc8Fu8iIj2UfccGzcFLIBUDiHREa9ZJayUMpe6FzKrLCZxfjvw9Z36WBFR6zWrgsW9gR5gwrltFgdkekZqDqDBbjBJKPQZdbpALszmg7+VkFMkgSBdkxPnHGwzDrbbW5d+CddWR6Dl1N8kl0588b8lh4Vgf3UtXIcSEJ8PEIk5Bm7lm/PaiRBJrGWz/1DZvEtbArQwbKudgcV9aEPcMk3Nk6lsCszI2BO39E34HyW8KsrVbogv7D2FO8jNw9BWxUKfKukNf33HsNW1k+P7+Fi/ZOtr9JnaBW8cCruK8OwUPYyZGAJWEyGvez4HU3GB+TpyHZ1jgbthDZtTA9UmZ0BcqUFnBZeiO44RzFSdG8y5hbAa3rn4PJa7SBhAO+/iK6r48FzYwk/wsNkxi0Pq3AVECwbE5Iqpe1MY+qXUsDWrw6X+sag2XkjUwNFLsd1gwldMjA=","associated_data":"coupon","nonce":"8LSgP6Pee83S"}`) httpHeaders := make(http.Header) if err := json.Unmarshal([]byte(header), &httpHeaders); err != nil { From 11b42a9b46b4aec1d58226f6f77620cd0fa08db1 Mon Sep 17 00:00:00 2001 From: ziming Date: Fri, 27 Mar 2026 18:22:07 +0800 Subject: [PATCH 140/144] =?UTF-8?q?=E6=94=B9=E9=80=A0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/pkg/request/request_test.go | 11 +++++------ test/bank_multi_activity_test.go | 6 +++--- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/internal/pkg/request/request_test.go b/internal/pkg/request/request_test.go index 7394914..fd1c4f7 100644 --- a/internal/pkg/request/request_test.go +++ b/internal/pkg/request/request_test.go @@ -70,15 +70,14 @@ func Test_RequestStatusCode(t *testing.T) { func Test_WxNotifyRequest(t *testing.T) { - //uri := "https://gateway.dev.cdlsxd.cn/voucher/v1/notify/1100040695" - uri := "https://voucher.86698.cn/voucher/v1/notify/1100040695" + uri := "https://gateway.dev.cdlsxd.cn/voucher/v1/notify/1100040695" + //uri := "https://voucher.86698.cn/voucher/v1/notify/1100040695" - bodyBytes := []byte(`{"id":"0e92e862-66b0-55a3-b1da-0e54a820b923","create_time":"2026-03-27T14:20:12+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","resource":{"original_type":"coupon","algorithm":"AEAD_AES_256_GCM","ciphertext":"LFFqFfGIu1uHYbKwqxND29QS7sH54U1k9uj+W5ecKf0ZzBT8YzikcSqa6W0iIcpnlQE0AJkDxs/nfJQ/pcRJajwy+4JzE8fzrMmhn1vgvUiXt+EyvJ8mPJwfVMtg8YtMuQBUUPxIyc5ACZVawGDVlwG9nxFYCGF0jZAtzKdwZw2MMmGFTCJri6kCE60hic5Yn3b9iPaCwmXdFlVKA6NozMEp2uNoQYYzy1+OEAfyXHyspz0IQnhUFcVuxfnGbK4zJQYUZ8yGhg3YfNFTtDW0vRTBwkNeR8BWy/LXSKMZJ7sC+SaX7eASd0QHXA/UPWpItCQGRDeZHUXWEQw6yB3GpHfKRTlO5H7VINLVe9pj+XWnXMzDEqDShpUnCP3cgf7TBYykopXPKXfI/bekTxy6ZWbikQH1Ts9lc/AMqd3NK7H5AmQPNld6DkBkt1jgI/DP5tUJeyQyYX39fX0pJCFpy94AQhXyPXLKT2VQHmnX3qnaQyztgY7GfvavtmgTNFunKcnqXtgU65wpg156iTGhHyoG01msdoUMjynY7pbdUHM1O/ljsizysqiGBvYaDfhx4Mcf8Fd3kT5bbuelM2DLg7nycz9ZjPrsKWz47eNG2U3hixk6yVxrBpAsO4QM7/ngB1+5Z7Pv06nSoP7jnFxIMv9iwPn5Y+O5tb1JfO/2su7UTtDcY25iJBqiKJvwgtzy9IB1hR781PPNzHVDT9ORUfJVVlrkGgaOn7aMDyzv3DYYUkK0fTENAReCa7D/wrajM6U6Gu8Z1/TfKkJ9BdwbEqI=","associated_data":"coupon","nonce":"b2AW4NUIk2AX"}}`) - - header := []byte(`{"Accept":["*/*"],"Cache-Control":["no-cache"],"Connection":["close"],"Content-Length":["1109"],"Content-Type":["application/json"],"Pragma":["no-cache"],"User-Agent":["Mozilla/4.0"],"Wechatpay-Nonce":["OaVm4rYgmQnOzu9dra2OM9J560D5uuLh"],"Wechatpay-Serial":["PUB_KEY_ID_0111000406952026032500382251001000"],"Wechatpay-Signature":["QUnj2WwPfh3nAycuwQ/7zFVjhSHObnFfmfLnPx/4UXjgFlkY1REyCHrBHrJ1lDpUOodiY5qsGg/7CQ6aMlXZe24xsLOBjwOgZr0JGmsRisrwnQL+jy6ar8pAf1fABv+e/lCLO+UZtLXrv1GCQix51GLerkELEVTQkn6qzT4aVyL1Ukte6hk+r3HlWOQC6OM3cpxyr8XhVilGWaElRCgPwHI2XYLGjH6QWtcRnCSMESLFGOwI7t9GbxgGLptEelfmVpBHdRFTDWa8rWJI7b9ArI//vMawkJUE7tW5nkqF5sWMydKUQ5ZDLf5oI447zic/LOljZ4SA7I/aTN4MlpulDA=="],"Wechatpay-Signature-Type":["WECHATPAY2-SHA256-RSA2048"],"Wechatpay-Timestamp":["1774592412"],"X-Forwarded-For":["121.51.58.169"],"X-Real-Ip":["121.51.58.169"]}`) + headerBytes := []byte(`{"Accept":["*/*"],"Cache-Control":["no-cache"],"Connection":["close"],"Content-Length":["1137"],"Content-Type":["application/json"],"Pragma":["no-cache"],"User-Agent":["Mozilla/4.0"],"Wechatpay-Nonce":["kHzkvdHwssdU0CRpFfgCpzdxtdzQGsIS"],"Wechatpay-Serial":["PUB_KEY_ID_0111000406952026032500382251001000"],"Wechatpay-Signature":["jt/2zYvqTlvOHAb9Lb1bfbLUDnqy59dc1JF87AiHVtagZAxzWNP5Jgsrr/jv9C3UVv+MHvbTxuaDQjJAfXx4CT7ihYUNEF6rQL/ilToSMuZpw23/pPjyAzXvBWBsj3AY3rxfa4OkaviRnG6vRA5HKnaHHG5wmDdrcwOoKBiLJ6cax8OYu9GV8Opr0uSzWj7ZxPoSxXy65MxEaampVXJcLnCm1iVp2mHZH6jafBxyjhDGIZ6uOJD0LdCUCJMfbDKvlthO7CrfLRsdosVrVmnL3lJU2ti5rjngmzAxHFi+J4JUsbTvkWnBEXZXaXZ4vNi4gkGnOGPJHs4ch+s0MV/ZFg=="],"Wechatpay-Signature-Type":["WECHATPAY2-SHA256-RSA2048"],"Wechatpay-Timestamp":["1774605950"],"X-Forwarded-For":["121.51.58.172, 172.17.0.1"]}`) + bodyBytes := []byte(`{"id":"4a5e22d9-9018-5bfc-94ba-3c9957a62355","create_time":"2026-03-27T18:05:50+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","resource":{"original_type":"coupon","algorithm":"AEAD_AES_256_GCM","ciphertext":"aTS2kVk7l/lzfIEUMUVfk5+6ouRSakspFTP/nR4sDPp0nvrj2VtEQBXkYqTgDDxs35V9CrJI90X31Mho2+adZm1ScPPH0HL6iUaUcypzgvHAhSaJBhugz8slqoQ3zaynBKa/HpyU6Jnfd3OTbhcrS2i27cBdWawd2UQ5HBgAemV8/k/gf+LPC04fJNFfrrttTxqtBDpXr3H/ob5pV825C57SJJhpXxgZtnX/e9avpWZlPIXhOSA3FSwYZtoW1BYoXrsLID6fGcP5IJuc8EE40Z7si95tQbQcM1eqH19OZbRAmXitV+sY1pOlo0Zehnc5vyH5RunVYa2lwdsirSGU1EI0PlDYwHoNxUEtSpKZ4MP8IReNdkjKlwpBQXtgZMGqMNdDU+/Db/ZUr/R8xaF7RoHfiiTHEHQaEK9xbCZji+F8YDiU0K8I8getKsyHiZwHZrU67p/6ql6zVzcWBjaDyL71tcl1k1ppkyOFdg9g0WqcJD56xa9qMhXaOyQoIU4hJsMMTvBWxyRelE4+o1Z79nUVUMswvg/hPZ4QEeIF8C+ezzk+/PhggYhZE4g4WnzqKRh/WASXRja0UsucBVIs9hViV+aDuQVEjxmy29S3UTrwL0Kr0+5hUz7q87gJUaAvehF6pIPtvliJvR0tnpdCrLHuIsc/LiSxoX2WSkGGZet58swdJ7wHYzdfNFKbhRS2IBj4JzI6wc+zRkGmLCijTRFSadZGnNZf5SPez1iEqr0fecCDjN6NUMv/YETfjeEqUxRvNVjMK9vCzEm+bdVJNNL+3i5RiJA9Bng1JtmTMsHatgn8dg==","associated_data":"coupon","nonce":"L3TIxaSQ12pD"}}`) var headerMap http.Header - if err := json.Unmarshal(header, &headerMap); err != nil { + if err := json.Unmarshal(headerBytes, &headerMap); err != nil { t.Error(fmt.Sprintf("解析 headers 失败: %v", err)) return } diff --git a/test/bank_multi_activity_test.go b/test/bank_multi_activity_test.go index 880e641..5e3fb47 100644 --- a/test/bank_multi_activity_test.go +++ b/test/bank_multi_activity_test.go @@ -117,11 +117,11 @@ func Test_QixingNotifyData(t *testing.T) { func Test_MarketingNotify(t *testing.T) { - header := `{"Accept":["*/*"],"Cache-Control":["no-cache"],"Connection":["close"],"Content-Length":["1109"],"Content-Type":["application/json"],"Pragma":["no-cache"],"User-Agent":["Mozilla/4.0"],"Wechatpay-Nonce":["9OdaHVZ5OW75Wq0m1oQydpGCb4ONM2Wl"],"Wechatpay-Serial":["PUB_KEY_ID_0111000406952026032500382251001000"],"Wechatpay-Signature":["RtBgwpQebjUFhasnP3816IA0KoekMIYxqOnK79/EElILbDOpIDD1yE2ckpQ8Vb+/JJm15n5iHSQPL2MQ0xI4fDb4doaRiytIZxvr8EhxLGVmtjKmHrerTEIGf0wSoYofeE+RT4n25l06EAPNGVBmr4bd6VIhV7kz0Upmmlld4DhuDomAAjOxETd+Uy8ck2T9Y8kn31LF320DMRXrub7UG/n1PtKMI9XTHo+TRMBwVUvaWMchSVwUoqVzRejg0ogjCNE8YcxkEqx7hbvxiLPFEthGnSlZdg/0iHkCduxsiQSLo30xBTm1NYZbb1ww4nauajqNTowk5+2TeUONcQAW/g=="],"Wechatpay-Signature-Type":["WECHATPAY2-SHA256-RSA2048"],"Wechatpay-Timestamp":["1774579207"],"X-Forwarded-For":["121.51.58.169"],"X-Real-Ip":["121.51.58.169"]}` - bodyBytes := []byte(`{"id":"8285f110-2fd9-5d05-9c0b-86a54be0a6ba","create_time":"2026-03-27T10:40:07+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","resource":{"original_type":"coupon","algorithm":"AEAD_AES_256_GCM","ciphertext":"+64vCJPvAAikNVJ8IPRNWawVz/8rZtzNzrtFSSTxZs0x3YY91wypH7RnTcYdXQo7TMe89eha5+c1Cf/u2It4P/tfVx5F3CkW1epDY9v0dII3nKNHW291Nv+dGrYUsLdnfBK/EydUNrsSpSrleKvHkA5bd0qF3asuZ9uHEWeeCPtKa5+YNJY56sIRsyzJbkgJcnBTlGoeuWeGJzcQ3YCJIkPXYk25QJkGbvJpOrCZNhUVUtJlkuR5IGxiPP/pR55fGG2+J6akuUp9oKXxeXDlHnPTTVLfj14I2WeEp7E/+l6vqHQiFwtN5M9AY7362IdqxS27v227NoW41si5RINCc8Fu8iIj2UfccGzcFLIBUDiHREa9ZJayUMpe6FzKrLCZxfjvw9Z36WBFR6zWrgsW9gR5gwrltFgdkekZqDqDBbjBJKPQZdbpALszmg7+VkFMkgSBdkxPnHGwzDrbbW5d+CddWR6Dl1N8kl0588b8lh4Vgf3UtXIcSEJ8PEIk5Bm7lm/PaiRBJrGWz/1DZvEtbArQwbKudgcV9aEPcMk3Nk6lsCszI2BO39E34HyW8KsrVbogv7D2FO8jNw9BWxUKfKukNf33HsNW1k+P7+Fi/ZOtr9JnaBW8cCruK8OwUPYyZGAJWEyGvez4HU3GB+TpyHZ1jgbthDZtTA9UmZ0BcqUFnBZeiO44RzFSdG8y5hbAa3rn4PJa7SBhAO+/iK6r48FzYwk/wsNkxi0Pq3AVECwbE5Iqpe1MY+qXUsDWrw6X+sag2XkjUwNFLsd1gwldMjA=","associated_data":"coupon","nonce":"8LSgP6Pee83S"}`) + headerBytes := []byte(`{"Accept":["*/*"],"Cache-Control":["no-cache"],"Connection":["close"],"Content-Length":["1137"],"Content-Type":["application/json"],"Pragma":["no-cache"],"User-Agent":["Mozilla/4.0"],"Wechatpay-Nonce":["kHzkvdHwssdU0CRpFfgCpzdxtdzQGsIS"],"Wechatpay-Serial":["PUB_KEY_ID_0111000406952026032500382251001000"],"Wechatpay-Signature":["jt/2zYvqTlvOHAb9Lb1bfbLUDnqy59dc1JF87AiHVtagZAxzWNP5Jgsrr/jv9C3UVv+MHvbTxuaDQjJAfXx4CT7ihYUNEF6rQL/ilToSMuZpw23/pPjyAzXvBWBsj3AY3rxfa4OkaviRnG6vRA5HKnaHHG5wmDdrcwOoKBiLJ6cax8OYu9GV8Opr0uSzWj7ZxPoSxXy65MxEaampVXJcLnCm1iVp2mHZH6jafBxyjhDGIZ6uOJD0LdCUCJMfbDKvlthO7CrfLRsdosVrVmnL3lJU2ti5rjngmzAxHFi+J4JUsbTvkWnBEXZXaXZ4vNi4gkGnOGPJHs4ch+s0MV/ZFg=="],"Wechatpay-Signature-Type":["WECHATPAY2-SHA256-RSA2048"],"Wechatpay-Timestamp":["1774605950"],"X-Forwarded-For":["121.51.58.172, 172.17.0.1"]}`) + bodyBytes := []byte(`{"id":"4a5e22d9-9018-5bfc-94ba-3c9957a62355","create_time":"2026-03-27T18:05:50+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","resource":{"original_type":"coupon","algorithm":"AEAD_AES_256_GCM","ciphertext":"aTS2kVk7l/lzfIEUMUVfk5+6ouRSakspFTP/nR4sDPp0nvrj2VtEQBXkYqTgDDxs35V9CrJI90X31Mho2+adZm1ScPPH0HL6iUaUcypzgvHAhSaJBhugz8slqoQ3zaynBKa/HpyU6Jnfd3OTbhcrS2i27cBdWawd2UQ5HBgAemV8/k/gf+LPC04fJNFfrrttTxqtBDpXr3H/ob5pV825C57SJJhpXxgZtnX/e9avpWZlPIXhOSA3FSwYZtoW1BYoXrsLID6fGcP5IJuc8EE40Z7si95tQbQcM1eqH19OZbRAmXitV+sY1pOlo0Zehnc5vyH5RunVYa2lwdsirSGU1EI0PlDYwHoNxUEtSpKZ4MP8IReNdkjKlwpBQXtgZMGqMNdDU+/Db/ZUr/R8xaF7RoHfiiTHEHQaEK9xbCZji+F8YDiU0K8I8getKsyHiZwHZrU67p/6ql6zVzcWBjaDyL71tcl1k1ppkyOFdg9g0WqcJD56xa9qMhXaOyQoIU4hJsMMTvBWxyRelE4+o1Z79nUVUMswvg/hPZ4QEeIF8C+ezzk+/PhggYhZE4g4WnzqKRh/WASXRja0UsucBVIs9hViV+aDuQVEjxmy29S3UTrwL0Kr0+5hUz7q87gJUaAvehF6pIPtvliJvR0tnpdCrLHuIsc/LiSxoX2WSkGGZet58swdJ7wHYzdfNFKbhRS2IBj4JzI6wc+zRkGmLCijTRFSadZGnNZf5SPez1iEqr0fecCDjN6NUMv/YETfjeEqUxRvNVjMK9vCzEm+bdVJNNL+3i5RiJA9Bng1JtmTMsHatgn8dg==","associated_data":"coupon","nonce":"L3TIxaSQ12pD"}}`) httpHeaders := make(http.Header) - if err := json.Unmarshal([]byte(header), &httpHeaders); err != nil { + if err := json.Unmarshal(headerBytes, &httpHeaders); err != nil { fmt.Printf("headers Unmarshal err: %+v\n", err) return } From edae25ff6a0f3ea1f6a5d16c966c5011bd050c85 Mon Sep 17 00:00:00 2001 From: ziming Date: Thu, 2 Apr 2026 09:05:44 +0800 Subject: [PATCH 141/144] =?UTF-8?q?=E6=94=B9=E9=80=A0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/pkg/request/request_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/pkg/request/request_test.go b/internal/pkg/request/request_test.go index fd1c4f7..fa07f27 100644 --- a/internal/pkg/request/request_test.go +++ b/internal/pkg/request/request_test.go @@ -73,8 +73,8 @@ func Test_WxNotifyRequest(t *testing.T) { uri := "https://gateway.dev.cdlsxd.cn/voucher/v1/notify/1100040695" //uri := "https://voucher.86698.cn/voucher/v1/notify/1100040695" - headerBytes := []byte(`{"Accept":["*/*"],"Cache-Control":["no-cache"],"Connection":["close"],"Content-Length":["1137"],"Content-Type":["application/json"],"Pragma":["no-cache"],"User-Agent":["Mozilla/4.0"],"Wechatpay-Nonce":["kHzkvdHwssdU0CRpFfgCpzdxtdzQGsIS"],"Wechatpay-Serial":["PUB_KEY_ID_0111000406952026032500382251001000"],"Wechatpay-Signature":["jt/2zYvqTlvOHAb9Lb1bfbLUDnqy59dc1JF87AiHVtagZAxzWNP5Jgsrr/jv9C3UVv+MHvbTxuaDQjJAfXx4CT7ihYUNEF6rQL/ilToSMuZpw23/pPjyAzXvBWBsj3AY3rxfa4OkaviRnG6vRA5HKnaHHG5wmDdrcwOoKBiLJ6cax8OYu9GV8Opr0uSzWj7ZxPoSxXy65MxEaampVXJcLnCm1iVp2mHZH6jafBxyjhDGIZ6uOJD0LdCUCJMfbDKvlthO7CrfLRsdosVrVmnL3lJU2ti5rjngmzAxHFi+J4JUsbTvkWnBEXZXaXZ4vNi4gkGnOGPJHs4ch+s0MV/ZFg=="],"Wechatpay-Signature-Type":["WECHATPAY2-SHA256-RSA2048"],"Wechatpay-Timestamp":["1774605950"],"X-Forwarded-For":["121.51.58.172, 172.17.0.1"]}`) - bodyBytes := []byte(`{"id":"4a5e22d9-9018-5bfc-94ba-3c9957a62355","create_time":"2026-03-27T18:05:50+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","resource":{"original_type":"coupon","algorithm":"AEAD_AES_256_GCM","ciphertext":"aTS2kVk7l/lzfIEUMUVfk5+6ouRSakspFTP/nR4sDPp0nvrj2VtEQBXkYqTgDDxs35V9CrJI90X31Mho2+adZm1ScPPH0HL6iUaUcypzgvHAhSaJBhugz8slqoQ3zaynBKa/HpyU6Jnfd3OTbhcrS2i27cBdWawd2UQ5HBgAemV8/k/gf+LPC04fJNFfrrttTxqtBDpXr3H/ob5pV825C57SJJhpXxgZtnX/e9avpWZlPIXhOSA3FSwYZtoW1BYoXrsLID6fGcP5IJuc8EE40Z7si95tQbQcM1eqH19OZbRAmXitV+sY1pOlo0Zehnc5vyH5RunVYa2lwdsirSGU1EI0PlDYwHoNxUEtSpKZ4MP8IReNdkjKlwpBQXtgZMGqMNdDU+/Db/ZUr/R8xaF7RoHfiiTHEHQaEK9xbCZji+F8YDiU0K8I8getKsyHiZwHZrU67p/6ql6zVzcWBjaDyL71tcl1k1ppkyOFdg9g0WqcJD56xa9qMhXaOyQoIU4hJsMMTvBWxyRelE4+o1Z79nUVUMswvg/hPZ4QEeIF8C+ezzk+/PhggYhZE4g4WnzqKRh/WASXRja0UsucBVIs9hViV+aDuQVEjxmy29S3UTrwL0Kr0+5hUz7q87gJUaAvehF6pIPtvliJvR0tnpdCrLHuIsc/LiSxoX2WSkGGZet58swdJ7wHYzdfNFKbhRS2IBj4JzI6wc+zRkGmLCijTRFSadZGnNZf5SPez1iEqr0fecCDjN6NUMv/YETfjeEqUxRvNVjMK9vCzEm+bdVJNNL+3i5RiJA9Bng1JtmTMsHatgn8dg==","associated_data":"coupon","nonce":"L3TIxaSQ12pD"}}`) + headerBytes := []byte(`{"Accept":["*/*"],"Cache-Control":["no-cache"],"Connection":["close"],"Content-Length":["1113"],"Content-Type":["application/json"],"Pragma":["no-cache"],"User-Agent":["Mozilla/4.0"],"Wechatpay-Nonce":["TD238pwsm3x0rYo57A8Z27Qa3AFDmToI"],"Wechatpay-Serial":["PUB_KEY_ID_0111000406952026032500382251001000"],"Wechatpay-Signature":["a64Z4Lngoz61OhhpG82g/rv8tYV0KmnE7g5KA4hvj7bY6XKSdQaifajXnLQHxA70owmd1kGA6wY5ZxDCvxnNHRjiNP4f68Ii7swL0TCfcU3aLpkLlmPM4wtzSh4YK1cIg4z/mqpxLDkxUky2vp4BvmGx6oKjK5Yv/emFerS2rWWdM2tTdi5Rv1rAGz/osqmrW8d+ZsLlGKHZ6wh7GUmNqt/qF+/kx+GB1jpzCgn8aUQqNvfnbtFcs9SKMQqm+DmBUO85IHL2WCQsuQ6cWpTrRCg5TGj0M0b8TEVHLbX0eBxtUyyGtdwuQbVbEzqCbwLs234YLG1Koq8lxZx6Nib1OQ=="],"Wechatpay-Signature-Type":["WECHATPAY2-SHA256-RSA2048"],"Wechatpay-Timestamp":["1774864935"],"X-Forwarded-For":["121.51.58.174, 172.29.42.89"]}`) + bodyBytes := []byte(`{"id":"ab4dbb6c-d9e9-5d2f-9f22-9697dfe2a58e","create_time":"2026-03-30T18:02:15+08:00","resource_type":"encrypt-resource","event_type":"COUPON.USE","summary":"代金券核销通知","resource":{"original_type":"coupon","algorithm":"AEAD_AES_256_GCM","ciphertext":"lsBBeh/DxRiecPLgtFyZov6tsCA38vWQ4QZxZouyp48mr3/kP3jSu/l2Sl1Rr59a4F0iIFSxuc7p6+/ovX11Ol7i+gbJmNlc+F3Yfo/bb8zyt/ubys4Ep0Or3YEvZr+h4G4k8rPn0iTYlzuY2hBcfLAazt/qTSz87aNmpBz3XN1CJL+5C0LffWdd5j1zjxfY2AYiUwtSuzo2cRYrxberxyeL9SeHJnqc+8o0njB6fyOJ4q5D9SyPqSNe228tW2xw0/d9QNWtzIlwQhyCwuhdXh0GBSWO7jdEmD3tQCIOk3OgoooywBZFtfwUvMiy2/tohEKd9JIQi3OVV2ctUx+cBdJ7RIH2r8PFZO55PqIND9itDCraSbfHQpqlYfVLZMossa92U54khv9FOzy/RQJPut/YWFkC4PsXgtfPpXQI0l6M+if1AVX+h4XHKk+wCd3fQ37tAwpxNF9kCuLgCIzgmnWjxj8296z6lQkKFomDn6xLdv6ECtnNYGhItQqU6v122/klkeysURSn1qFSbkujFvByCEKSmasTHe1yLorvfAYl+YJVnvLKsBxjjlCiEdCjspTFlDG3m+N/DxsYpYegYxvzfrM2MhgjJwPBbuxxgz4KMtYyiOrHvMDg8qCA41fRkxO4JkSA0JP22Q7v6zXOcHgf/w5aNo6xyICUrg2AueY0UV2uDA4yF6hYTho+CfGLjRfdFCuDReBPlA2TcJKSEtAWJW25nFrqKY/C87xAiD99JPf+tg7h2HcDCzasnV8h/AGA3PSQf2ZX4TsuyRdyPQ+uQWSW","associated_data":"coupon","nonce":"c6H0W8tKIiJP"}}`) var headerMap http.Header if err := json.Unmarshal(headerBytes, &headerMap); err != nil { From 5e6d810773571ab40ca09d509b9d22e7a7b5f50f Mon Sep 17 00:00:00 2001 From: ziming Date: Tue, 7 Apr 2026 13:46:03 +0800 Subject: [PATCH 142/144] =?UTF-8?q?=E6=94=B9=E9=80=A0=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 2e4535a..1bef7ab 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -79,8 +79,8 @@ func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.Wech return err } - if order.ActivityId != "" { - return fmt.Errorf("批次活动ID不为空,不是多笔立减金,请检查") + if order.ActivityId == "" { + return fmt.Errorf("批次活动ID为空,不是多笔立减金,请检查") } if err = biz.Run(ctx, ip, source, req, order); err != nil { From 2e3dd99115161df037fba705fb1ba8eb28e66853 Mon Sep 17 00:00:00 2001 From: fuzhongyun <15339891972@163.com> Date: Mon, 27 Apr 2026 18:23:09 +0800 Subject: [PATCH 143/144] =?UTF-8?q?fix=EF=BC=88notify=EF=BC=89:=20?= =?UTF-8?q?=E4=B8=B4=E6=97=B6=E4=BF=AE=E5=A4=8D=20multi=20=E5=9B=9E?= =?UTF-8?q?=E8=B0=83=E6=97=B6=E6=A0=B8=E9=94=80=E9=87=91=E9=A2=9D=E4=B8=BA?= =?UTF-8?q?=E7=A9=BA=E5=AF=BC=E8=87=B4=E7=9A=84=E6=8E=A8=E9=80=81=E4=B8=8B?= =?UTF-8?q?=E6=B8=B8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/biz/multi.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/internal/biz/multi.go b/internal/biz/multi.go index 1bef7ab..a6f4983 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -5,8 +5,6 @@ import ( "encoding/json" "errors" "fmt" - "github.com/go-kratos/kratos/v2/log" - "gorm.io/gorm" v1 "voucher/api/v1" "voucher/internal/biz/bo" "voucher/internal/biz/cmb" @@ -16,6 +14,9 @@ import ( "voucher/internal/conf" "voucher/internal/data" "voucher/internal/pkg/lock" + + "github.com/go-kratos/kratos/v2/log" + "gorm.io/gorm" ) type MultiBiz struct { @@ -149,6 +150,11 @@ func (biz *MultiBiz) RetryRunByMultiNotifyDataId(ctx context.Context, multiNotif } func (biz *MultiBiz) run(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) error { + // 如果核销金额为空,不再推送下游 + if mnd.ConsumeAmount == 0 { + log.Warnf("[%s] multi notify log consume amount is 0,req:%+v", mnd.NotifyID, req) + return nil + } nl, request, err := biz.nlCreate(ctx, req, mnd, order) if err != nil { From 5e4b072062ceae493f349645d2c2dc5885d824fc Mon Sep 17 00:00:00 2001 From: fuzhongyun <15339891972@163.com> Date: Thu, 28 May 2026 16:27:05 +0800 Subject: [PATCH 144/144] =?UTF-8?q?feat(multi-notify):=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E5=90=88=E5=8D=95=E6=94=AF=E4=BB=98=E5=9B=9E=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 2 + internal/biz/bo/wechat_notify_bo.go | 51 ++++++++++ internal/biz/multi.go | 105 +++++++++++++++++++-- internal/biz/repo/multi_notify_log.go | 1 + internal/biz/wechat_notify.go | 2 +- internal/data/repoimpl/multi_notify_log.go | 22 ++++- 6 files changed, 172 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index b3a0095..2c999f1 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,8 @@ Thumbs.db *.swp .vscode/ .idea/ +.trae/ + bin/ cert/ log diff --git a/internal/biz/bo/wechat_notify_bo.go b/internal/biz/bo/wechat_notify_bo.go index 26c4007..d8c35ae 100644 --- a/internal/biz/bo/wechat_notify_bo.go +++ b/internal/biz/bo/wechat_notify_bo.go @@ -16,6 +16,19 @@ type ConsumeInformation struct { ConsumeAmount int `json:"consume_amount"` // 核销金额(单位:分) // 多笔立减金必须 validate:"required" } +// CombineSubOrder 定义合单子单消费信息结构体 +type CombineSubOrder struct { + TransactionID string `json:"transaction_id" validate:"required"` // 合单子单微信支付订单号 + ConsumeAmount int `json:"comsume_amount" validate:"required"` // 子单核销金额,微信文档字段名如此定义 + ConsumeTime time.Time `json:"consume_time" validate:"required"` // 子单核销时间 +} + +// CombineOrderInfo 定义合单订单信息结构体 +type CombineOrderInfo struct { + CombineConsumeAmount int `json:"combine_consume_amount"` // 合单总核销金额 + SubOrders []CombineSubOrder `json:"sub_orders,omitempty"` // 合单子单列表 +} + // PlainText 定义明文数据结构体 type PlainText struct { StockCreatorMchid string `json:"stock_creator_mchid" validate:"required"` @@ -29,7 +42,9 @@ type PlainText struct { NoCash bool `json:"no_cash"` Singleitem bool `json:"singleitem"` BusinessType string `json:"business_type"` // 业务类型 + IsCombineOrder bool `json:"is_combine_order,omitempty"` ConsumeInformation *ConsumeInformation `json:"consume_information,omitempty"` + CombineOrderInfo *CombineOrderInfo `json:"combine_order_info,omitempty"` } type WechatVoucherNotifyBo struct { @@ -63,3 +78,39 @@ func (c *WechatVoucherNotifyBo) Validate() error { return nil } + +func (c *WechatVoucherNotifyBo) ValidateMultiNotify() error { + if err := c.Validate(); err != nil { + return err + } + + if c.PlainText.IsCombineOrder { + if c.PlainText.CombineOrderInfo == nil { + return fmt.Errorf("合单订单信息不能为空") + } + if len(c.PlainText.CombineOrderInfo.SubOrders) == 0 { + return fmt.Errorf("合单子单不能为空") + } + for _, subOrder := range c.PlainText.CombineOrderInfo.SubOrders { + if subOrder.TransactionID == "" { + return fmt.Errorf("合单子单微信支付订单号不能为空") + } + if subOrder.ConsumeAmount <= 0 { + return fmt.Errorf("合单子单核销金额必须大于0") + } + if subOrder.ConsumeTime.IsZero() { + return fmt.Errorf("合单子单核销时间不能为空") + } + } + return nil + } + + if c.PlainText.ConsumeInformation == nil { + return fmt.Errorf("消费信息不能为空") + } + if c.PlainText.ConsumeInformation.ConsumeAmount <= 0 { + return fmt.Errorf("消费金额必须大于0") + } + + return nil +} diff --git a/internal/biz/multi.go b/internal/biz/multi.go index a6f4983..598a92c 100644 --- a/internal/biz/multi.go +++ b/internal/biz/multi.go @@ -71,8 +71,8 @@ func (biz *MultiBiz) Notify(ctx context.Context, ip, source string, req *bo.Wech return lock.NewMutex(biz.rdb.Rdb, cl.TTL).Lock(ctx, cl.Key, func(ctx context.Context) error { - if req.PlainText.ConsumeInformation.ConsumeAmount == 0 { - return fmt.Errorf("消费金额不能为0") + if err = req.ValidateMultiNotify(); err != nil { + return fmt.Errorf("multi validate req error: %v", err) } order, err := biz.order(ctx, req) @@ -115,7 +115,7 @@ func (biz *MultiBiz) Run(ctx context.Context, ip, source string, req *bo.WechatV } if mnd != nil { - if mnd.NoticeNum > 0 { + if !req.PlainText.IsCombineOrder && mnd.NoticeNum > 0 { log.Warnf("[%s] multi notify log already exists,req:%+v", source, req) return nil } @@ -150,6 +150,14 @@ func (biz *MultiBiz) RetryRunByMultiNotifyDataId(ctx context.Context, multiNotif } func (biz *MultiBiz) run(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) error { + if req.PlainText.IsCombineOrder { + return biz.runCombine(ctx, req, mnd, order) + } + + return biz.runSingle(ctx, req, mnd, order) +} + +func (biz *MultiBiz) runSingle(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) error { // 如果核销金额为空,不再推送下游 if mnd.ConsumeAmount == 0 { log.Warnf("[%s] multi notify log consume amount is 0,req:%+v", mnd.NotifyID, req) @@ -165,20 +173,50 @@ func (biz *MultiBiz) run(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd return fmt.Errorf("请求错误 error: %v", err) } + return biz.updateOrderStatus(ctx, req, order) +} + +func (biz *MultiBiz) runCombine(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) error { + for _, subOrder := range req.PlainText.CombineOrderInfo.SubOrders { + exists, err := biz.MultiNotifyLogRepo.ExistsSuccessByDataIDAndTransactionID(ctx, mnd.ID, subOrder.TransactionID) + if err != nil { + return fmt.Errorf("查询合单子单通知记录错误 error: %v", err) + } + if exists { + log.Warnf("[%s] combine sub order already notified,transaction_id:%s", mnd.NotifyID, subOrder.TransactionID) + continue + } + + nl, request, err := biz.nlCreateBySubOrder(ctx, req, mnd, order, subOrder) + if err != nil { + return fmt.Errorf("创建合单子单通知日志错误 error: %v", err) + } + + if err = biz.Request(ctx, mnd, nl, request); err != nil { + return fmt.Errorf("合单子单请求错误 error: %v", err) + } + } + + return biz.updateOrderStatus(ctx, req, order) +} + +func (biz *MultiBiz) updateOrderStatus(ctx context.Context, req *bo.WechatVoucherNotifyBo, order *bo.OrderBo) error { + consumeTime := req.PlainText.ConsumeInformation.ConsumeTime + if req.PlainText.Status.IsUsed() { if order.Status.IsUse() { - if err = biz.OrderRepo.MultiOverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime, "再次核销完成"); err != nil { + if err := biz.OrderRepo.MultiOverUsed(ctx, order.ID, consumeTime, "再次核销完成"); err != nil { return fmt.Errorf("订单再次核销完成修改发生错误 error: %v", err) } } else { - if err = biz.OrderRepo.MultiOverUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime, "核销完成"); err != nil { + if err := biz.OrderRepo.MultiOverUsed(ctx, order.ID, consumeTime, "核销完成"); err != nil { return fmt.Errorf("订单核销完成修改发生错误 error: %v", err) } } } else { - if err = biz.OrderRepo.MultiLastUsed(ctx, order.ID, req.PlainText.ConsumeInformation.ConsumeTime); err != nil { + if err := biz.OrderRepo.MultiLastUsed(ctx, order.ID, consumeTime); err != nil { return fmt.Errorf("订单核销修改发生错误 error: %v", err) } } @@ -193,6 +231,13 @@ func (biz *MultiBiz) mndCreate(ctx context.Context, ip, source string, req *bo.W return nil, fmt.Errorf("通知数据 json str 错误 error: %v", err) } + consumeAmount := int32(req.PlainText.ConsumeInformation.ConsumeAmount) + consumeTime := &req.PlainText.ConsumeInformation.ConsumeTime + transactionID := req.PlainText.ConsumeInformation.TransactionID + if req.PlainText.IsCombineOrder { + consumeAmount = int32(req.PlainText.CombineOrderInfo.CombineConsumeAmount) + } + return biz.MultiNotifyDataRepo.Create(ctx, &bo.MultiNotifyDataBo{ Source: source, IP: ip, @@ -201,15 +246,57 @@ func (biz *MultiBiz) mndCreate(ctx context.Context, ip, source string, req *bo.W OutBizNo: order.OutBizNo, CouponID: req.PlainText.CouponID, StockID: req.PlainText.StockID, - ConsumeAmount: int32(req.PlainText.ConsumeInformation.ConsumeAmount), - ConsumeTime: &req.PlainText.ConsumeInformation.ConsumeTime, - TransactionID: req.PlainText.ConsumeInformation.TransactionID, + ConsumeAmount: consumeAmount, + ConsumeTime: consumeTime, + TransactionID: transactionID, EventType: req.EventType, Status: req.PlainText.Status, OriginalData: originalData, }) } +func (biz *MultiBiz) nlCreateBySubOrder(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo, subOrder bo.CombineSubOrder) (*bo.MultiNotifyLogBo, *v1.CmbRequest, error) { + if biz.bc.Cmb.MultiNotifyUrl == "" { + return nil, nil, fmt.Errorf("CMB多笔立减金通知地址为空") + } + + consumeTime := subOrder.ConsumeTime + nl := &bo.MultiNotifyLogBo{ + MultiNotifyDataID: mnd.ID, + OrderNo: mnd.OrderNo, + OutBizNo: mnd.OutBizNo, + CouponID: mnd.CouponID, + ActivityNo: order.ProductNo, + StockID: mnd.StockID, + EventType: mnd.EventType, + Status: req.PlainText.Status, + ConsumeAmount: int32(subOrder.ConsumeAmount), + ConsumeTime: &consumeTime, + TransactionID: subOrder.TransactionID, + RequestURL: biz.bc.Cmb.MultiNotifyUrl, + RequestStatus: vo.MultiNotifyLogStatusWait.GetValue(), + OrderCreateTime: order.CreateTime, + CouponCreateTime: &req.PlainText.CreateTime, + } + + request, cmbRequestBo, err := biz.GetRequest(ctx, nl, order) + if err != nil { + return nil, nil, err + } + + b, _ := json.Marshal(request) + nl.OriginReq = cmbRequestBo.BizContent + nl.Request = string(b) + + res, err := biz.MultiNotifyLogRepo.Create(ctx, nl) + if err != nil { + return nil, nil, fmt.Errorf("创建通知日志错误 error: %v", err) + } + res.ConsumeTime = nl.ConsumeTime + + return res, request, nil +} + func (biz *MultiBiz) nlCreate(ctx context.Context, req *bo.WechatVoucherNotifyBo, mnd *bo.MultiNotifyDataBo, order *bo.OrderBo) (*bo.MultiNotifyLogBo, *v1.CmbRequest, error) { if biz.bc.Cmb.MultiNotifyUrl == "" { diff --git a/internal/biz/repo/multi_notify_log.go b/internal/biz/repo/multi_notify_log.go index a43f049..470d38e 100644 --- a/internal/biz/repo/multi_notify_log.go +++ b/internal/biz/repo/multi_notify_log.go @@ -8,6 +8,7 @@ import ( type MultiNotifyLogRepo interface { Create(ctx context.Context, req *bo.MultiNotifyLogBo) (*bo.MultiNotifyLogBo, error) GetByID(ctx context.Context, id int64) (*bo.MultiNotifyLogBo, error) + ExistsSuccessByDataIDAndTransactionID(ctx context.Context, multiNotifyDataID int64, transactionID string) (bool, error) Success(ctx context.Context, id int64, response string) error Fail(ctx context.Context, id int64, remark string) error } diff --git a/internal/biz/wechat_notify.go b/internal/biz/wechat_notify.go index 0c3206c..9de75dc 100644 --- a/internal/biz/wechat_notify.go +++ b/internal/biz/wechat_notify.go @@ -34,7 +34,7 @@ func (this *VoucherBiz) WechatNotifyConsumer(ctx context.Context, ip string, req } if order.ActivityId != "" { - if err = req.Validate(); err != nil { + if err = req.ValidateMultiNotify(); err != nil { return fmt.Errorf("multi validate req error: %v", err) } return this.MultiBiz.Run(ctx, ip, req.PlainText.StockCreatorMchid, req, order) diff --git a/internal/data/repoimpl/multi_notify_log.go b/internal/data/repoimpl/multi_notify_log.go index d1f4cec..ba12dca 100644 --- a/internal/data/repoimpl/multi_notify_log.go +++ b/internal/data/repoimpl/multi_notify_log.go @@ -2,8 +2,8 @@ package repoimpl import ( "context" + "errors" "fmt" - "gorm.io/gorm" "time" "unicode/utf8" err2 "voucher/api/err" @@ -12,6 +12,8 @@ import ( "voucher/internal/biz/vo" "voucher/internal/data" "voucher/internal/data/model" + + "gorm.io/gorm" ) // MultiNotifyLogRepoImpl . @@ -77,6 +79,24 @@ func (p *MultiNotifyLogRepoImpl) GetByID(ctx context.Context, id int64) (*bo.Mul return p.ToBo(&item), nil } +func (p *MultiNotifyLogRepoImpl) ExistsSuccessByDataIDAndTransactionID(ctx context.Context, multiNotifyDataID int64, transactionID string) (bool, error) { + var item model.MultiNotifyLog + + err := p.DB(ctx). + Select("id"). + Where("multi_notify_data_id = ? AND transaction_id = ? AND request_status = ?", multiNotifyDataID, transactionID, vo.MultiNotifyLogStatusSuccess.GetValue()). + Limit(1). + Take(&item).Error + if err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return false, nil + } + return false, err + } + + return true, nil +} + func (p *MultiNotifyLogRepoImpl) Success(ctx context.Context, id int64, response string) error { now := time.Now()