// Copyright 2019 getensh.com. All rights reserved. // Use of this source code is governed by getensh.com. package company import ( "context" "encoding/json" "fmt" "git.getensh.com/common/gopkgs/database" "git.getensh.com/common/gopkgs/logger" "go.uber.org/zap" "google.golang.org/grpc/status" "property-company/errors" dbmodel "property-company/model" "property-company/parser" "property-company/pb" pb_v1 "property-company/pb/v1" "property-company/utils" "strings" "time" ) var identityTypeM = map[string]string{ "IDENTIFICATION_TYPE_IDCARD": "中国大陆居民-身份证", "IDENTIFICATION_TYPE_OVERSEA_PASSPORT": "其他国家或地区居民-护照", "IDENTIFICATION_TYPE_HONGKONG_PASSPORT": "中国香港居民-来往内地通行证", "IDENTIFICATION_TYPE_MACAO_PASSPORT": "中国澳门居民-来往内地通行证", "IDENTIFICATION_TYPE_TAIWAN_PASSPORT": "中国台湾居民-来往大陆通行证", } func checkSubjectLicenseInfo(req *pb_v1.CompanyWxAccountApplyRequest) error { license := req.SubjectInfo.BusinessLicenseInfo begin := checkDate(license.PeriodBegin) end := checkDate(license.PeriodEnd) switch { case license.LegalPerson == "": return status.Error(10003, "主体信息-营业执照:法人不能为空") case license.LicenseCopy == "" || license.LicenseCopyUrl == "": return status.Error(10003, "主体信息-营业执照:营业执照片不能为空") case license.LicenseNumber == "": return status.Error(10003, "主体信息-营业执照:统一社会信用码不能为空") case license.MerchantName == "": return status.Error(10003, "主体信息-营业执照:营业执照商户名不能为空") case license.LicenseAddress == "": return status.Error(10003, "主体信息-营业执照:营业执照注册地址不能为空") case begin == 0: return status.Error(10003, "主体信息-营业执照:营业执照开始时间错误") case license.PeriodEnd != "长期" && end == 0: return status.Error(10003, "主体信息-营业执照:营业执照结束时间错误") case license.PeriodEnd != "长期" && end < begin: return status.Error(10003, "主体信息-营业执照:营业执照结束时间或开始时间") } return nil } func checkSubjectIdentityInfo(req *pb_v1.CompanyWxAccountApplyRequest) error { identity := req.SubjectInfo.IdentityInfo if identity.IdDocInfo == nil { return status.Error(10003, "主体信息-法人信息:法人证件信息不能为空") } p := identity.IdDocInfo begin := checkDate(p.DocPeriodBegin) end := checkDate(p.DocPeriodEnd) switch { case identityTypeM[p.IdDocType] == "": return status.Error(10003, "主体信息-法人信息:不支持的证件类型") case begin == 0: return status.Error(10003, "主体信息-法人信息:证件开始时间错误") case p.DocPeriodEnd != "长期" && end == 0: return status.Error(10003, "主体信息-法人信息:证件结束时间错误") case p.DocPeriodEnd != "长期" && end < begin: return status.Error(10003, "主体信息-法人信息:证件结束时间或开始时间错误") case p.IdDocCopy == "" || p.IdDocCopyUrl == "": return status.Error(10003, "主体信息-法人信息:人像面照片不能为空") case (p.IdDocCopyBack == "" || p.IdDocCopyBackUrl == "") && p.IdDocType != "IDENTIFICATION_TYPE_OVERSEA_PASSPORT": return status.Error(10003, "主体信息-法人信息:除护照外证件背面不能为空") case p.IdDocName == "": return status.Error(10003, "主体信息-法人信息:证件姓名不能为空") case p.IdDocNumber == "": return status.Error(10003, "主体信息-法人信息:证件号不能为空") case p.IdDocAddress == "": return status.Error(10003, "主体信息-法人信息:证件地址不能为空") } return nil } func checkSubjectUboInfo(req *pb_v1.CompanyWxAccountApplyRequest) error { array := req.SubjectInfo.UboInfos for _, p := range array { begin := checkDate(p.DocPeriodBegin) end := checkDate(p.DocPeriodEnd) switch { case identityTypeM[p.IdDocType] == "": return status.Error(10003, "主体信息-受益人信息:收益人证件类型错误") case p.IdDocCopy == "" || p.IdDocCopyUrl == "": return status.Error(10003, "主体信息-受益人信息:证件正面不能为空") case (p.IdDocCopyBack == "" || p.IdDocCopyBackUrl == "") && p.IdDocType != "IDENTIFICATION_TYPE_OVERSEA_PASSPORT": return status.Error(10003, "主体信息-受益人信息:除护照外,证件背面不能为空") case p.IdDocName == "": return status.Error(10003, "主体信息-受益人信息:证件姓名不能为空") case p.IdDocNumber == "": return status.Error(10003, "主体信息-受益人信息:证件号不能为空") case p.IdDocAddress == "": return status.Error(10003, "主体信息-受益人信息:证件地址不能为空") case begin == 0: return status.Error(10003, "主体信息-受益人信息:证件开始时间错误") case p.DocPeriodEnd != "长期" && end == 0: return status.Error(10003, "主体信息-受益人信息:证件结束时间错误") case p.DocPeriodEnd != "长期" && end < begin: return status.Error(10003, "主体信息-受益人信息:证件开始时间或结束时间错误") } } return nil } func checkSubjectInfo(req *pb_v1.CompanyWxAccountApplyRequest) error { subject := req.SubjectInfo switch { case subject.SubjectType != "SUBJECT_TYPE_ENTERPRISE": return status.Error(10003, "主体类型错误") case subject.BusinessLicenseInfo == nil: return status.Error(10003, "营业执照信息不能为空") case subject.IdentityInfo == nil: return status.Error(10003, "法人身份信息不能为空") case !subject.IdentityInfo.Owner && len(subject.UboInfos) == 0: return status.Error(10003, "法人不是最终受益人需填写受益人资料") } if err := checkSubjectLicenseInfo(req); err != nil { return err } if err := checkSubjectIdentityInfo(req); err != nil { return err } if err := checkSubjectUboInfo(req); err != nil { return err } return nil } func checkBusinessInfo(req *pb_v1.CompanyWxAccountApplyRequest) error { p := req.BusinessInfo switch { case p.MerchantShortname == "": return status.Error(10003, "经营资料商户简称不能为空") case p.ServicePhone == "": return status.Error(10003, "经营资料客服电话不能为空") } return nil } func checkBankInfo(req *pb_v1.CompanyWxAccountApplyRequest) error { p := req.BankAccountInfo switch { case p.BankAccountType != "BANK_ACCOUNT_TYPE_CORPORATE": return status.Error(10003, "银行账户类型错误") case p.AccountName == "": return status.Error(10003, "开户名称不能为空") case p.AccountBank == "": return status.Error(10003, "开户银行不能为空") case p.AccountNumber == "": return status.Error(10003, "银行卡号(账号)不能为空") case p.BankAddressCode == "": return status.Error(10003, "开户银行省市编码不能为空") case p.BankName == "": return status.Error(10003, "开户银行全称不能为空") } return nil } func checkDate(data string) int64 { array := strings.Split(data, "-") if len(array) != 3 { return 0 } t, err := time.ParseInLocation("2006-01-02", data, time.Local) if err != nil { return 0 } if t.Format("2006-01-02") != data { return 0 } return t.Unix() } func checkContactInfo(req *pb_v1.CompanyWxAccountApplyRequest) error { p := req.ContactInfo begin := checkDate(p.ContactPeriodBegin) end := checkDate(p.ContactPeriodEnd) switch { case p.ContactEmail == "": return status.Error(10003, "超管员邮箱不能为空") case p.MobilePhone == "": return status.Error(10003, "超管员手机号不能为空") case p.ContactType != "LEGAL" && p.ContactType != "SUPER": return status.Error(10003, "超管员类型不能为空") case p.ContactType == "SUPER" && identityTypeM[p.ContactIdDocType] == "": return status.Error(10003, "超管员为经办人时:证件类型错误") case p.ContactType == "SUPER" && (p.ContactIdDocCopy == "" || p.ContactIdDocCopyUrl == ""): return status.Error(10003, "超管员为经办人时:超管员证件正面不能为空") case p.ContactType == "SUPER" && (p.ContactIdDocCopyBack == "" || p.ContactIdDocCopyBackUrl == "") && p.ContactIdDocType != "IDENTIFICATION_TYPE_OVERSEA_PASSPORT": return status.Error(10003, "超管员为经办人时:除护照外,超管员证件背面不能为空") case p.ContactType == "SUPER" && p.ContactIdNumber == "": return status.Error(10003, "超管员为经办人时:证件号不能为空") case p.ContactType == "SUPER" && p.ContactName == "": return status.Error(10003, "超管员为经办人时:超管员证件证件姓名不能为空") case p.ContactType == "SUPER" && (p.BusinessAuthorizationLetter == "" || p.BusinessAuthorizationLetterUrl == ""): return status.Error(10003, "超管员为经办人时:业务办理授权函不能为空") case p.ContactType == "SUPER" && (begin == 0): return status.Error(10003, "超管员为经办人时:证件开始时间错误") case p.ContactType == "SUPER" && p.ContactPeriodEnd != "长期" && end == 0: return status.Error(10003, "超管员为经办人时:证件结束时间错误") case p.ContactType == "SUPER" && p.ContactPeriodEnd != "长期" && end < begin: return status.Error(10003, "超管员为经办人时:证件结束时间或开始时间错误") } return nil } func checkCompanyWxAccountApplyParam(req *pb_v1.CompanyWxAccountApplyRequest) error { switch { case req.Cid == 0: return status.Error(10003, "公司不能为空不能为空") case req.SubjectInfo == nil: return status.Error(10003, "主体资料不能为空") case req.BusinessInfo == nil: return status.Error(10003, "经营资料不能为空") case req.BankAccountInfo == nil: return status.Error(10003, "银行资料不能为空") case req.ContactInfo == nil: return status.Error(10003, "超管员信息不能为空") } if err := checkContactInfo(req); err != nil { return err } if err := checkSubjectInfo(req); err != nil { return err } if err := checkBusinessInfo(req); err != nil { return err } if err := checkBankInfo(req); err != nil { return err } return nil } func copyContactInfo(mreq *pb_v1.WxMerchantApplyRequest, req *pb_v1.CompanyWxAccountApplyRequest) { mreq.ContactInfo = &pb_v1.WxContactInfo{ ContactType: req.ContactInfo.ContactType, ContactName: req.ContactInfo.ContactName, ContactIdDocType: req.ContactInfo.ContactIdDocType, ContactIdNumber: req.ContactInfo.ContactIdNumber, ContactIdDocCopy: req.ContactInfo.ContactIdDocCopy, ContactIdDocCopyBack: req.ContactInfo.ContactIdDocCopyBack, ContactPeriodBegin: req.ContactInfo.ContactPeriodBegin, ContactPeriodEnd: req.ContactInfo.ContactPeriodEnd, BusinessAuthorizationLetter: req.ContactInfo.BusinessAuthorizationLetter, MobilePhone: req.ContactInfo.MobilePhone, ContactEmail: req.ContactInfo.ContactEmail, } } func copySubjectInfo(mreq *pb_v1.WxMerchantApplyRequest, req *pb_v1.CompanyWxAccountApplyRequest) { mreq.SubjectInfo = &pb_v1.WxSubjectInfo{ SubjectType: req.SubjectInfo.SubjectType, } mreq.SubjectInfo.UboInfos = make([]*pb_v1.WxUboInfo, len(req.SubjectInfo.UboInfos)) for i, v := range req.SubjectInfo.UboInfos { mreq.SubjectInfo.UboInfos[i] = &pb_v1.WxUboInfo{ UboIdDocType: v.IdDocType, UboIdDocCopy: v.IdDocCopy, UboIdDocCopyBack: v.IdDocCopyBack, UboIdDocAddress: v.IdDocAddress, UboIdDocName: v.IdDocName, UboIdDocNumber: v.IdDocNumber, UboPeriodBegin: v.DocPeriodBegin, UboPeriodEnd: v.DocPeriodEnd, } } mreq.SubjectInfo.BusinessLicenseInfo = &pb_v1.WxBusinessLicenseInfo{ LicenseCopy: req.SubjectInfo.BusinessLicenseInfo.LicenseCopy, LicenseNumber: req.SubjectInfo.BusinessLicenseInfo.LicenseNumber, MerchantName: req.SubjectInfo.BusinessLicenseInfo.MerchantName, LegalPerson: req.SubjectInfo.BusinessLicenseInfo.LegalPerson, LicenseAddress: req.SubjectInfo.BusinessLicenseInfo.LicenseAddress, PeriodBegin: req.SubjectInfo.BusinessLicenseInfo.PeriodBegin, PeriodEnd: req.SubjectInfo.BusinessLicenseInfo.PeriodEnd, } mreq.SubjectInfo.IdentityInfo = &pb_v1.WxIdentityInfo{ IdHolderType: "LEGAL", IdDocType: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocType, Owner: req.SubjectInfo.IdentityInfo.Owner, } if req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocType == "IDENTIFICATION_TYPE_IDCARD" { mreq.SubjectInfo.IdentityInfo.IdCardInfo = &pb_v1.WxIdCardInfo{ IdCardCopy: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocCopy, IdCardNational: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocCopyBack, IdCardNumber: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocNumber, CardPeriodBegin: req.SubjectInfo.IdentityInfo.IdDocInfo.DocPeriodBegin, CardPeriodEnd: req.SubjectInfo.IdentityInfo.IdDocInfo.DocPeriodEnd, IdCardName: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocName, IdCardAddress: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocAddress, } } else { mreq.SubjectInfo.IdentityInfo.IdDocInfo = &pb_v1.WxIdDocInfo{ IdDocCopy: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocCopy, IdDocCopyBack: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocCopyBack, IdDocName: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocName, IdDocNumber: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocNumber, DocPeriodBegin: req.SubjectInfo.IdentityInfo.IdDocInfo.DocPeriodBegin, DocPeriodEnd: req.SubjectInfo.IdentityInfo.IdDocInfo.DocPeriodEnd, IdDocAddress: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocAddress, } } } func copyBankInfo(mreq *pb_v1.WxMerchantApplyRequest, req *pb_v1.CompanyWxAccountApplyRequest) { mreq.BankAccountInfo = &pb_v1.WxBankAccountInfo{ AccountName: req.BankAccountInfo.AccountName, AccountBank: req.BankAccountInfo.AccountBank, BankAddressCode: req.BankAccountInfo.BankAddressCode, BankBranchId: req.BankAccountInfo.BankBranchId, BankName: req.BankAccountInfo.BankName, AccountNumber: req.BankAccountInfo.AccountNumber, BankAccountType: req.BankAccountInfo.BankAccountType, } } func copyBusinessInfo(mreq *pb_v1.WxMerchantApplyRequest, req *pb_v1.CompanyWxAccountApplyRequest) { mreq.BusinessInfo = &pb_v1.WxBusinessInfo{ MerchantShortname: req.BusinessInfo.MerchantShortname, ServicePhone: req.BusinessInfo.ServicePhone, } } // func CompanyWxAccountApply(ctx context.Context, req *pb_v1.CompanyWxAccountApplyRequest) (reply *pb_v1.CompanyWxAccountApplyReply, err error) { reply = &pb_v1.CompanyWxAccountApplyReply{} // 捕获各个task中的异常并返回给调用者 defer func() { if r := recover(); r != nil { err = fmt.Errorf("%+v", r) e := &status.Status{} if er := json.Unmarshal([]byte(err.Error()), e); er != nil { logger.Error("err", zap.String("system_err", err.Error()), zap.Stack("stacktrace")) } } }() err = checkCompanyWxAccountApplyParam(req) if err != nil { return nil, err } businessCode := fmt.Sprintf("%s_%d_%d_%s", parser.Conf.ThirdParty.Wx.AppletMchId, req.Cid, time.Now().Unix(), utils.GenerateRandomStr(16, "mix")) // 先插入记录 db := database.DB().Begin() p := dbmodel.TWxMerchantInfo{} p.Cid = req.Cid p.BusinessCode = businessCode p.CreatedAt = time.Now() p.UpdatedAt = time.Now() bytes, _ := json.Marshal(req.SubjectInfo) p.SubjectInfo = string(bytes) bytes, _ = json.Marshal(req.BusinessInfo) p.BusinessInfo = string(bytes) bytes, _ = json.Marshal(req.BankAccountInfo) p.BankAccountInfo = string(bytes) bytes, _ = json.Marshal(req.ContactInfo) p.ContactInfo = string(bytes) p.StateMsg = "准备中" p.State = "APPLYMENT_STATE_PREPARE" if err = p.Insert(db); err != nil { db.Rollback() return nil, errors.DataBaseError } if p.ID == 0 { db.Rollback() return nil, errors.DataBaseError } // 调三方接口进件 mreq := &pb_v1.WxMerchantApplyRequest{} mreq.BusinessCode = businessCode copyContactInfo(mreq, req) copySubjectInfo(mreq, req) copyBankInfo(mreq, req) copyBusinessInfo(mreq, req) mreply, err := pb.Thirdparty.WxMerchantApply(ctx, mreq) if err != nil { db.Rollback() return nil, err } db.Commit() // 更新记录 where := map[string]interface{}{ "id": p.ID, } values := map[string]interface{}{ "apply_id": mreply.ApplymentId, "state": "APPLYMENT_STATE_AUDITING", } // 此处不管失败情况 _ = p.Update(database.DB(), where, values) utils.RobotMsg(fmt.Sprintf("微信商户(%s)提交审核,请关注", req.BusinessInfo.MerchantShortname)) return reply, nil }