diff --git a/pkg/wecom/message/client.go b/pkg/wecom/message/client.go new file mode 100644 index 0000000..8741f76 --- /dev/null +++ b/pkg/wecom/message/client.go @@ -0,0 +1,13 @@ +package message + +import "lone-services/pkg/wecom/auth" + +type Client struct { + auth *auth.Client +} + +func NewClient(authClient *auth.Client) *Client { + return &Client{ + auth: authClient, + } +} diff --git a/pkg/wecom/message/common.go b/pkg/wecom/message/common.go new file mode 100644 index 0000000..25acc5f --- /dev/null +++ b/pkg/wecom/message/common.go @@ -0,0 +1,76 @@ +package message + +type TemplateSource struct { + IconURL string `json:"icon_url,omitempty"` + Desc string `json:"desc,omitempty"` + DescColor int `json:"desc_color,omitempty"` +} + +type ActionMenu struct { + Desc string `json:"desc,omitempty"` + ActionList []ActionItem `json:"action_list,omitempty"` +} + +type ActionItem struct { + Text string `json:"text,omitempty"` + Key string `json:"key,omitempty"` +} + +type MainTitle struct { + Title string `json:"title,omitempty"` + Desc string `json:"desc,omitempty"` +} + +type QuoteArea struct { + Type int `json:"type,omitempty"` + URL string `json:"url,omitempty"` + Title string `json:"title,omitempty"` + QuoteText string `json:"quote_text,omitempty"` +} + +type EmphasisContent struct { + Title string `json:"title,omitempty"` + Desc string `json:"desc,omitempty"` +} + +type HorizontalContent struct { + Type int `json:"type,omitempty"` + KeyName string `json:"keyname,omitempty"` + Value string `json:"value,omitempty"` + URL string `json:"url,omitempty"` + MediaId string `json:"media_id,omitempty"` + UserId string `json:"userid,omitempty"` +} + +type Jump struct { + Type int `json:"type,omitempty"` + Title string `json:"title,omitempty"` + URL string `json:"url,omitempty"` + AppId string `json:"appid,omitempty"` + PagePath string `json:"pagepath,omitempty"` +} + +type CardAction struct { + Type int `json:"type,omitempty"` + URL string `json:"url,omitempty"` + AppId string `json:"appid,omitempty"` + PagePath string `json:"pagepath,omitempty"` +} + +type Button struct { + Text string `json:"text,omitempty"` + Style int `json:"style,omitempty"` + Key string `json:"key,omitempty"` +} + +type ButtonSelection struct { + QuestionKey string `json:"question_key,omitempty"` + Title string `json:"title,omitempty"` + OptionList []ButtonOption `json:"option_list,omitempty"` + SelectedId string `json:"selected_id,omitempty"` +} + +type ButtonOption struct { + Id string `json:"id,omitempty"` + Text string `json:"text,omitempty"` +} diff --git a/pkg/wecom/message/notice_butten.go b/pkg/wecom/message/notice_butten.go new file mode 100644 index 0000000..545a68a --- /dev/null +++ b/pkg/wecom/message/notice_butten.go @@ -0,0 +1,17 @@ +package message + +type ButtonNoticeCard struct { + CardType string `json:"card_type"` + Source *TemplateSource `json:"source,omitempty"` + ActionMenu *ActionMenu `json:"action_menu,omitempty"` + TaskId string `json:"task_id,omitempty"` + MainTitle *MainTitle `json:"main_title,omitempty"` + QuoteArea *QuoteArea `json:"quote_area,omitempty"` + EmphasisContent *EmphasisContent `json:"emphasis_content,omitempty"` + SubTitleText string `json:"sub_title_text,omitempty"` + HorizontalContentList []HorizontalContent `json:"horizontal_content_list,omitempty"` + JumpList []Jump `json:"jump_list,omitempty"` + CardAction *CardAction `json:"card_action,omitempty"` + ButtonSelection *ButtonSelection `json:"button_selection,omitempty"` + ButtonList []Button `json:"button_list,omitempty"` +} diff --git a/pkg/wecom/message/notice_text.go b/pkg/wecom/message/notice_text.go new file mode 100644 index 0000000..b7ead52 --- /dev/null +++ b/pkg/wecom/message/notice_text.go @@ -0,0 +1,15 @@ +package message + +type TextNoticeCard struct { + CardType string `json:"card_type"` + Source *TemplateSource `json:"source,omitempty"` + ActionMenu *ActionMenu `json:"action_menu,omitempty"` + TaskId string `json:"task_id,omitempty"` + MainTitle *MainTitle `json:"main_title,omitempty"` + QuoteArea *QuoteArea `json:"quote_area,omitempty"` + EmphasisContent *EmphasisContent `json:"emphasis_content,omitempty"` + SubTitleText string `json:"sub_title_text,omitempty"` + HorizontalContentList []HorizontalContent `json:"horizontal_content_list,omitempty"` + JumpList []Jump `json:"jump_list,omitempty"` + CardAction *CardAction `json:"card_action,omitempty"` +} diff --git a/pkg/wecom/message/request.go b/pkg/wecom/message/request.go new file mode 100644 index 0000000..b2a4d20 --- /dev/null +++ b/pkg/wecom/message/request.go @@ -0,0 +1,28 @@ +package message + +type SendRequest struct { + ToUser string `json:"touser,omitempty"` + ToParty string `json:"toparty,omitempty"` + ToTag string `json:"totag,omitempty"` + MsgType string `json:"msgtype"` + AgentId int `json:"agentid"` + + TaskId string `json:"task_id"` + + TemplateCard interface{} `json:"template_card,omitempty"` + + EnableIdTrans int `json:"enable_id_trans,omitempty"` + EnableDuplicateCheck int `json:"enable_duplicate_check,omitempty"` + DuplicateCheckInterval int `json:"duplicate_check_interval,omitempty"` +} + +type UpdateCardRequest struct { + AgentId int64 `json:"agentid"` + Atall uint8 `json:"atall"` + ResponseCode string `json:"response_code"` + Button *UpdateButton `json:"button"` +} + +type UpdateButton struct { + ReplaceName string `json:"replace_name"` +} diff --git a/pkg/wecom/message/response.go b/pkg/wecom/message/response.go new file mode 100644 index 0000000..78786b6 --- /dev/null +++ b/pkg/wecom/message/response.go @@ -0,0 +1,6 @@ +package message + +type Response struct { + ErrCode int `json:"errcode"` + ErrMsg string `json:"errmsg"` +} diff --git a/pkg/wecom/message/send.go b/pkg/wecom/message/send.go new file mode 100644 index 0000000..3b64878 --- /dev/null +++ b/pkg/wecom/message/send.go @@ -0,0 +1,58 @@ +package message + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "net/http" +) + +func (c *Client) Send(ctx context.Context, reqData *SendRequest) error { + + token, err := c.auth.AccessToken(ctx) + if err != nil { + return err + } + + url := fmt.Sprintf( + "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=%s", + token, + ) + + body, err := json.Marshal(reqData) + if err != nil { + return err + } + + req, err := http.NewRequestWithContext( + ctx, + http.MethodPost, + url, + bytes.NewBuffer(body), + ) + if err != nil { + return err + } + + req.Header.Set("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + var result Response + + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return err + } + + if result.ErrCode != 0 { + return errors.New(result.ErrMsg) + } + + return nil +} diff --git a/pkg/wecom/message/update_card.go b/pkg/wecom/message/update_card.go new file mode 100644 index 0000000..40d5f78 --- /dev/null +++ b/pkg/wecom/message/update_card.go @@ -0,0 +1,58 @@ +package message + +import ( + "bytes" + "context" + "encoding/json" + "errors" + "fmt" + "net/http" +) + +func (c *Client) Update(ctx context.Context, reqData *UpdateCardRequest) error { + + token, err := c.auth.AccessToken(ctx) + if err != nil { + return err + } + + url := fmt.Sprintf( + "https://qyapi.weixin.qq.com/cgi-bin/message/update_template_card?access_token=%s", + token, + ) + + body, err := json.Marshal(reqData) + if err != nil { + return err + } + + req, err := http.NewRequestWithContext( + ctx, + http.MethodPost, + url, + bytes.NewBuffer(body), + ) + if err != nil { + return err + } + + req.Header.Set("Content-Type", "application/json") + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + var result Response + + if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { + return err + } + + if result.ErrCode != 0 { + return errors.New(result.ErrMsg) + } + + return nil +} diff --git a/rpc/chore/chore.pb b/rpc/chore/chore.pb index eecb0e8..e46ac06 100644 Binary files a/rpc/chore/chore.pb and b/rpc/chore/chore.pb differ diff --git a/rpc/chore/chore.proto b/rpc/chore/chore.proto index 17983ea..8a01557 100644 --- a/rpc/chore/chore.proto +++ b/rpc/chore/chore.proto @@ -81,6 +81,9 @@ service Chore { } // 按 id 批量查地区名称 rpc RegionsByIds(RegionsByIdsReq) returns (RegionsByIdsData); + + // 服务间:写入编辑申请日志 + rpc InternalActionApplyLogCreate(InternalActionApplyLogCreateReq) returns (InternalActionApplyLogCreateData); } message Response { @@ -150,4 +153,16 @@ message RegionsByIdsData { message SendSmsReq { string mobile = 1; int32 scene = 2; +} + +message InternalActionApplyLogCreateReq { + int64 admin_id = 1; + int64 obj_id = 2; + uint32 type = 3; // 1产品 2门店 3地址 4销售 + string reason = 4; + uint32 status = 5; // 1通过 2驳回 9待审核 +} + +message InternalActionApplyLogCreateData { + int64 id = 1; } \ No newline at end of file diff --git a/rpc/chore/pb/chore.pb.go b/rpc/chore/pb/chore.pb.go index 56fd514..52f04a0 100644 --- a/rpc/chore/pb/chore.pb.go +++ b/rpc/chore/pb/chore.pb.go @@ -7,12 +7,13 @@ package chore import ( - _ "google.golang.org/genproto/googleapis/api/annotations" - protoreflect "google.golang.org/protobuf/reflect/protoreflect" - protoimpl "google.golang.org/protobuf/runtime/protoimpl" reflect "reflect" sync "sync" unsafe "unsafe" + + _ "google.golang.org/genproto/googleapis/api/annotations" + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" ) const ( @@ -742,6 +743,124 @@ func (x *SendSmsReq) GetScene() int32 { return 0 } +type InternalActionApplyLogCreateReq struct { + state protoimpl.MessageState `protogen:"open.v1"` + AdminId int64 `protobuf:"varint,1,opt,name=admin_id,json=adminId,proto3" json:"admin_id,omitempty"` + ObjId int64 `protobuf:"varint,2,opt,name=obj_id,json=objId,proto3" json:"obj_id,omitempty"` + Type uint32 `protobuf:"varint,3,opt,name=type,proto3" json:"type,omitempty"` // 1产品 2门店 + Reason string `protobuf:"bytes,4,opt,name=reason,proto3" json:"reason,omitempty"` + Status uint32 `protobuf:"varint,5,opt,name=status,proto3" json:"status,omitempty"` // 1通过 2驳回 9待审核 + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InternalActionApplyLogCreateReq) Reset() { + *x = InternalActionApplyLogCreateReq{} + mi := &file_chore_chore_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InternalActionApplyLogCreateReq) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InternalActionApplyLogCreateReq) ProtoMessage() {} + +func (x *InternalActionApplyLogCreateReq) ProtoReflect() protoreflect.Message { + mi := &file_chore_chore_proto_msgTypes[14] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +func (*InternalActionApplyLogCreateReq) Descriptor() ([]byte, []int) { + return file_chore_chore_proto_rawDescGZIP(), []int{14} +} + +func (x *InternalActionApplyLogCreateReq) GetAdminId() int64 { + if x != nil { + return x.AdminId + } + return 0 +} + +func (x *InternalActionApplyLogCreateReq) GetObjId() int64 { + if x != nil { + return x.ObjId + } + return 0 +} + +func (x *InternalActionApplyLogCreateReq) GetType() uint32 { + if x != nil { + return x.Type + } + return 0 +} + +func (x *InternalActionApplyLogCreateReq) GetReason() string { + if x != nil { + return x.Reason + } + return "" +} + +func (x *InternalActionApplyLogCreateReq) GetStatus() uint32 { + if x != nil { + return x.Status + } + return 0 +} + +type InternalActionApplyLogCreateData struct { + state protoimpl.MessageState `protogen:"open.v1"` + Id int64 `protobuf:"varint,1,opt,name=id,proto3" json:"id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *InternalActionApplyLogCreateData) Reset() { + *x = InternalActionApplyLogCreateData{} + mi := &file_chore_chore_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *InternalActionApplyLogCreateData) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*InternalActionApplyLogCreateData) ProtoMessage() {} + +func (x *InternalActionApplyLogCreateData) ProtoReflect() protoreflect.Message { + mi := &file_chore_chore_proto_msgTypes[15] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +func (*InternalActionApplyLogCreateData) Descriptor() ([]byte, []int) { + return file_chore_chore_proto_rawDescGZIP(), []int{15} +} + +func (x *InternalActionApplyLogCreateData) GetId() int64 { + if x != nil { + return x.Id + } + return 0 +} + var File_chore_chore_proto protoreflect.FileDescriptor const file_chore_chore_proto_rawDesc = "" + @@ -791,7 +910,16 @@ const file_chore_chore_proto_rawDesc = "" + "\n" + "SendSmsReq\x12\x16\n" + "\x06mobile\x18\x01 \x01(\tR\x06mobile\x12\x14\n" + - "\x05scene\x18\x02 \x01(\x05R\x05scene2\xe9\t\n" + + "\x05scene\x18\x02 \x01(\x05R\x05scene\"\x97\x01\n" + + "\x1fInternalActionApplyLogCreateReq\x12\x19\n" + + "\badmin_id\x18\x01 \x01(\x03R\aadminId\x12\x15\n" + + "\x06obj_id\x18\x02 \x01(\x03R\x05objId\x12\x12\n" + + "\x04type\x18\x03 \x01(\rR\x04type\x12\x16\n" + + "\x06reason\x18\x04 \x01(\tR\x06reason\x12\x16\n" + + "\x06status\x18\x05 \x01(\rR\x06status\"2\n" + + " InternalActionApplyLogCreateData\x12\x0e\n" + + "\x02id\x18\x01 \x01(\x03R\x02id2\xda\n" + + "\n" + "\x05Chore\x12P\n" + "\x06Policy\x12\x10.chore.PolicyReq\x1a\x0f.chore.Response\"#\x82\xd3\xe4\x93\x02\x1d:\x01*\x12\x18/api/v3/chore/oss/policy\x12F\n" + "\x04Test\x12\x0e.chore.TestReq\x1a\x0f.chore.Response\"\x1d\x82\xd3\xe4\x93\x02\x17:\x01*\"\x12/api/v3/chore/test\x12_\n" + @@ -811,7 +939,8 @@ const file_chore_chore_proto_rawDesc = "" + "\n" + "SystemInfo\x12\f.chore.IdReq\x1a\x0f.chore.Response\"#\x82\xd3\xe4\x93\x02\x1d\x12\x1b/admin/v3/chore/system/info\x12U\n" + "\vSystemItems\x12\x0f.chore.EmptyReq\x1a\x0f.chore.Response\"$\x82\xd3\xe4\x93\x02\x1e\x12\x1c/admin/v3/chore/system/items\x12?\n" + - "\fRegionsByIds\x12\x16.chore.RegionsByIdsReq\x1a\x17.chore.RegionsByIdsDataB\x19Z\x17lone-services/rpc/choreb\x06proto3" + "\fRegionsByIds\x12\x16.chore.RegionsByIdsReq\x1a\x17.chore.RegionsByIdsData\x12o\n" + + "\x1cInternalActionApplyLogCreate\x12&.chore.InternalActionApplyLogCreateReq\x1a'.chore.InternalActionApplyLogCreateDataB\x19Z\x17lone-services/rpc/choreb\x06proto3" var ( file_chore_chore_proto_rawDescOnce sync.Once @@ -825,22 +954,24 @@ func file_chore_chore_proto_rawDescGZIP() []byte { return file_chore_chore_proto_rawDescData } -var file_chore_chore_proto_msgTypes = make([]protoimpl.MessageInfo, 14) +var file_chore_chore_proto_msgTypes = make([]protoimpl.MessageInfo, 16) var file_chore_chore_proto_goTypes = []any{ - (*Response)(nil), // 0: chore.Response - (*EmptyReq)(nil), // 1: chore.EmptyReq - (*IdReq)(nil), // 2: chore.IdReq - (*StatusReq)(nil), // 3: chore.StatusReq - (*EditReq)(nil), // 4: chore.EditReq - (*SystemEditReq)(nil), // 5: chore.SystemEditReq - (*PolicyReq)(nil), // 6: chore.PolicyReq - (*TestReq)(nil), // 7: chore.TestReq - (*DecryptMobileReq)(nil), // 8: chore.DecryptMobileReq - (*RegionsReq)(nil), // 9: chore.RegionsReq - (*RegionsByIdsReq)(nil), // 10: chore.RegionsByIdsReq - (*RegionItem)(nil), // 11: chore.RegionItem - (*RegionsByIdsData)(nil), // 12: chore.RegionsByIdsData - (*SendSmsReq)(nil), // 13: chore.SendSmsReq + (*Response)(nil), // 0: chore.Response + (*EmptyReq)(nil), // 1: chore.EmptyReq + (*IdReq)(nil), // 2: chore.IdReq + (*StatusReq)(nil), // 3: chore.StatusReq + (*EditReq)(nil), // 4: chore.EditReq + (*SystemEditReq)(nil), // 5: chore.SystemEditReq + (*PolicyReq)(nil), // 6: chore.PolicyReq + (*TestReq)(nil), // 7: chore.TestReq + (*DecryptMobileReq)(nil), // 8: chore.DecryptMobileReq + (*RegionsReq)(nil), // 9: chore.RegionsReq + (*RegionsByIdsReq)(nil), // 10: chore.RegionsByIdsReq + (*RegionItem)(nil), // 11: chore.RegionItem + (*RegionsByIdsData)(nil), // 12: chore.RegionsByIdsData + (*SendSmsReq)(nil), // 13: chore.SendSmsReq + (*InternalActionApplyLogCreateReq)(nil), // 14: chore.InternalActionApplyLogCreateReq + (*InternalActionApplyLogCreateData)(nil), // 15: chore.InternalActionApplyLogCreateData } var file_chore_chore_proto_depIdxs = []int32{ 11, // 0: chore.RegionsByIdsData.items:type_name -> chore.RegionItem @@ -859,23 +990,25 @@ var file_chore_chore_proto_depIdxs = []int32{ 2, // 13: chore.Chore.SystemInfo:input_type -> chore.IdReq 1, // 14: chore.Chore.SystemItems:input_type -> chore.EmptyReq 10, // 15: chore.Chore.RegionsByIds:input_type -> chore.RegionsByIdsReq - 0, // 16: chore.Chore.Policy:output_type -> chore.Response - 0, // 17: chore.Chore.Test:output_type -> chore.Response - 0, // 18: chore.Chore.DecryptMobile:output_type -> chore.Response - 0, // 19: chore.Chore.Regions:output_type -> chore.Response - 0, // 20: chore.Chore.SendSms:output_type -> chore.Response - 0, // 21: chore.Chore.AnswerItems:output_type -> chore.Response - 0, // 22: chore.Chore.AnswerStatus:output_type -> chore.Response - 0, // 23: chore.Chore.AnswerInfo:output_type -> chore.Response - 0, // 24: chore.Chore.AnswerCreate:output_type -> chore.Response - 0, // 25: chore.Chore.AnswerEdit:output_type -> chore.Response - 0, // 26: chore.Chore.SystemEdit:output_type -> chore.Response - 0, // 27: chore.Chore.SystemStatus:output_type -> chore.Response - 0, // 28: chore.Chore.SystemInfo:output_type -> chore.Response - 0, // 29: chore.Chore.SystemItems:output_type -> chore.Response - 12, // 30: chore.Chore.RegionsByIds:output_type -> chore.RegionsByIdsData - 16, // [16:31] is the sub-list for method output_type - 1, // [1:16] is the sub-list for method input_type + 14, // 16: chore.Chore.InternalActionApplyLogCreate:input_type -> chore.InternalActionApplyLogCreateReq + 0, // 17: chore.Chore.Policy:output_type -> chore.Response + 0, // 18: chore.Chore.Test:output_type -> chore.Response + 0, // 19: chore.Chore.DecryptMobile:output_type -> chore.Response + 0, // 20: chore.Chore.Regions:output_type -> chore.Response + 0, // 21: chore.Chore.SendSms:output_type -> chore.Response + 0, // 22: chore.Chore.AnswerItems:output_type -> chore.Response + 0, // 23: chore.Chore.AnswerStatus:output_type -> chore.Response + 0, // 24: chore.Chore.AnswerInfo:output_type -> chore.Response + 0, // 25: chore.Chore.AnswerCreate:output_type -> chore.Response + 0, // 26: chore.Chore.AnswerEdit:output_type -> chore.Response + 0, // 27: chore.Chore.SystemEdit:output_type -> chore.Response + 0, // 28: chore.Chore.SystemStatus:output_type -> chore.Response + 0, // 29: chore.Chore.SystemInfo:output_type -> chore.Response + 0, // 30: chore.Chore.SystemItems:output_type -> chore.Response + 12, // 31: chore.Chore.RegionsByIds:output_type -> chore.RegionsByIdsData + 15, // 32: chore.Chore.InternalActionApplyLogCreate:output_type -> chore.InternalActionApplyLogCreateData + 17, // [17:33] is the sub-list for method output_type + 1, // [1:17] is the sub-list for method input_type 1, // [1:1] is the sub-list for extension type_name 1, // [1:1] is the sub-list for extension extendee 0, // [0:1] is the sub-list for field type_name @@ -892,7 +1025,7 @@ func file_chore_chore_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_chore_chore_proto_rawDesc), len(file_chore_chore_proto_rawDesc)), NumEnums: 0, - NumMessages: 14, + NumMessages: 16, NumExtensions: 0, NumServices: 1, }, diff --git a/rpc/chore/pb/chore_grpc.pb.go b/rpc/chore/pb/chore_grpc.pb.go index 7c86c7e..dac0429 100644 --- a/rpc/chore/pb/chore_grpc.pb.go +++ b/rpc/chore/pb/chore_grpc.pb.go @@ -19,21 +19,22 @@ import ( const _ = grpc.SupportPackageIsVersion9 const ( - Chore_Policy_FullMethodName = "/chore.Chore/Policy" - Chore_Test_FullMethodName = "/chore.Chore/Test" - Chore_DecryptMobile_FullMethodName = "/chore.Chore/DecryptMobile" - Chore_Regions_FullMethodName = "/chore.Chore/Regions" - Chore_SendSms_FullMethodName = "/chore.Chore/SendSms" - Chore_AnswerItems_FullMethodName = "/chore.Chore/AnswerItems" - Chore_AnswerStatus_FullMethodName = "/chore.Chore/AnswerStatus" - Chore_AnswerInfo_FullMethodName = "/chore.Chore/AnswerInfo" - Chore_AnswerCreate_FullMethodName = "/chore.Chore/AnswerCreate" - Chore_AnswerEdit_FullMethodName = "/chore.Chore/AnswerEdit" - Chore_SystemEdit_FullMethodName = "/chore.Chore/SystemEdit" - Chore_SystemStatus_FullMethodName = "/chore.Chore/SystemStatus" - Chore_SystemInfo_FullMethodName = "/chore.Chore/SystemInfo" - Chore_SystemItems_FullMethodName = "/chore.Chore/SystemItems" - Chore_RegionsByIds_FullMethodName = "/chore.Chore/RegionsByIds" + Chore_Policy_FullMethodName = "/chore.Chore/Policy" + Chore_Test_FullMethodName = "/chore.Chore/Test" + Chore_DecryptMobile_FullMethodName = "/chore.Chore/DecryptMobile" + Chore_Regions_FullMethodName = "/chore.Chore/Regions" + Chore_SendSms_FullMethodName = "/chore.Chore/SendSms" + Chore_AnswerItems_FullMethodName = "/chore.Chore/AnswerItems" + Chore_AnswerStatus_FullMethodName = "/chore.Chore/AnswerStatus" + Chore_AnswerInfo_FullMethodName = "/chore.Chore/AnswerInfo" + Chore_AnswerCreate_FullMethodName = "/chore.Chore/AnswerCreate" + Chore_AnswerEdit_FullMethodName = "/chore.Chore/AnswerEdit" + Chore_SystemEdit_FullMethodName = "/chore.Chore/SystemEdit" + Chore_SystemStatus_FullMethodName = "/chore.Chore/SystemStatus" + Chore_SystemInfo_FullMethodName = "/chore.Chore/SystemInfo" + Chore_SystemItems_FullMethodName = "/chore.Chore/SystemItems" + Chore_RegionsByIds_FullMethodName = "/chore.Chore/RegionsByIds" + Chore_InternalActionApplyLogCreate_FullMethodName = "/chore.Chore/InternalActionApplyLogCreate" ) // ChoreClient is the client API for Chore service. @@ -56,6 +57,8 @@ type ChoreClient interface { SystemItems(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*Response, error) // 按 id 批量查地区名称 RegionsByIds(ctx context.Context, in *RegionsByIdsReq, opts ...grpc.CallOption) (*RegionsByIdsData, error) + // 服务间:写入编辑申请日志 + InternalActionApplyLogCreate(ctx context.Context, in *InternalActionApplyLogCreateReq, opts ...grpc.CallOption) (*InternalActionApplyLogCreateData, error) } type choreClient struct { @@ -216,6 +219,16 @@ func (c *choreClient) RegionsByIds(ctx context.Context, in *RegionsByIdsReq, opt return out, nil } +func (c *choreClient) InternalActionApplyLogCreate(ctx context.Context, in *InternalActionApplyLogCreateReq, opts ...grpc.CallOption) (*InternalActionApplyLogCreateData, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(InternalActionApplyLogCreateData) + err := c.cc.Invoke(ctx, Chore_InternalActionApplyLogCreate_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // ChoreServer is the server API for Chore service. // All implementations must embed UnimplementedChoreServer // for forward compatibility. @@ -236,6 +249,8 @@ type ChoreServer interface { SystemItems(context.Context, *EmptyReq) (*Response, error) // 按 id 批量查地区名称 RegionsByIds(context.Context, *RegionsByIdsReq) (*RegionsByIdsData, error) + // 服务间:写入编辑申请日志 + InternalActionApplyLogCreate(context.Context, *InternalActionApplyLogCreateReq) (*InternalActionApplyLogCreateData, error) mustEmbedUnimplementedChoreServer() } @@ -291,6 +306,9 @@ func (UnimplementedChoreServer) SystemItems(context.Context, *EmptyReq) (*Respon func (UnimplementedChoreServer) RegionsByIds(context.Context, *RegionsByIdsReq) (*RegionsByIdsData, error) { return nil, status.Error(codes.Unimplemented, "method RegionsByIds not implemented") } +func (UnimplementedChoreServer) InternalActionApplyLogCreate(context.Context, *InternalActionApplyLogCreateReq) (*InternalActionApplyLogCreateData, error) { + return nil, status.Error(codes.Unimplemented, "method InternalActionApplyLogCreate not implemented") +} func (UnimplementedChoreServer) mustEmbedUnimplementedChoreServer() {} func (UnimplementedChoreServer) testEmbeddedByValue() {} @@ -582,6 +600,24 @@ func _Chore_RegionsByIds_Handler(srv interface{}, ctx context.Context, dec func( return interceptor(ctx, in, info, handler) } +func _Chore_InternalActionApplyLogCreate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(InternalActionApplyLogCreateReq) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ChoreServer).InternalActionApplyLogCreate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Chore_InternalActionApplyLogCreate_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ChoreServer).InternalActionApplyLogCreate(ctx, req.(*InternalActionApplyLogCreateReq)) + } + return interceptor(ctx, in, info, handler) +} + // Chore_ServiceDesc is the grpc.ServiceDesc for Chore service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -649,6 +685,10 @@ var Chore_ServiceDesc = grpc.ServiceDesc{ MethodName: "RegionsByIds", Handler: _Chore_RegionsByIds_Handler, }, + { + MethodName: "InternalActionApplyLogCreate", + Handler: _Chore_InternalActionApplyLogCreate_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "chore/chore.proto", diff --git a/services/chore/choreclient/chore.go b/services/chore/choreclient/chore.go index 992495d..bb86000 100644 --- a/services/chore/choreclient/chore.go +++ b/services/chore/choreclient/chore.go @@ -14,20 +14,22 @@ import ( ) type ( - DecryptMobileReq = chore.DecryptMobileReq - EditReq = chore.EditReq - EmptyReq = chore.EmptyReq - IdReq = chore.IdReq - PolicyReq = chore.PolicyReq - RegionItem = chore.RegionItem - RegionsByIdsData = chore.RegionsByIdsData - RegionsByIdsReq = chore.RegionsByIdsReq - RegionsReq = chore.RegionsReq - Response = chore.Response - SendSmsReq = chore.SendSmsReq - StatusReq = chore.StatusReq - SystemEditReq = chore.SystemEditReq - TestReq = chore.TestReq + DecryptMobileReq = chore.DecryptMobileReq + EditReq = chore.EditReq + EmptyReq = chore.EmptyReq + IdReq = chore.IdReq + InternalActionApplyLogCreateData = chore.InternalActionApplyLogCreateData + InternalActionApplyLogCreateReq = chore.InternalActionApplyLogCreateReq + PolicyReq = chore.PolicyReq + RegionItem = chore.RegionItem + RegionsByIdsData = chore.RegionsByIdsData + RegionsByIdsReq = chore.RegionsByIdsReq + RegionsReq = chore.RegionsReq + Response = chore.Response + SendSmsReq = chore.SendSmsReq + StatusReq = chore.StatusReq + SystemEditReq = chore.SystemEditReq + TestReq = chore.TestReq Chore interface { Policy(ctx context.Context, in *PolicyReq, opts ...grpc.CallOption) (*Response, error) @@ -46,6 +48,8 @@ type ( SystemItems(ctx context.Context, in *EmptyReq, opts ...grpc.CallOption) (*Response, error) // 按 id 批量查地区名称 RegionsByIds(ctx context.Context, in *RegionsByIdsReq, opts ...grpc.CallOption) (*RegionsByIdsData, error) + // 服务间:写入编辑申请日志 + InternalActionApplyLogCreate(ctx context.Context, in *InternalActionApplyLogCreateReq, opts ...grpc.CallOption) (*InternalActionApplyLogCreateData, error) } defaultChore struct { @@ -134,3 +138,9 @@ func (m *defaultChore) RegionsByIds(ctx context.Context, in *RegionsByIdsReq, op client := chore.NewChoreClient(m.cli.Conn()) return client.RegionsByIds(ctx, in, opts...) } + +// 服务间:写入编辑申请日志 +func (m *defaultChore) InternalActionApplyLogCreate(ctx context.Context, in *InternalActionApplyLogCreateReq, opts ...grpc.CallOption) (*InternalActionApplyLogCreateData, error) { + client := chore.NewChoreClient(m.cli.Conn()) + return client.InternalActionApplyLogCreate(ctx, in, opts...) +} diff --git a/services/chore/internal/dao/action_apply_log.go b/services/chore/internal/dao/action_apply_log.go new file mode 100644 index 0000000..15b3bc3 --- /dev/null +++ b/services/chore/internal/dao/action_apply_log.go @@ -0,0 +1,25 @@ +package dao + +// 编辑申请对象类型 +const ( + ApplyLogTypeProduct uint8 = 1 // 产品 + ApplyLogTypeStore uint8 = 2 // 门店 + ApplyLogTypeAddress uint8 = 3 // 地址 + ApplyLogTypeSales uint8 = 4 // 销售 +) + +// 编辑申请状态 +const ( + ApplyStatusDone uint8 = 1 // 通过 + ApplyStatusNotPass uint8 = 2 // 驳回 + ApplyStatusWaitPass uint8 = 9 // 待审核 +) + +type ActionApplyLogCreate struct { + Id int `gorm:"column:id;primaryKey;autoIncrement"` + AdminId int `gorm:"column:admin_id"` + ObjId int `gorm:"column:obj_id"` + Type uint8 `gorm:"column:type"` + Reason string `gorm:"column:reason"` + Status uint8 `gorm:"column:status"` +} diff --git a/services/chore/internal/logic/internalActionApplyLogCreateLogic.go b/services/chore/internal/logic/internalActionApplyLogCreateLogic.go new file mode 100644 index 0000000..b7e0498 --- /dev/null +++ b/services/chore/internal/logic/internalActionApplyLogCreateLogic.go @@ -0,0 +1,52 @@ +package logic + +import ( + "context" + + "lone-services/pkg/utils" + chore "lone-services/rpc/chore/pb" + "lone-services/services/chore/internal/dao" + "lone-services/services/chore/internal/model" + "lone-services/services/chore/internal/svc" + + "github.com/zeromicro/go-zero/core/logx" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type InternalActionApplyLogCreateLogic struct { + ctx context.Context + svcCtx *svc.ServiceContext + logx.Logger +} + +func NewInternalActionApplyLogCreateLogic(ctx context.Context, svcCtx *svc.ServiceContext) *InternalActionApplyLogCreateLogic { + return &InternalActionApplyLogCreateLogic{ + ctx: ctx, + svcCtx: svcCtx, + Logger: logx.WithContext(ctx), + } +} + +// InternalActionApplyLogCreate 服务间写入编辑申请日志 +func (l *InternalActionApplyLogCreateLogic) InternalActionApplyLogCreate(in *chore.InternalActionApplyLogCreateReq) (*chore.InternalActionApplyLogCreateData, error) { + if in.GetAdminId() < int64(utils.NumberOne) || in.GetObjId() < int64(utils.NumberOne) { + return nil, status.Error(codes.InvalidArgument, utils.ErrorParams.Msg) + } + if in.GetType() < 1 || in.GetStatus() < 1 { + return nil, status.Error(codes.InvalidArgument, utils.ErrorParams.Msg) + } + + add := dao.ActionApplyLogCreate{ + AdminId: int(in.GetAdminId()), + ObjId: int(in.GetObjId()), + Type: uint8(in.GetType()), + Reason: in.GetReason(), + Status: uint8(in.GetStatus()), + } + if err := (model.ActionApplyLogModel{}.Init().Create(&add)); err != nil { + l.Errorf("action apply log create: %v", err) + return nil, status.Error(codes.Internal, utils.Fail.Msg) + } + return &chore.InternalActionApplyLogCreateData{Id: int64(add.Id)}, nil +} diff --git a/services/product/internal/model/action_apply_log_model.go b/services/chore/internal/model/action_apply_log.go similarity index 89% rename from services/product/internal/model/action_apply_log_model.go rename to services/chore/internal/model/action_apply_log.go index c60dea5..98cd496 100644 --- a/services/product/internal/model/action_apply_log_model.go +++ b/services/chore/internal/model/action_apply_log.go @@ -2,7 +2,7 @@ package model import ( "lone-services/pkg/modelbase" - "lone-services/services/product/internal/dao" + "lone-services/services/chore/internal/dao" ) type ActionApplyLogModel struct { diff --git a/services/chore/internal/server/choreServer.go b/services/chore/internal/server/choreServer.go index 5a7d6e0..d2394b8 100644 --- a/services/chore/internal/server/choreServer.go +++ b/services/chore/internal/server/choreServer.go @@ -98,3 +98,9 @@ func (s *ChoreServer) RegionsByIds(ctx context.Context, in *chore.RegionsByIdsRe l := logic.NewRegionsByIdsLogic(ctx, s.svcCtx) return l.RegionsByIds(in) } + +// 服务间:写入编辑申请日志 +func (s *ChoreServer) InternalActionApplyLogCreate(ctx context.Context, in *chore.InternalActionApplyLogCreateReq) (*chore.InternalActionApplyLogCreateData, error) { + l := logic.NewInternalActionApplyLogCreateLogic(ctx, s.svcCtx) + return l.InternalActionApplyLogCreate(in) +} diff --git a/services/product/internal/apply/apply.go b/services/product/internal/apply/apply.go index 4da1f34..7af92e9 100644 --- a/services/product/internal/apply/apply.go +++ b/services/product/internal/apply/apply.go @@ -3,9 +3,10 @@ package apply import ( "context" "lone-services/pkg/redis" + "lone-services/pkg/rpcclient" "lone-services/pkg/utils" + chorepb "lone-services/rpc/chore/pb" "lone-services/services/product/internal/dao" - "lone-services/services/product/internal/model" "lone-services/services/product/internal/wecom" "strconv" "time" @@ -66,8 +67,7 @@ func Del(ctx context.Context, uId, objId int) error { return redis.Client.Del(ctx, redisKey(uId, objId)).Err() } -// Set 发起编辑申请:写 Redis + action_apply_log,企微消息预留 -func Set(ctx context.Context, uId, objId int, reason string) error { +func Set(ctx context.Context, choreSvcName string, uId, objId int, reason string) error { data := Data{ Uid: uId, Id: objId, @@ -82,15 +82,7 @@ func Set(ctx context.Context, uId, objId int, reason string) error { return err } - add := dao.ActionApplyLogCreate{ - AdminId: uId, - ObjId: objId, - Type: dao.EditLogTypeProduct, - Reason: reason, - Status: dao.ApplyStatusWaitPass, - } - err = model.ActionApplyLogModel{}.Init().Create(&add) - if err != nil { + if err = createApplyLog(ctx, choreSvcName, uId, objId, dao.EditLogTypeProduct, dao.ApplyStatusWaitPass, reason); err != nil { _ = Del(ctx, uId, objId) logx.WithContext(ctx).Errorf("action apply log create: %v", err) return err @@ -128,7 +120,6 @@ func saveRedis(ctx context.Context, data *Data) (string, error) { return md5, nil } -// CanEdit 是否已获编辑权限(申请通过) func CanEdit(ctx context.Context, uId, objId int) bool { data, err := Get(ctx, uId, objId) if err != nil { @@ -138,8 +129,7 @@ func CanEdit(ctx context.Context, uId, objId int) bool { return data.Status == dao.ApplyStatusDone } -// Pass 临时:将编辑申请置为通过(企微审批对接后删除) -func Pass(ctx context.Context, uId, objId int) error { +func Pass(ctx context.Context, choreSvcName string, uId, objId int) error { data, err := Get(ctx, uId, objId) if err != nil { return err @@ -171,15 +161,7 @@ func Pass(ctx context.Context, uId, objId int) error { return err } - add := dao.ActionApplyLogCreate{ - AdminId: uId, - ObjId: objId, - Type: dao.EditLogTypeProduct, - Reason: reason, - Status: dao.ApplyStatusDone, - } - err = model.ActionApplyLogModel{}.Init().Create(&add) - if err != nil { + if err = createApplyLog(ctx, choreSvcName, uId, objId, dao.EditLogTypeProduct, dao.ApplyStatusDone, reason); err != nil { logx.WithContext(ctx).Errorf("action apply log pass: %v", err) return err } @@ -194,6 +176,21 @@ func Pass(ctx context.Context, uId, objId int) error { return nil } +func createApplyLog(ctx context.Context, choreSvcName string, adminId, objId int, typ, status uint8, reason string) error { + cli, err := rpcclient.Get(choreSvcName) + if err != nil { + return err + } + _, err = chorepb.NewChoreClient(cli.Conn()).InternalActionApplyLogCreate(ctx, &chorepb.InternalActionApplyLogCreateReq{ + AdminId: int64(adminId), + ObjId: int64(objId), + Type: uint32(typ), + Reason: reason, + Status: uint32(status), + }) + return err +} + // errAlreadyHandled 申请已驳回等不可再通过 var errAlreadyHandled = errApply("申请状态不可通过") diff --git a/services/product/internal/dao/edit.go b/services/product/internal/dao/edit.go index 31d296c..320f6db 100644 --- a/services/product/internal/dao/edit.go +++ b/services/product/internal/dao/edit.go @@ -23,12 +23,3 @@ type EditLogCreate struct { AdminId int `gorm:"column:admin_id"` AdminName string `gorm:"column:admin_name"` } - -type ActionApplyLogCreate struct { - Id int `gorm:"column:id;primaryKey;autoIncrement"` - AdminId int `gorm:"column:admin_id"` - ObjId int `gorm:"column:obj_id"` - Type uint8 `gorm:"column:type"` - Reason string `gorm:"column:reason"` - Status uint8 `gorm:"column:status"` -} diff --git a/services/product/internal/logic/editapplylogic.go b/services/product/internal/logic/editapplylogic.go index 4c433d1..9494cfc 100644 --- a/services/product/internal/logic/editapplylogic.go +++ b/services/product/internal/logic/editapplylogic.go @@ -63,7 +63,7 @@ func (l *EditApplyLogic) EditApply(in *product.EditApplyReq) (*product.Response, return outResponse(utils.Fail, "已经申请过"), nil } - if err = apply.Set(l.ctx, adminId, int(req.Id), req.Reason); err != nil { + if err = apply.Set(l.ctx, l.svcCtx.ChoreSvcName, adminId, int(req.Id), req.Reason); err != nil { l.Errorf("edit apply set: %v", err) return failResponse(utils.Fail), nil } @@ -96,7 +96,7 @@ func (l *EditApplyLogic) EditApplyPass(in *product.EditApplyPassReq) (*product.R } adminId := int(adminInfo.ID) - if err = apply.Pass(l.ctx, adminId, int(req.Id)); err != nil { + if err = apply.Pass(l.ctx, l.svcCtx.ChoreSvcName, adminId, int(req.Id)); err != nil { if apply.IsAlreadyHandled(err) { return outResponse(utils.Fail, err.Error()), nil } diff --git a/services/product/internal/svc/servicecontext.go b/services/product/internal/svc/servicecontext.go index 66b0bba..91020b8 100644 --- a/services/product/internal/svc/servicecontext.go +++ b/services/product/internal/svc/servicecontext.go @@ -4,19 +4,26 @@ import ( "lone-services/pkg/utils" "lone-services/services/product/internal/config" + "github.com/zeromicro/go-zero/core/logx" "gorm.io/gorm" ) type ServiceContext struct { - Config config.Config - DB *gorm.DB - Prefix string + Config config.Config + DB *gorm.DB + Prefix string + ChoreSvcName string } func NewServiceContext(c config.Config, db *gorm.DB) *ServiceContext { + choreSvc := utils.GetConfigString("services.chore") + if choreSvc == utils.StringEmpty { + logx.Error("config services.chore empty") + } return &ServiceContext{ - Config: c, - DB: db, - Prefix: utils.GetConfigString("mysql.prefix"), + Config: c, + DB: db, + Prefix: utils.GetConfigString("mysql.prefix"), + ChoreSvcName: choreSvc, } } diff --git a/services/wecom/internal/logic/message/action/review_action.go b/services/wecom/internal/logic/message/action/review_action.go new file mode 100644 index 0000000..cd34649 --- /dev/null +++ b/services/wecom/internal/logic/message/action/review_action.go @@ -0,0 +1,115 @@ +package action + +import ( + "base-service/app/dao" + "base-service/app/models" + "base-service/app/services/wecom/client" + "base-service/pkg/wecom/message" + "base-service/utils" + "context" + "errors" + "fmt" + "strconv" + "time" +) + +type ReviewActionService struct { +} + +func (s ReviewActionService) UpdateCard(reqData *dao.ReviewActionRequest) error { + lockKey := fmt.Sprintf( + "wecom:review:lock:%s", + reqData.TaskId, + ) + + ok, err := utils.Redis.SetNX(lockKey, 1, 5*time.Second).Result() + + if err != nil { + utils.Logger.Error("redis read filed") + return err + } + + if !ok { + utils.Logger.Info("You clicked too fast") + return errors.New("You clicked too fast") + } + w := models.Params{ + Eq: map[string]string{ + "task_id": reqData.TaskId, + }, + } + task := dao.WecomReviewTask{} + err = models.WecomReviewTask{}.Init().GetOne( + w, + &task, + ) + if task.ReviewStatus != dao.WecomReviewPending { + utils.Logger.Info("task already reviewed") + return errors.New("task already reviewed") + } + if err != nil { + return err + } + replaceName := dao.WecomReviewExpiredFormat + reviewStatus := dao.WecomReviewExpired + + switch reqData.Action { + + case dao.WecomReviewActionAgree: + + replaceName = dao.WecomReviewAgreeFormat + reviewStatus = dao.WecomReviewAgree + + case dao.WecomReviewActionReject: + + replaceName = dao.WecomReviewRejectFormat + reviewStatus = dao.WecomReviewReject + case dao.WecomReviewActionExpired: + + replaceName = dao.WecomReviewExpiredFormat + reviewStatus = dao.WecomReviewExpired + default: + + replaceName = dao.WecomReviewExpiredFormat + reviewStatus = dao.WecomReviewExpired + } + + updateReq := &message.UpdateCardRequest{ + AgentId: reqData.AgentId, + Atall: dao.NumberOne, + ResponseCode: reqData.ResponseCode, + + Button: &message.UpdateButton{ + ReplaceName: replaceName, + }, + } + + messageClient := client.NewMessageClient(dao.WecomTypeReview) + + err = messageClient.Update(context.Background(), updateReq) + + if err != nil { + utils.Logger.Info("messageclient update filed", err) + return err + } + + _, err = models.WecomReviewTask{}.Init().Edit( + w, + map[string]string{ + "review_status": strconv.Itoa(reviewStatus), + "response_code": reqData.ResponseCode, + }, + ) + return err + + // 只做更新卡片,去掉发送通知 + // notifyJob := dao.WeComJob{ + // ObjId: task.ObjId, + // ObjType: task.ObjType, + // Type: dao.WecomTypeNotify, + // NotifyObjId: task.NotifyObjId, + // Message: task.Message, + // } + + // return sender.WecomSenderService{}.Send(notifyJob) +} diff --git a/services/wecom/internal/logic/message/builder/button_card.go b/services/wecom/internal/logic/message/builder/button_card.go new file mode 100644 index 0000000..876b9f0 --- /dev/null +++ b/services/wecom/internal/logic/message/builder/button_card.go @@ -0,0 +1,48 @@ +package builder + +import ( + "base-service/pkg/wecom/message" + "base-service/utils" +) + +type ButtonCardParams struct { + ToUser string + TaskId string + Title string + Desc string + Content []message.HorizontalContent + Buttons []message.Button +} + +func ButtonCard(params ButtonCardParams) (*message.SendRequest, error) { + + return &message.SendRequest{ + ToUser: params.ToUser, + MsgType: "template_card", + AgentId: utils.GetConfigInt("wecom.review_agent_id"), + + TemplateCard: &message.ButtonNoticeCard{ + CardType: "button_interaction", + + TaskId: params.TaskId, + + Source: &message.TemplateSource{ + IconURL: utils.GetConfigString("wecom.icon_url"), + Desc: params.Desc, + }, + + MainTitle: &message.MainTitle{ + Title: params.Title, + }, + + HorizontalContentList: params.Content, + + ButtonList: params.Buttons, + + // CardAction: &message.CardAction{ + // Type: 1, + // URL: params.URL, + // }, + }, + }, nil +} diff --git a/services/wecom/internal/logic/message/builder/text_card.go b/services/wecom/internal/logic/message/builder/text_card.go new file mode 100644 index 0000000..7855caa --- /dev/null +++ b/services/wecom/internal/logic/message/builder/text_card.go @@ -0,0 +1,42 @@ +package builder + +import ( + "base-service/pkg/wecom/message" + "base-service/utils" +) + +type TextCardParams struct { + ToUser string + Desc string + Title string + Content []message.HorizontalContent +} + +func TextCard(params TextCardParams) (*message.SendRequest, error) { + + return &message.SendRequest{ + ToUser: params.ToUser, + MsgType: "template_card", + AgentId: utils.GetConfigInt("wecom.notify_agent_id"), + + TemplateCard: &message.TextNoticeCard{ + CardType: "text_notice", + + Source: &message.TemplateSource{ + IconURL: utils.GetConfigString("wecom.icon_url"), + Desc: params.Desc, + }, + + MainTitle: &message.MainTitle{ + Title: params.Title, + }, + + HorizontalContentList: params.Content, + + CardAction: &message.CardAction{ + Type: 1, + URL: "https://google.com", + }, + }, + }, nil +} diff --git a/services/wecom/internal/logic/message/handlers/base.go b/services/wecom/internal/logic/message/handlers/base.go new file mode 100644 index 0000000..1c96530 --- /dev/null +++ b/services/wecom/internal/logic/message/handlers/base.go @@ -0,0 +1,10 @@ +package handlers + +import ( + "base-service/app/dao" + "base-service/pkg/wecom/message" +) + +type Handler interface { + Build(job dao.WeComJob) ([]*message.SendRequest, error) +} diff --git a/services/wecom/internal/logic/message/handlers/factory.go b/services/wecom/internal/logic/message/handlers/factory.go new file mode 100644 index 0000000..4707a51 --- /dev/null +++ b/services/wecom/internal/logic/message/handlers/factory.go @@ -0,0 +1,79 @@ +package handlers + +import ( + "base-service/app/dao" + "errors" +) + +func GetHandler(job dao.WeComJob) (Handler, error) { + + switch job.ObjType { + + // 产品 + case dao.EditLogTypeProduct: + + switch job.Type { + + case dao.ProductApplyReview: + return ProductReviewHandler{}, nil + + case dao.ProductNeedReview: + return ProductNotifyApplyHandler{}, nil + + case dao.ProductSubmitReview: + return ProductNotifySubmitHandler{}, nil + + case dao.ProductFirstReview: + return ProductNotifyFirstHandler{}, nil + + case dao.ProductSecondReview: + return ProductNotifySecondHandler{}, nil + + case dao.ProductOrdinaryReview: + return ProductNotifyOrdinaryHandler{}, nil + } + // 门店 + case dao.EditLogTypeStore: + switch job.Type { + + case dao.StoreApplyReview: + return StoreReviewHandler{}, nil + + case dao.StoreNeedReview: + return StoreNotifyApplyHandler{}, nil + + case dao.StoreSubmitReview: + return StoreNotifySubmitHandler{}, nil + + case dao.StoreFirstReview: + return StoreNotifyFirstHandler{}, nil + + case dao.StoreSecondReview: + return StoreNotifySecondHandler{}, nil + } + // 地址 + case dao.EditLogTypeAddress: + switch job.Type { + + case dao.AddressApplyReview: + return AddressReviewHandler{}, nil + + case dao.AddressSubmitReview: + return AddressNotifySubmitHandler{}, nil + + case dao.AddressFirstReview: + return AddressNotifyFirstHandler{}, nil + + case dao.AddressSecondReview: + return AddressNotifySecondHandler{}, nil + } + case dao.EditLogTypeSales: + switch job.Type { + + case dao.SalesApplyReview: + return SalesNotifyApplyHandler{}, nil + } + } + + return nil, errors.New("wecom handler not found") +} diff --git a/services/wecom/internal/logic/message/handlers/notify_address_first.go b/services/wecom/internal/logic/message/handlers/notify_address_first.go new file mode 100644 index 0000000..e497c64 --- /dev/null +++ b/services/wecom/internal/logic/message/handlers/notify_address_first.go @@ -0,0 +1,49 @@ +package handlers + +import ( + "base-service/app/dao" + "base-service/app/models" + "base-service/app/services/wecom/message/builder" + util "base-service/app/services/wecom/message/utils" + "base-service/pkg/wecom/message" + "base-service/utils" + "strconv" +) + +type AddressNotifyFirstHandler struct { +} + +func (h AddressNotifyFirstHandler) Build(job dao.WeComJob) ([]*message.SendRequest, error) { + + logInfo := dao.EditLogInfo{} + + models.EditLogModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "obj_id": strconv.Itoa(job.ObjId), + "obj_type": strconv.Itoa(int(job.ObjType)), + }, + Order: "id desc", + }, &logInfo) + + content := util.BuildContent( + struct{ Key, Value string }{"门店名称", getTypeNameFromContent(logInfo.Content)}, + struct{ Key, Value string }{"提醒消息", "您有一个地址需要审核"}, + ) + + crad := builder.TextCardParams{ + ToUser: utils.GetConfigString("wecom.address_notify_second_ids"), + Desc: "地址通知", + Title: "地址审核通知", + Content: content, + } + + req, err := builder.TextCard(crad) + + if err != nil { + return nil, err + } + + return []*message.SendRequest{ + req, + }, nil +} diff --git a/services/wecom/internal/logic/message/handlers/notify_address_second.go b/services/wecom/internal/logic/message/handlers/notify_address_second.go new file mode 100644 index 0000000..e29dd19 --- /dev/null +++ b/services/wecom/internal/logic/message/handlers/notify_address_second.go @@ -0,0 +1,77 @@ +package handlers + +import ( + "base-service/app/dao" + "base-service/app/models" + "base-service/app/services/wecom/message/builder" + "base-service/pkg/wecom/message" + "base-service/utils" + "fmt" + "strconv" +) + +type AddressNotifySecondHandler struct { +} + +func (h AddressNotifySecondHandler) Build(job dao.WeComJob) ([]*message.SendRequest, error) { + + logInfo := dao.EditLogInfo{} + + models.EditLogModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "obj_id": strconv.Itoa(job.ObjId), + "obj_type": strconv.Itoa(int(job.ObjType)), + }, + Order: "id desc", + }, &logInfo) + + if logInfo.Id == dao.NumberZero { + return nil, fmt.Errorf("edit log not found") + } + + // 需要返回的字段 + fields := []dao.FieldConfig{ + { + Field: "type_name", + Label: "门店名称", + }, + { + Field: "name", + Label: "收货人", + }, + { + Field: "address", + Label: "地址", + }, + { + Field: "district", + Label: "地区", + }, + { + Field: "mobile", + Label: "收货人电话", + }, + } + + content, err := BuildAddressDiff(logInfo.Content, fields) + if err != nil { + return nil, err + } + + crad := builder.TextCardParams{ + ToUser: utils.GetConfigString("wecom.address_reviewer_ids"), + Desc: "地址通知", + Title: "地址审核通知", + Content: content, + } + + req, err := builder.TextCard(crad) + + if err != nil { + return nil, err + } + + return []*message.SendRequest{ + req, + }, nil +} diff --git a/services/wecom/internal/logic/message/handlers/notify_address_submit.go b/services/wecom/internal/logic/message/handlers/notify_address_submit.go new file mode 100644 index 0000000..c0b62d9 --- /dev/null +++ b/services/wecom/internal/logic/message/handlers/notify_address_submit.go @@ -0,0 +1,76 @@ +package handlers + +import ( + "base-service/app/dao" + "base-service/app/models" + "base-service/app/services/wecom/message/builder" + util "base-service/app/services/wecom/message/utils" + "base-service/pkg/wecom/message" + "base-service/utils" + "strconv" + + jsoniter "github.com/json-iterator/go" +) + +type AddressNotifySubmitHandler struct { +} + +func (h AddressNotifySubmitHandler) Build(job dao.WeComJob) ([]*message.SendRequest, error) { + + logInfo := dao.EditLogInfo{} + + models.EditLogModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "obj_id": strconv.Itoa(job.ObjId), + "obj_type": strconv.Itoa(int(job.ObjType)), + }, + Order: "id desc", + }, &logInfo) + + content := util.BuildContent( + struct{ Key, Value string }{"门店名称", getTypeNameFromContent(logInfo.Content)}, + struct{ Key, Value string }{"提醒消息", "您有一个地址需要审核"}, + ) + + crad := builder.TextCardParams{ + ToUser: utils.GetConfigString("wecom.address_notify_first_ids"), + Desc: "地址通知", + Title: "地址审核通知", + Content: content, + } + + req, err := builder.TextCard(crad) + + if err != nil { + return nil, err + } + + return []*message.SendRequest{ + req, + }, nil +} + +func getTypeNameFromContent(content string) string { + if content == "" { + return "" + } + + var data struct { + Old map[string]any `json:"old"` + New map[string]any `json:"new"` + } + + if err := jsoniter.Unmarshal([]byte(content), &data); err != nil { + return "" + } + + if v, ok := data.New["type_name"].(string); ok && v != "" { + return v + } + + if v, ok := data.Old["type_name"].(string); ok && v != "" { + return v + } + + return "" +} diff --git a/services/wecom/internal/logic/message/handlers/notify_product_apply.go b/services/wecom/internal/logic/message/handlers/notify_product_apply.go new file mode 100644 index 0000000..d3d6c3c --- /dev/null +++ b/services/wecom/internal/logic/message/handlers/notify_product_apply.go @@ -0,0 +1,64 @@ +package handlers + +import ( + "base-service/app/dao" + "base-service/app/models" + "base-service/app/services/wecom/message/builder" + util "base-service/app/services/wecom/message/utils" + "base-service/pkg/wecom/message" + "strconv" +) + +type ProductNotifyApplyHandler struct { +} + +func (h ProductNotifyApplyHandler) Build(job dao.WeComJob) ([]*message.SendRequest, error) { + + product := dao.ProductInfo{} + models.ProductModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "id": strconv.Itoa(job.ObjId), + }, + }, &product) + + var ( + toUser string + content []message.HorizontalContent + ) + + user, err := util.GetActionWecomUser(job.ObjId, job.ObjType) + toUser = user.WecomUserId + if err != nil { + return nil, err + } + + switch job.Action { + case dao.ActionPass: + + content = util.BuildContent( + struct{ Key, Value string }{"产品名称", product.Name}, + struct{ Key, Value string }{"审核状态", "已通过"}, + ) + + case dao.ActionReject: + content = util.BuildContent( + struct{ Key, Value string }{"产品名称", product.Name}, + struct{ Key, Value string }{"审核状态", "未通过"}, + struct{ Key, Value string }{"拒绝原因", util.GetExtString(job.Ext, "reason")}, + ) + } + card := builder.TextCardParams{ + ToUser: toUser, + Desc: "产品通知", + Title: "产品审核通知", + Content: content, + } + + req, err := builder.TextCard(card) + + if err != nil { + return nil, err + } + + return []*message.SendRequest{req}, nil +} diff --git a/services/wecom/internal/logic/message/handlers/notify_product_first.go b/services/wecom/internal/logic/message/handlers/notify_product_first.go new file mode 100644 index 0000000..b547cb3 --- /dev/null +++ b/services/wecom/internal/logic/message/handlers/notify_product_first.go @@ -0,0 +1,66 @@ +package handlers + +import ( + "base-service/app/dao" + "base-service/app/models" + "base-service/app/services/wecom/message/builder" + util "base-service/app/services/wecom/message/utils" + "base-service/pkg/wecom/message" + "base-service/utils" + "strconv" +) + +type ProductNotifyFirstHandler struct { +} + +func (h ProductNotifyFirstHandler) Build(job dao.WeComJob) ([]*message.SendRequest, error) { + + product := dao.ProductInfo{} + models.ProductModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "id": strconv.Itoa(job.ObjId), + }, + }, &product) + + var ( + toUser string + content []message.HorizontalContent + ) + + switch job.Action { + case dao.ActionPass: + toUser = utils.GetConfigString("wecom.product_notify_second_ids") + content = util.BuildContent( + struct{ Key, Value string }{"产品名称", product.Name}, + struct{ Key, Value string }{"提醒消息", "您有一个产品需要审核"}, + ) + + case dao.ActionReject: + user, err := util.GetActionWecomUser(job.ObjId, job.ObjType) + + if err != nil { + return nil, err + } + + toUser = user.WecomUserId + content = util.BuildContent( + struct{ Key, Value string }{"产品名称", product.Name}, + struct{ Key, Value string }{"审核状态", "未通过"}, + struct{ Key, Value string }{"拒绝原因", util.GetExtString(job.Ext, "reason")}, + ) + } + card := builder.TextCardParams{ + ToUser: toUser, + Desc: "产品通知", + Title: "产品审核通知", + Content: content, + } + + req, err := builder.TextCard(card) + + if err != nil { + return nil, err + } + + return []*message.SendRequest{req}, nil +} diff --git a/services/wecom/internal/logic/message/handlers/notify_product_ordinary.go b/services/wecom/internal/logic/message/handlers/notify_product_ordinary.go new file mode 100644 index 0000000..e67c14c --- /dev/null +++ b/services/wecom/internal/logic/message/handlers/notify_product_ordinary.go @@ -0,0 +1,49 @@ +package handlers + +import ( + "base-service/app/dao" + "base-service/app/models" + util "base-service/app/services/wecom/message/utils" + "base-service/pkg/wecom/message" + "base-service/utils" + "strconv" +) + +type ProductNotifyOrdinaryHandler struct { +} + +func (h ProductNotifyOrdinaryHandler) Build(job dao.WeComJob) ([]*message.SendRequest, error) { + + product := dao.ProductInfo{} + + models.ProductModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "id": strconv.Itoa(job.ObjId), + }, + }, &product) + + user, err := util.GetApplicantUser(job.ObjId, job.ObjType, dao.NumberTwo) + + if err != nil { + return nil, err + } + + req1, err := toProductReviewer(utils.GetConfigString("wecom.product_reviewer_ids"), job) + if err != nil { + return nil, err + } + reqs := []*message.SendRequest{ + req1, + } + + if user.Id > dao.NumberZero { + req2, err := toProductReviewer(user.WecomUserId, job) + if err != nil { + return nil, err + } + + reqs = append(reqs, req2) + } + + return reqs, nil +} diff --git a/services/wecom/internal/logic/message/handlers/notify_product_second.go b/services/wecom/internal/logic/message/handlers/notify_product_second.go new file mode 100644 index 0000000..20fb7c0 --- /dev/null +++ b/services/wecom/internal/logic/message/handlers/notify_product_second.go @@ -0,0 +1,261 @@ +package handlers + +import ( + "base-service/app/dao" + "base-service/app/models" + "base-service/app/services/wecom/message/builder" + util "base-service/app/services/wecom/message/utils" + "base-service/pkg/wecom/message" + "base-service/utils" + "encoding/json" + "fmt" + "strconv" +) + +type ProductNotifySecondHandler struct { +} + +func (h ProductNotifySecondHandler) Build(job dao.WeComJob) ([]*message.SendRequest, error) { + + product := dao.ProductInfo{} + + models.ProductModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "id": strconv.Itoa(job.ObjId), + }, + }, &product) + + user, err := util.GetApplicantUser(job.ObjId, job.ObjType, dao.NumberTwo) + + if err != nil { + return nil, err + } + + switch job.Action { + + case dao.ActionPass: + + req1, err := toProductReviewer(utils.GetConfigString("wecom.product_reviewer_ids"), job) + if err != nil { + return nil, err + } + reqs := []*message.SendRequest{ + req1, + } + + if user.Id > dao.NumberZero { + req2, err := toProductApply(user.WecomUserId, product) + if err != nil { + return nil, err + } + + reqs = append(reqs, req2) + } + + return reqs, nil + case dao.ActionReject: + + content := util.BuildContent( + struct{ Key, Value string }{"产品名称", product.Name}, + struct{ Key, Value string }{"审核状态", "未通过"}, + struct{ Key, Value string }{"拒绝原因", util.GetExtString(job.Ext, "reason")}, + ) + + if user.Id > dao.NumberZero { + card := builder.TextCardParams{ + ToUser: user.WecomUserId, + Desc: "产品通知", + Title: "产品审核通知", + Content: content, + } + req, err := builder.TextCard(card) + + if err != nil { + return nil, err + } + + return []*message.SendRequest{ + req, + }, nil + } + } + return nil, nil +} + +func toProductApply(toUser string, product dao.ProductInfo) (*message.SendRequest, error) { + + content := util.BuildContent( + struct{ Key, Value string }{"产品名称", product.Name}, + struct{ Key, Value string }{"审核状态", "已通过"}, + ) + card := builder.TextCardParams{ + ToUser: toUser, + Desc: "产品通知", + Title: "产品审核通知", + Content: content, + } + req, err := builder.TextCard(card) + + if err != nil { + return nil, err + } + return req, nil +} + +func toProductReviewer(toUser string, job dao.WeComJob) (*message.SendRequest, error) { + + logInfo := dao.EditLogInfo{} + + models.EditLogModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "obj_id": strconv.Itoa(job.ObjId), + "obj_type": strconv.Itoa(dao.EditLogTypeProduct), + }, + Order: "id desc", + }, &logInfo) + + if logInfo.Id == dao.NumberZero { + return nil, fmt.Errorf("edit log not found") + } + + title := "产品消息通知" + + diffList, err := buildProductDiff(logInfo.Content) + if err != nil { + return nil, err + } + + if len(diffList) > 5 { + diffList = diffList[:5] + title += "(修改内容过多已自动截取)" + } + + diffList = append(diffList, message.HorizontalContent{ + KeyName: "审核时间", + Value: utils.Now().Format(), + }) + + card := builder.TextCardParams{ + ToUser: toUser, + Desc: "产品通知", + Title: title, + Content: diffList, + } + req, err := builder.TextCard(card) + + if err != nil { + return nil, err + } + return req, nil +} + +func buildProductDiff(content string) ([]message.HorizontalContent, error) { + + fields1 := []dao.FieldConfig{ + { + Field: "agent_price", + Label: "代理提成", + }, + { + Field: "price", + Label: "产品价格", + }, + { + Field: "store_price", + Label: "门店价格", + }, + { + Field: "sale_price", + Label: "销售底价", + }, + { + Field: "share_price", + Label: "抽成金额", + }, + } + + fields2 := []dao.FieldConfig{ + { + Field: "name", + Label: "产品名称", + }, + { + Field: "period_validity", + Label: "有效期", + }, + { + Field: "subhead", + Label: "副标题", + }, + { + Field: "weight", + Label: "重量", + }, + { + Field: "cubage", + Label: "体积", + }, + { + Field: "content", + Label: "产品描述", + }, + } + + var data struct { + Old map[string]interface{} `json:"old"` + New map[string]interface{} `json:"new"` + } + + if err := json.Unmarshal([]byte(content), &data); err != nil { + return nil, err + } + + fields := fields2 + + if _, ok := data.New["model_code"]; ok { + fields = fields1 + } + + result := make([]message.HorizontalContent, 0) + + for _, field := range fields { + + if len(result) >= 6 { + break + } + + oldValue := util.GetMapString(data.Old, field.Field) + newValue := util.GetMapString(data.New, field.Field) + + if oldValue == newValue { + continue + } + if field.Field == "content" { + + result = append(result, message.HorizontalContent{ + KeyName: "产品描述", + Value: "产品描述进行了修改", + }) + + continue + } + value := newValue + + if oldValue != dao.StringEmpty && + oldValue != dao.StringZero { + + value = fmt.Sprintf( + "%s → %s", + oldValue, + newValue, + ) + } + + result = append(result, message.HorizontalContent{ + KeyName: field.Label, + Value: value, + }) + } + + return result, nil +} diff --git a/services/wecom/internal/logic/message/handlers/notify_product_submit.go b/services/wecom/internal/logic/message/handlers/notify_product_submit.go new file mode 100644 index 0000000..f325e87 --- /dev/null +++ b/services/wecom/internal/logic/message/handlers/notify_product_submit.go @@ -0,0 +1,50 @@ +package handlers + +import ( + "base-service/app/dao" + "base-service/app/models" + "base-service/app/services/wecom/message/builder" + util "base-service/app/services/wecom/message/utils" + "base-service/pkg/wecom/message" + "base-service/utils" + "strconv" +) + +type ProductNotifySubmitHandler struct { +} + +func (h ProductNotifySubmitHandler) Build(job dao.WeComJob) ([]*message.SendRequest, error) { + + product := dao.ProductInfo{} + models.ProductModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "id": strconv.Itoa(job.ObjId), + }, + }, &product) + + var ( + toUser string + content []message.HorizontalContent + ) + + toUser = utils.GetConfigString("wecom.product_notify_first_ids") + content = util.BuildContent( + struct{ Key, Value string }{"产品名称", product.Name}, + struct{ Key, Value string }{"提醒消息", "您有一个产品需要审核"}, + ) + + card := builder.TextCardParams{ + ToUser: toUser, + Desc: "产品通知", + Title: "产品审核通知", + Content: content, + } + + req, err := builder.TextCard(card) + + if err != nil { + return nil, err + } + + return []*message.SendRequest{req}, nil +} diff --git a/services/wecom/internal/logic/message/handlers/notify_sales_apply.go b/services/wecom/internal/logic/message/handlers/notify_sales_apply.go new file mode 100644 index 0000000..eb9b220 --- /dev/null +++ b/services/wecom/internal/logic/message/handlers/notify_sales_apply.go @@ -0,0 +1,44 @@ +package handlers + +import ( + "base-service/app/dao" + "base-service/app/models" + "base-service/app/services/wecom/message/builder" + util "base-service/app/services/wecom/message/utils" + "base-service/pkg/wecom/message" + "base-service/utils" + "strconv" +) + +type SalesNotifyApplyHandler struct { +} + +func (h SalesNotifyApplyHandler) Build(job dao.WeComJob) ([]*message.SendRequest, error) { + + sales := dao.SaleInfo{} + models.SalesModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "id": strconv.Itoa(job.ObjId), + }, + }, &sales) + + content := util.BuildContent( + struct{ Key, Value string }{"销售名称", sales.Name}, + struct{ Key, Value string }{"提醒消息", "您有一个销售需要审核"}, + ) + + card := builder.TextCardParams{ + ToUser: utils.GetConfigString("wecom.sales_notify_apply_ids"), + Desc: "销售通知", + Title: "销售审核通知", + Content: content, + } + + req, err := builder.TextCard(card) + + if err != nil { + return nil, err + } + + return []*message.SendRequest{req}, nil +} diff --git a/services/wecom/internal/logic/message/handlers/notify_store_apply.go b/services/wecom/internal/logic/message/handlers/notify_store_apply.go new file mode 100644 index 0000000..5faeeb6 --- /dev/null +++ b/services/wecom/internal/logic/message/handlers/notify_store_apply.go @@ -0,0 +1,64 @@ +package handlers + +import ( + "base-service/app/dao" + "base-service/app/models" + "base-service/app/services/wecom/message/builder" + util "base-service/app/services/wecom/message/utils" + "base-service/pkg/wecom/message" + "strconv" +) + +type StoreNotifyApplyHandler struct { +} + +func (h StoreNotifyApplyHandler) Build(job dao.WeComJob) ([]*message.SendRequest, error) { + + store := dao.StoreInfo{} + models.StoreModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "id": strconv.Itoa(job.ObjId), + }, + }, &store) + + var ( + toUser string + content []message.HorizontalContent + ) + + user, err := util.GetActionWecomUser(job.ObjId, job.ObjType) + toUser = user.WecomUserId + if err != nil { + return nil, err + } + switch job.Action { + case dao.ActionPass: + + content = util.BuildContent( + struct{ Key, Value string }{"门店名称", store.Name}, + struct{ Key, Value string }{"审核状态", "已通过"}, + ) + + case dao.ActionReject: + + content = util.BuildContent( + struct{ Key, Value string }{"门店名称", store.Name}, + struct{ Key, Value string }{"审核状态", "未通过"}, + struct{ Key, Value string }{"拒绝原因", util.GetExtString(job.Ext, "reason")}, + ) + } + card := builder.TextCardParams{ + ToUser: toUser, + Desc: "门店通知", + Title: "门店审核通知", + Content: content, + } + + req, err := builder.TextCard(card) + + if err != nil { + return nil, err + } + + return []*message.SendRequest{req}, nil +} diff --git a/services/wecom/internal/logic/message/handlers/notify_store_first.go b/services/wecom/internal/logic/message/handlers/notify_store_first.go new file mode 100644 index 0000000..44fb909 --- /dev/null +++ b/services/wecom/internal/logic/message/handlers/notify_store_first.go @@ -0,0 +1,67 @@ +package handlers + +import ( + "base-service/app/dao" + "base-service/app/models" + "base-service/app/services/wecom/message/builder" + util "base-service/app/services/wecom/message/utils" + "base-service/pkg/wecom/message" + "base-service/utils" + "strconv" +) + +type StoreNotifyFirstHandler struct { +} + +func (h StoreNotifyFirstHandler) Build(job dao.WeComJob) ([]*message.SendRequest, error) { + + store := dao.StoreInfo{} + models.StoreModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "id": strconv.Itoa(job.ObjId), + }, + }, &store) + + var ( + toUser string + content []message.HorizontalContent + ) + + switch job.Action { + case dao.ActionPass: + toUser = utils.GetConfigString("wecom.store_notify_second_ids") + content = util.BuildContent( + struct{ Key, Value string }{"门店名称", store.Name}, + struct{ Key, Value string }{"提醒消息", "您有一个门店需要审核"}, + ) + + case dao.ActionReject: + user, err := util.GetActionWecomUser(job.ObjId, job.ObjType) + + if err != nil { + return nil, err + } + + toUser = user.WecomUserId + + content = util.BuildContent( + struct{ Key, Value string }{"门店名称", store.Name}, + struct{ Key, Value string }{"审核状态", "未通过"}, + struct{ Key, Value string }{"拒绝原因", util.GetExtString(job.Ext, "reason")}, + ) + } + card := builder.TextCardParams{ + ToUser: toUser, + Desc: "门店通知", + Title: "门店审核通知", + Content: content, + } + + req, err := builder.TextCard(card) + + if err != nil { + return nil, err + } + + return []*message.SendRequest{req}, nil +} diff --git a/services/wecom/internal/logic/message/handlers/notify_store_second.go b/services/wecom/internal/logic/message/handlers/notify_store_second.go new file mode 100644 index 0000000..3408e70 --- /dev/null +++ b/services/wecom/internal/logic/message/handlers/notify_store_second.go @@ -0,0 +1,220 @@ +package handlers + +import ( + "base-service/app/dao" + "base-service/app/models" + "base-service/app/services/wecom/message/builder" + util "base-service/app/services/wecom/message/utils" + "base-service/pkg/wecom/message" + "base-service/utils" + "encoding/json" + "fmt" + "strconv" +) + +type StoreNotifySecondHandler struct { +} + +func (h StoreNotifySecondHandler) Build(job dao.WeComJob) ([]*message.SendRequest, error) { + + store := dao.StoreInfo{} + + models.StoreModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "id": strconv.Itoa(job.ObjId), + }, + }, &store) + + user, err := util.GetApplicantUser(job.ObjId, job.ObjType, dao.NumberTwo) + + if err != nil { + return nil, err + } + + switch job.Action { + + case dao.ActionPass: + + req1, err := toStoreReviewer(utils.GetConfigString("wecom.store_reviewer_ids"), job) + if err != nil { + return nil, err + } + reqs := []*message.SendRequest{ + req1, + } + + if user.Id > dao.NumberZero { + req2, err := toStoreApply(user.WecomUserId, store) + if err != nil { + return nil, err + } + + reqs = append(reqs, req2) + } + + return reqs, nil + case dao.ActionReject: + + content := util.BuildContent( + struct{ Key, Value string }{"门店名称", store.Name}, + struct{ Key, Value string }{"审核状态", "未通过"}, + struct{ Key, Value string }{"拒绝原因", util.GetExtString(job.Ext, "reason")}, + ) + + if user.Id > dao.NumberZero { + card := builder.TextCardParams{ + ToUser: user.WecomUserId, + Desc: "门店通知", + Title: "门店审核通知", + Content: content, + } + req, err := builder.TextCard(card) + + if err != nil { + return nil, err + } + + return []*message.SendRequest{ + req, + }, nil + } + } + return nil, nil +} + +func toStoreApply(toUser string, store dao.StoreInfo) (*message.SendRequest, error) { + + content := util.BuildContent( + struct{ Key, Value string }{"门店名称", store.Name}, + struct{ Key, Value string }{"审核状态", "已通过"}, + ) + card := builder.TextCardParams{ + ToUser: toUser, + Desc: "门店通知", + Title: "门店审核通知", + Content: content, + } + req, err := builder.TextCard(card) + + if err != nil { + return nil, err + } + return req, nil +} + +func toStoreReviewer(toUser string, job dao.WeComJob) (*message.SendRequest, error) { + + logInfo := dao.EditLogInfo{} + + models.EditLogModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "obj_id": strconv.Itoa(job.ObjId), + "obj_type": strconv.Itoa(dao.EditLogTypeStore), + }, + Order: "id desc", + }, &logInfo) + + if logInfo.Id == dao.NumberZero { + return nil, fmt.Errorf("edit log not found") + } + + title := "门店消息通知" + + diffList, err := buildStoreDiff(logInfo.Content) + if err != nil { + return nil, err + } + + if len(diffList) > 5 { + diffList = diffList[:5] + title += "(修改内容过多已自动截取)" + } + + diffList = append(diffList, message.HorizontalContent{ + KeyName: "审核时间", + Value: utils.Now().Format(), + }) + + card := builder.TextCardParams{ + ToUser: toUser, + Desc: "门店通知", + Title: title, + Content: diffList, + } + req, err := builder.TextCard(card) + + if err != nil { + return nil, err + } + return req, nil +} + +func buildStoreDiff(content string) ([]message.HorizontalContent, error) { + + fields := []dao.FieldConfig{ + { + Field: "name", + Label: "门店名称", + }, + { + Field: "user_name", + Label: "负责人", + }, + { + Field: "store_price", + Label: "门店价格", + }, + { + Field: "sale_price", + Label: "销售底价", + }, + { + Field: "share_price", + Label: "抽成金额", + }, + } + + var data struct { + Old map[string]interface{} `json:"old"` + New map[string]interface{} `json:"new"` + } + + if err := json.Unmarshal([]byte(content), &data); err != nil { + return nil, err + } + + result := make([]message.HorizontalContent, 0) + + for _, field := range fields { + + if len(result) >= 6 { + break + } + + oldValue := util.GetMapString(data.Old, field.Field) + newValue := util.GetMapString(data.New, field.Field) + + if oldValue == newValue { + continue + } + + value := newValue + + if oldValue != dao.StringEmpty && + oldValue != dao.StringZero { + + value = fmt.Sprintf( + "%s → %s", + oldValue, + newValue, + ) + } + + result = append(result, message.HorizontalContent{ + KeyName: field.Label, + Value: value, + }) + } + + return result, nil +} diff --git a/services/wecom/internal/logic/message/handlers/notify_store_submit.go b/services/wecom/internal/logic/message/handlers/notify_store_submit.go new file mode 100644 index 0000000..c7c6b25 --- /dev/null +++ b/services/wecom/internal/logic/message/handlers/notify_store_submit.go @@ -0,0 +1,51 @@ +package handlers + +import ( + "base-service/app/dao" + "base-service/app/models" + "base-service/app/services/wecom/message/builder" + util "base-service/app/services/wecom/message/utils" + "base-service/pkg/wecom/message" + "base-service/utils" + "strconv" +) + +type StoreNotifySubmitHandler struct { +} + +func (h StoreNotifySubmitHandler) Build(job dao.WeComJob) ([]*message.SendRequest, error) { + + store := dao.StoreInfo{} + models.StoreModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "id": strconv.Itoa(job.ObjId), + }, + }, &store) + + var ( + toUser string + content []message.HorizontalContent + ) + + toUser = utils.GetConfigString("wecom.store_notify_first_ids") + + content = util.BuildContent( + struct{ Key, Value string }{"门店名称", store.Name}, + struct{ Key, Value string }{"提醒消息", "您有一个门店需要审核"}, + ) + + card := builder.TextCardParams{ + ToUser: toUser, + Desc: "门店通知", + Title: "门店审核通知", + Content: content, + } + + req, err := builder.TextCard(card) + + if err != nil { + return nil, err + } + + return []*message.SendRequest{req}, nil +} diff --git a/services/wecom/internal/logic/message/handlers/review_address.go b/services/wecom/internal/logic/message/handlers/review_address.go new file mode 100644 index 0000000..1bea656 --- /dev/null +++ b/services/wecom/internal/logic/message/handlers/review_address.go @@ -0,0 +1,191 @@ +package handlers + +import ( + "base-service/app/dao" + "base-service/app/models" + "base-service/app/services/wecom/message/builder" + "base-service/app/services/wecom/message/task" + util "base-service/app/services/wecom/message/utils" + "base-service/pkg/wecom/message" + "base-service/utils" + "encoding/json" + "fmt" + "strconv" + + "github.com/google/uuid" +) + +type AddressReviewHandler struct { +} + +func (h AddressReviewHandler) Build(job dao.WeComJob) ([]*message.SendRequest, error) { + + logInfo := dao.EditLogInfo{} + + models.EditLogModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "obj_id": strconv.Itoa(job.ObjId), + "obj_type": strconv.Itoa(int(job.ObjType)), + }, + Order: "id desc", + }, &logInfo) + + if logInfo.Id == dao.NumberZero { + return nil, fmt.Errorf("edit log not found") + } + + // 需要返回的字段 + fields := []dao.FieldConfig{ + { + Field: "type_name", + Label: "门店名称", + }, + { + Field: "name", + Label: "收货人", + }, + { + Field: "address", + Label: "地址", + }, + { + Field: "district", + Label: "地区", + }, + { + Field: "mobile", + Label: "收货人电话", + }, + } + + content, err := BuildAddressDiff(logInfo.Content, fields) + if err != nil { + return nil, err + } + + toUser := utils.GetConfigString("wecom.address_reviewer_ids") + + taskId := uuid.NewString() + + detailUrl := fmt.Sprintf("https://baidu.com/xxx/review?id=%d", job.ObjId) + + err = task.Service{}.Create(job, taskId, detailUrl) + + if err != nil { + return nil, err + } + + req, err := builder.ButtonCard( + builder.ButtonCardParams{ + ToUser: toUser, + TaskId: taskId, + Title: "地址信息变更审核", + Content: content, + Buttons: []message.Button{ + { + Text: "拒绝", + Style: dao.NumberTwo, + Key: fmt.Sprintf("%s:reject:%s", util.GetExtString(job.Ext, "key"), taskId), + }, + { + Text: "通过", + Style: dao.NumberOne, + Key: fmt.Sprintf("%s:agree:%s", util.GetExtString(job.Ext, "key"), taskId), + }, + }, + }, + ) + + if err != nil { + return nil, err + } + + return []*message.SendRequest{ + req, + }, nil +} + +func BuildAddressDiff(content string, fields []dao.FieldConfig) ([]message.HorizontalContent, error) { + + var data struct { + Old map[string]interface{} `json:"old"` + New map[string]interface{} `json:"new"` + } + + if err := json.Unmarshal([]byte(content), &data); err != nil { + return nil, err + } + + result := make([]message.HorizontalContent, 0) + + districtOld := util.GetMapString(data.Old, "district") + districtNew := util.GetMapString(data.New, "district") + + for _, field := range fields { + + // district 不单独显示 + if field.Field == "district" { + continue + } + + oldValue := util.GetMapString(data.Old, field.Field) + newValue := util.GetMapString(data.New, field.Field) + + // type_name 新数据可能不存在 + if field.Field == "type_name" && newValue == dao.StringEmpty { + newValue = oldValue + } + + // 地址拼接地区 + if field.Field == "address" { + + if districtOld != dao.StringEmpty { + oldValue = districtOld + oldValue + } + + if districtNew != dao.StringEmpty { + newValue = districtNew + newValue + } + } + + // old 数据,手机号解密 + if field.Field == "mobile" { + + if oldValue != dao.StringEmpty { + + decryptOld, err := utils.Decrypt(oldValue) + if err == nil { + oldValue = decryptOld + } + } + + // if newValue != dao.StringEmpty { + + // decryptNew, err := utils.Decrypt(newValue) + // if err == nil { + // newValue = decryptNew + // } + // } + } + + value := newValue + + if oldValue != dao.StringEmpty && + oldValue != dao.StringZero && + oldValue != newValue { + + value = fmt.Sprintf( + "%s → %s", + oldValue, + newValue, + ) + } + + result = append(result, message.HorizontalContent{ + KeyName: field.Label, + Value: value, + }) + } + + return result, nil +} diff --git a/services/wecom/internal/logic/message/handlers/review_product.go b/services/wecom/internal/logic/message/handlers/review_product.go new file mode 100644 index 0000000..4ef092f --- /dev/null +++ b/services/wecom/internal/logic/message/handlers/review_product.go @@ -0,0 +1,89 @@ +package handlers + +import ( + "base-service/app/dao" + "base-service/app/models" + "base-service/app/services/wecom/message/builder" + "base-service/app/services/wecom/message/task" + util "base-service/app/services/wecom/message/utils" + "base-service/pkg/wecom/message" + "base-service/utils" + "fmt" + "strconv" + + "github.com/google/uuid" +) + +type ProductReviewHandler struct { +} + +func (h ProductReviewHandler) Build(job dao.WeComJob) ([]*message.SendRequest, error) { + + product := dao.ProductInfo{} + models.ProductModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "id": strconv.Itoa(job.ObjId), + }, + }, &product) + + user, err := util.GetActionUser(job.ObjId, job.ObjType) + if err != nil { + return nil, err + } + + taskId := uuid.NewString() + + detailUrl := fmt.Sprintf("https://baidu.com/xxx/review?id=%d", job.ObjId) + + err = task.Service{}.Create(job, taskId, detailUrl) + + content := []message.HorizontalContent{ + { + KeyName: "产品名称", + Value: product.Name, + }, + { + KeyName: "申请原因", + Value: util.GetExtString(job.Ext, "reason"), + }, + { + KeyName: "申请人", + Value: user.Name, + }, + { + KeyName: "申请时间", + Value: utils.Now().Format(), + }, + } + + toUser := utils.GetConfigString("wecom.product_reviewer_ids") + req, err := builder.ButtonCard( + builder.ButtonCardParams{ + ToUser: toUser, + TaskId: taskId, + Desc: "产品审核", + Title: "产品信息变更审核", + Content: content, + Buttons: []message.Button{ + { + Text: "拒绝", + Style: dao.NumberTwo, + Key: fmt.Sprintf("%s:reject:%s", util.GetExtString(job.Ext, "key"), taskId), + }, + { + Text: "通过", + Style: dao.NumberOne, + Key: fmt.Sprintf("%s:agree:%s", util.GetExtString(job.Ext, "key"), taskId), + }, + }, + }, + ) + + if err != nil { + return nil, err + } + + return []*message.SendRequest{ + req, + }, nil +} diff --git a/services/wecom/internal/logic/message/handlers/review_store.go b/services/wecom/internal/logic/message/handlers/review_store.go new file mode 100644 index 0000000..5b85b74 --- /dev/null +++ b/services/wecom/internal/logic/message/handlers/review_store.go @@ -0,0 +1,91 @@ +package handlers + +import ( + "base-service/app/dao" + "base-service/app/models" + "base-service/app/services/wecom/message/builder" + "base-service/app/services/wecom/message/task" + util "base-service/app/services/wecom/message/utils" + "base-service/pkg/wecom/message" + "base-service/utils" + "fmt" + "strconv" + + "github.com/google/uuid" +) + +type StoreReviewHandler struct { +} + +func (h StoreReviewHandler) Build(job dao.WeComJob) ([]*message.SendRequest, error) { + + store := dao.StoreInfo{} + + models.StoreModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "id": strconv.Itoa(job.ObjId), + }, + }, &store) + + user, err := util.GetActionUser(job.ObjId, job.ObjType) + if err != nil { + return nil, err + } + + content := []message.HorizontalContent{ + { + KeyName: "门店名称", + Value: store.Name, + }, + { + KeyName: "申请原因", + Value: util.GetExtString(job.Ext, "reason"), + }, + { + KeyName: "申请人", + Value: user.Name, + }, + { + KeyName: "申请时间", + Value: utils.Now().Format(), + }, + } + + taskId := uuid.NewString() + + detailUrl := fmt.Sprintf("https://baidu.com/xxx/review?id=%d", job.ObjId) + + err = task.Service{}.Create(job, taskId, detailUrl) + + toUser := utils.GetConfigString("wecom.store_reviewer_ids") + + req, err := builder.ButtonCard( + builder.ButtonCardParams{ + ToUser: toUser, + TaskId: taskId, + Desc: "门店审核", + Title: "门店信息变更审核", + Content: content, + Buttons: []message.Button{ + { + Text: "拒绝", + Style: dao.NumberTwo, + Key: fmt.Sprintf("%s:reject:%s", util.GetExtString(job.Ext, "key"), taskId), + }, + { + Text: "通过", + Style: dao.NumberOne, + Key: fmt.Sprintf("%s:agree:%s", util.GetExtString(job.Ext, "key"), taskId), + }, + }, + }, + ) + + if err != nil { + return nil, err + } + + return []*message.SendRequest{ + req, + }, nil +} diff --git a/services/wecom/internal/logic/message/sender/sender.go b/services/wecom/internal/logic/message/sender/sender.go new file mode 100644 index 0000000..b6b98a1 --- /dev/null +++ b/services/wecom/internal/logic/message/sender/sender.go @@ -0,0 +1,38 @@ +package sender + +import ( + "base-service/app/dao" + "base-service/app/services/wecom/client" + "base-service/app/services/wecom/message/handlers" + "context" +) + +type WecomSenderService struct { +} + +func (s WecomSenderService) Send(job dao.WeComJob) error { + + handler, err := handlers.GetHandler(job) + if err != nil { + return err + } + + reqs, err := handler.Build(job) + if err != nil { + return err + } + + messageClient := client.NewMessageClient(job.Type) + for _, req := range reqs { + + if req == nil || req.ToUser == dao.StringEmpty { + continue + } + + if err = messageClient.Send(context.Background(), req); err != nil { + return err + } + } + + return nil +} diff --git a/services/wecom/internal/logic/message/task/task.go b/services/wecom/internal/logic/message/task/task.go new file mode 100644 index 0000000..ffbb653 --- /dev/null +++ b/services/wecom/internal/logic/message/task/task.go @@ -0,0 +1,34 @@ +package task + +import ( + "base-service/app/dao" + "base-service/app/models" + "base-service/utils" + + jsoniter "github.com/json-iterator/go" +) + +type Service struct { +} + +func (s Service) Create(job dao.WeComJob, taskId string, detailUrl string) error { + + payload, err := jsoniter.Marshal(job) + if err != nil { + return err + } + task := &dao.WecomReviewTask{ + TaskId: taskId, + ObjId: job.ObjId, + ObjType: job.ObjType, + Type: job.Type, + Payload: string(payload), + DetailUrl: detailUrl, + ReviewStatus: dao.WecomReviewPending, + ResponseCode: dao.StringEmpty, + AgentId: utils.GetConfigInt("wecom.review_agent_id"), + CreatedAt: utils.Now(), + } + + return models.WecomReviewTask{}.Init().Create(task) +} diff --git a/services/wecom/internal/logic/message/utils/helper.go b/services/wecom/internal/logic/message/utils/helper.go new file mode 100644 index 0000000..b06acb9 --- /dev/null +++ b/services/wecom/internal/logic/message/utils/helper.go @@ -0,0 +1,55 @@ +package utils + +import ( + "base-service/app/dao" + "base-service/pkg/wecom/message" + "base-service/utils" + "fmt" +) + +func GetMapString(data map[string]interface{}, key string) string { + + val, ok := data[key] + + if !ok || val == nil { + return dao.StringEmpty + } + + return fmt.Sprintf("%v", val) +} + +func GetExtString(ext map[string]any, key string) string { + + v, ok := ext[key].(string) + if !ok || v == "" { + return "无" + } + return v +} + +func BuildContent(kvPairs ...struct{ Key, Value string }) []message.HorizontalContent { + const maxTotal = 6 + const fixedItems = 1 + maxInput := maxTotal - fixedItems + + inputCount := len(kvPairs) + if inputCount > maxInput { + inputCount = maxInput + } + + content := make([]message.HorizontalContent, 0, inputCount+fixedItems) + + for i := 0; i < inputCount; i++ { + content = append(content, message.HorizontalContent{ + KeyName: kvPairs[i].Key, + Value: kvPairs[i].Value, + }) + } + + content = append(content, message.HorizontalContent{ + KeyName: "审核时间", + Value: utils.Now().Format(), + }) + + return content +} diff --git a/services/wecom/internal/logic/message/utils/user.go b/services/wecom/internal/logic/message/utils/user.go new file mode 100644 index 0000000..3f1709f --- /dev/null +++ b/services/wecom/internal/logic/message/utils/user.go @@ -0,0 +1,149 @@ +package utils + +import ( + "base-service/app/dao" + "base-service/app/models" + "strconv" + "strings" +) + +func GetApplicantUser(objId int, objType uint8, career uint8) (*dao.WecomUser, error) { + + w := models.Params{ + Eq: map[string]string{ + "obj_id": strconv.Itoa(objId), + "obj_type": strconv.Itoa(int(objType)), + }, + Order: "id desc", + } + + editLog := dao.EditLogInfo{} + err := models.EditLogModel{}.Init().GetOne(w, &editLog) + if err != nil { + return nil, err + } + + uw := models.Params{ + Eq: map[string]string{ + "obj_id": strconv.Itoa(editLog.AdminId), + "obj_type": strconv.Itoa(int(career)), + "status": strconv.Itoa(int(dao.StatusOk)), + }, + } + + user := dao.WecomUser{} + err = models.WecomUserModel{}.Init().GetOne(uw, &user) + if err != nil { + return nil, err + } + + return &user, nil +} + +func GetAdminUser(objId int, objType uint8) (*dao.Admin, error) { + + w := models.Params{ + Eq: map[string]string{ + "obj_id": strconv.Itoa(objId), + "obj_type": strconv.Itoa(int(objType)), + }, + Order: "id desc", + } + + editLog := dao.EditLogInfo{} + err := models.EditLogModel{}.Init().GetOne(w, &editLog) + if err != nil { + return nil, err + } + user := dao.Admin{} + + err = models.AdminUserModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "id": strconv.Itoa(editLog.AdminId), + }, + }, &user) + + return &user, nil +} + +func GetActionUser(objId int, objType uint8) (*dao.Admin, error) { + + w := models.Params{ + Eq: map[string]string{ + "obj_id": strconv.Itoa(objId), + "type": strconv.Itoa(int(objType)), + }, + Order: "id desc", + } + + apply := dao.ActionApplyLogInfo{} + err := models.ActionApplyLogModel{}.Init().GetOne(w, &apply) + if err != nil { + return nil, err + } + user := dao.Admin{} + + err = models.AdminUserModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "id": strconv.Itoa(apply.AdminId), + }, + }, &user) + + return &user, nil +} + +func GetActionWecomUser(objId int, objType uint8) (*dao.WecomUser, error) { + + w := models.Params{ + Eq: map[string]string{ + "obj_id": strconv.Itoa(objId), + "type": strconv.Itoa(int(objType)), + }, + Order: "id desc", + } + + apply := dao.ActionApplyLogInfo{} + err := models.ActionApplyLogModel{}.Init().GetOne(w, &apply) + if err != nil { + return nil, err + } + user := dao.Admin{} + + err = models.AdminUserModel{}.Init().GetOne(models.Params{ + Eq: map[string]string{ + "id": strconv.Itoa(apply.AdminId), + }, + }, &user) + + uw := models.Params{ + Eq: map[string]string{ + "obj_id": strconv.Itoa(user.Id), + "obj_type": strconv.Itoa(dao.NumberTwo), + "status": strconv.Itoa(int(dao.StatusOk)), + }, + } + + wecom := dao.WecomUser{} + err = models.WecomUserModel{}.Init().GetOne(uw, &wecom) + if err != nil { + return nil, err + } + + return &wecom, nil +} + +func JoinWecomUsers(users ...string) string { + + result := make([]string, 0) + + for _, user := range users { + + if user == "" { + continue + } + + result = append(result, user) + } + + return strings.Join(result, "|") +}