wx_account_apply.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. // Copyright 2019 getensh.com. All rights reserved.
  2. // Use of this source code is governed by getensh.com.
  3. package company
  4. import (
  5. "context"
  6. "encoding/json"
  7. "fmt"
  8. "git.getensh.com/common/gopkgs/database"
  9. "git.getensh.com/common/gopkgs/logger"
  10. "go.uber.org/zap"
  11. "google.golang.org/grpc/status"
  12. "property-company/errors"
  13. dbmodel "property-company/model"
  14. "property-company/parser"
  15. "property-company/pb"
  16. pb_v1 "property-company/pb/v1"
  17. "property-company/utils"
  18. "strings"
  19. "time"
  20. )
  21. var identityTypeM = map[string]string{
  22. "IDENTIFICATION_TYPE_IDCARD": "中国大陆居民-身份证",
  23. "IDENTIFICATION_TYPE_OVERSEA_PASSPORT": "其他国家或地区居民-护照",
  24. "IDENTIFICATION_TYPE_HONGKONG_PASSPORT": "中国香港居民-来往内地通行证",
  25. "IDENTIFICATION_TYPE_MACAO_PASSPORT": "中国澳门居民-来往内地通行证",
  26. "IDENTIFICATION_TYPE_TAIWAN_PASSPORT": "中国台湾居民-来往大陆通行证",
  27. }
  28. func checkSubjectLicenseInfo(req *pb_v1.CompanyWxAccountApplyRequest) error {
  29. license := req.SubjectInfo.BusinessLicenseInfo
  30. begin := checkDate(license.PeriodBegin)
  31. end := checkDate(license.PeriodEnd)
  32. switch {
  33. case license.LegalPerson == "":
  34. return status.Error(10003, "主体信息-营业执照:法人不能为空")
  35. case license.LicenseCopy == "" || license.LicenseCopyUrl == "":
  36. return status.Error(10003, "主体信息-营业执照:营业执照片不能为空")
  37. case license.LicenseNumber == "":
  38. return status.Error(10003, "主体信息-营业执照:统一社会信用码不能为空")
  39. case license.MerchantName == "":
  40. return status.Error(10003, "主体信息-营业执照:营业执照商户名不能为空")
  41. case license.LicenseAddress == "":
  42. return status.Error(10003, "主体信息-营业执照:营业执照注册地址不能为空")
  43. case begin == 0:
  44. return status.Error(10003, "主体信息-营业执照:营业执照开始时间错误")
  45. case license.PeriodEnd != "长期" && end == 0:
  46. return status.Error(10003, "主体信息-营业执照:营业执照结束时间错误")
  47. case license.PeriodEnd != "长期" && end < begin:
  48. return status.Error(10003, "主体信息-营业执照:营业执照结束时间或开始时间")
  49. }
  50. return nil
  51. }
  52. func checkSubjectIdentityInfo(req *pb_v1.CompanyWxAccountApplyRequest) error {
  53. identity := req.SubjectInfo.IdentityInfo
  54. if identity.IdDocInfo == nil {
  55. return status.Error(10003, "主体信息-法人信息:法人证件信息不能为空")
  56. }
  57. p := identity.IdDocInfo
  58. begin := checkDate(p.DocPeriodBegin)
  59. end := checkDate(p.DocPeriodEnd)
  60. switch {
  61. case identityTypeM[p.IdDocType] == "":
  62. return status.Error(10003, "主体信息-法人信息:不支持的证件类型")
  63. case begin == 0:
  64. return status.Error(10003, "主体信息-法人信息:证件开始时间错误")
  65. case p.DocPeriodEnd != "长期" && end == 0:
  66. return status.Error(10003, "主体信息-法人信息:证件结束时间错误")
  67. case p.DocPeriodEnd != "长期" && end < begin:
  68. return status.Error(10003, "主体信息-法人信息:证件结束时间或开始时间错误")
  69. case p.IdDocCopy == "" || p.IdDocCopyUrl == "":
  70. return status.Error(10003, "主体信息-法人信息:人像面照片不能为空")
  71. case (p.IdDocCopyBack == "" || p.IdDocCopyBackUrl == "") && p.IdDocType != "IDENTIFICATION_TYPE_OVERSEA_PASSPORT":
  72. return status.Error(10003, "主体信息-法人信息:除护照外证件背面不能为空")
  73. case p.IdDocName == "":
  74. return status.Error(10003, "主体信息-法人信息:证件姓名不能为空")
  75. case p.IdDocNumber == "":
  76. return status.Error(10003, "主体信息-法人信息:证件号不能为空")
  77. case p.IdDocAddress == "":
  78. return status.Error(10003, "主体信息-法人信息:证件地址不能为空")
  79. }
  80. return nil
  81. }
  82. func checkSubjectUboInfo(req *pb_v1.CompanyWxAccountApplyRequest) error {
  83. array := req.SubjectInfo.UboInfos
  84. for _, p := range array {
  85. begin := checkDate(p.DocPeriodBegin)
  86. end := checkDate(p.DocPeriodEnd)
  87. switch {
  88. case identityTypeM[p.IdDocType] == "":
  89. return status.Error(10003, "主体信息-受益人信息:收益人证件类型错误")
  90. case p.IdDocCopy == "" || p.IdDocCopyUrl == "":
  91. return status.Error(10003, "主体信息-受益人信息:证件正面不能为空")
  92. case (p.IdDocCopyBack == "" || p.IdDocCopyBackUrl == "") && p.IdDocType != "IDENTIFICATION_TYPE_OVERSEA_PASSPORT":
  93. return status.Error(10003, "主体信息-受益人信息:除护照外,证件背面不能为空")
  94. case p.IdDocName == "":
  95. return status.Error(10003, "主体信息-受益人信息:证件姓名不能为空")
  96. case p.IdDocNumber == "":
  97. return status.Error(10003, "主体信息-受益人信息:证件号不能为空")
  98. case p.IdDocAddress == "":
  99. return status.Error(10003, "主体信息-受益人信息:证件地址不能为空")
  100. case begin == 0:
  101. return status.Error(10003, "主体信息-受益人信息:证件开始时间错误")
  102. case p.DocPeriodEnd != "长期" && end == 0:
  103. return status.Error(10003, "主体信息-受益人信息:证件结束时间错误")
  104. case p.DocPeriodEnd != "长期" && end < begin:
  105. return status.Error(10003, "主体信息-受益人信息:证件开始时间或结束时间错误")
  106. }
  107. }
  108. return nil
  109. }
  110. func checkSubjectInfo(req *pb_v1.CompanyWxAccountApplyRequest) error {
  111. subject := req.SubjectInfo
  112. switch {
  113. case subject.SubjectType != "SUBJECT_TYPE_ENTERPRISE":
  114. return status.Error(10003, "主体类型错误")
  115. case subject.BusinessLicenseInfo == nil:
  116. return status.Error(10003, "营业执照信息不能为空")
  117. case subject.IdentityInfo == nil:
  118. return status.Error(10003, "法人身份信息不能为空")
  119. case !subject.IdentityInfo.Owner && len(subject.UboInfos) == 0:
  120. return status.Error(10003, "法人不是最终受益人需填写受益人资料")
  121. }
  122. if err := checkSubjectLicenseInfo(req); err != nil {
  123. return err
  124. }
  125. if err := checkSubjectIdentityInfo(req); err != nil {
  126. return err
  127. }
  128. if err := checkSubjectUboInfo(req); err != nil {
  129. return err
  130. }
  131. return nil
  132. }
  133. func checkBusinessInfo(req *pb_v1.CompanyWxAccountApplyRequest) error {
  134. p := req.BusinessInfo
  135. switch {
  136. case p.MerchantShortname == "":
  137. return status.Error(10003, "经营资料商户简称不能为空")
  138. case p.ServicePhone == "":
  139. return status.Error(10003, "经营资料客服电话不能为空")
  140. }
  141. return nil
  142. }
  143. func checkBankInfo(req *pb_v1.CompanyWxAccountApplyRequest) error {
  144. p := req.BankAccountInfo
  145. switch {
  146. case p.BankAccountType != "BANK_ACCOUNT_TYPE_CORPORATE":
  147. return status.Error(10003, "银行账户类型错误")
  148. case p.AccountName == "":
  149. return status.Error(10003, "开户名称不能为空")
  150. case p.AccountBank == "":
  151. return status.Error(10003, "开户银行不能为空")
  152. case p.AccountNumber == "":
  153. return status.Error(10003, "银行卡号(账号)不能为空")
  154. case p.BankAddressCode == "":
  155. return status.Error(10003, "开户银行省市编码不能为空")
  156. case p.BankName == "":
  157. return status.Error(10003, "开户银行全称不能为空")
  158. }
  159. return nil
  160. }
  161. func checkDate(data string) int64 {
  162. array := strings.Split(data, "-")
  163. if len(array) != 3 {
  164. return 0
  165. }
  166. t, err := time.ParseInLocation("2006-01-02", data, time.Local)
  167. if err != nil {
  168. return 0
  169. }
  170. if t.Format("2006-01-02") != data {
  171. return 0
  172. }
  173. return t.Unix()
  174. }
  175. func checkContactInfo(req *pb_v1.CompanyWxAccountApplyRequest) error {
  176. p := req.ContactInfo
  177. begin := checkDate(p.ContactPeriodBegin)
  178. end := checkDate(p.ContactPeriodEnd)
  179. switch {
  180. case p.ContactEmail == "":
  181. return status.Error(10003, "超管员邮箱不能为空")
  182. case p.MobilePhone == "":
  183. return status.Error(10003, "超管员手机号不能为空")
  184. case p.ContactType != "LEGAL" && p.ContactType != "SUPER":
  185. return status.Error(10003, "超管员类型不能为空")
  186. case p.ContactType == "SUPER" && identityTypeM[p.ContactIdDocType] == "":
  187. return status.Error(10003, "超管员为经办人时:证件类型错误")
  188. case p.ContactType == "SUPER" && (p.ContactIdDocCopy == "" || p.ContactIdDocCopyUrl == ""):
  189. return status.Error(10003, "超管员为经办人时:超管员证件正面不能为空")
  190. case p.ContactType == "SUPER" && (p.ContactIdDocCopyBack == "" || p.ContactIdDocCopyBackUrl == "") && p.ContactIdDocType != "IDENTIFICATION_TYPE_OVERSEA_PASSPORT":
  191. return status.Error(10003, "超管员为经办人时:除护照外,超管员证件背面不能为空")
  192. case p.ContactType == "SUPER" && p.ContactIdNumber == "":
  193. return status.Error(10003, "超管员为经办人时:证件号不能为空")
  194. case p.ContactType == "SUPER" && p.ContactName == "":
  195. return status.Error(10003, "超管员为经办人时:超管员证件证件姓名不能为空")
  196. case p.ContactType == "SUPER" && (p.BusinessAuthorizationLetter == "" || p.BusinessAuthorizationLetterUrl == ""):
  197. return status.Error(10003, "超管员为经办人时:业务办理授权函不能为空")
  198. case p.ContactType == "SUPER" && (begin == 0):
  199. return status.Error(10003, "超管员为经办人时:证件开始时间错误")
  200. case p.ContactType == "SUPER" && p.ContactPeriodEnd != "长期" && end == 0:
  201. return status.Error(10003, "超管员为经办人时:证件结束时间错误")
  202. case p.ContactType == "SUPER" && p.ContactPeriodEnd != "长期" && end < begin:
  203. return status.Error(10003, "超管员为经办人时:证件结束时间或开始时间错误")
  204. }
  205. return nil
  206. }
  207. func checkCompanyWxAccountApplyParam(req *pb_v1.CompanyWxAccountApplyRequest) error {
  208. switch {
  209. case req.Cid == 0:
  210. return status.Error(10003, "公司不能为空不能为空")
  211. case req.SubjectInfo == nil:
  212. return status.Error(10003, "主体资料不能为空")
  213. case req.BusinessInfo == nil:
  214. return status.Error(10003, "经营资料不能为空")
  215. case req.BankAccountInfo == nil:
  216. return status.Error(10003, "银行资料不能为空")
  217. case req.ContactInfo == nil:
  218. return status.Error(10003, "超管员信息不能为空")
  219. }
  220. if err := checkContactInfo(req); err != nil {
  221. return err
  222. }
  223. if err := checkSubjectInfo(req); err != nil {
  224. return err
  225. }
  226. if err := checkBusinessInfo(req); err != nil {
  227. return err
  228. }
  229. if err := checkBankInfo(req); err != nil {
  230. return err
  231. }
  232. return nil
  233. }
  234. func copyContactInfo(mreq *pb_v1.WxMerchantApplyRequest, req *pb_v1.CompanyWxAccountApplyRequest) {
  235. mreq.ContactInfo = &pb_v1.WxContactInfo{
  236. ContactType: req.ContactInfo.ContactType,
  237. ContactName: req.ContactInfo.ContactName,
  238. ContactIdDocType: req.ContactInfo.ContactIdDocType,
  239. ContactIdNumber: req.ContactInfo.ContactIdNumber,
  240. ContactIdDocCopy: req.ContactInfo.ContactIdDocCopy,
  241. ContactIdDocCopyBack: req.ContactInfo.ContactIdDocCopyBack,
  242. ContactPeriodBegin: req.ContactInfo.ContactPeriodBegin,
  243. ContactPeriodEnd: req.ContactInfo.ContactPeriodEnd,
  244. BusinessAuthorizationLetter: req.ContactInfo.BusinessAuthorizationLetter,
  245. MobilePhone: req.ContactInfo.MobilePhone,
  246. ContactEmail: req.ContactInfo.ContactEmail,
  247. }
  248. }
  249. func copySubjectInfo(mreq *pb_v1.WxMerchantApplyRequest, req *pb_v1.CompanyWxAccountApplyRequest) {
  250. mreq.SubjectInfo = &pb_v1.WxSubjectInfo{
  251. SubjectType: req.SubjectInfo.SubjectType,
  252. }
  253. mreq.SubjectInfo.UboInfos = make([]*pb_v1.WxUboInfo, len(req.SubjectInfo.UboInfos))
  254. for i, v := range req.SubjectInfo.UboInfos {
  255. mreq.SubjectInfo.UboInfos[i] = &pb_v1.WxUboInfo{
  256. UboIdDocType: v.IdDocType,
  257. UboIdDocCopy: v.IdDocCopy,
  258. UboIdDocCopyBack: v.IdDocCopyBack,
  259. UboIdDocAddress: v.IdDocAddress,
  260. UboIdDocName: v.IdDocName,
  261. UboIdDocNumber: v.IdDocNumber,
  262. UboPeriodBegin: v.DocPeriodBegin,
  263. UboPeriodEnd: v.DocPeriodEnd,
  264. }
  265. }
  266. mreq.SubjectInfo.BusinessLicenseInfo = &pb_v1.WxBusinessLicenseInfo{
  267. LicenseCopy: req.SubjectInfo.BusinessLicenseInfo.LicenseCopy,
  268. LicenseNumber: req.SubjectInfo.BusinessLicenseInfo.LicenseNumber,
  269. MerchantName: req.SubjectInfo.BusinessLicenseInfo.MerchantName,
  270. LegalPerson: req.SubjectInfo.BusinessLicenseInfo.LegalPerson,
  271. LicenseAddress: req.SubjectInfo.BusinessLicenseInfo.LicenseAddress,
  272. PeriodBegin: req.SubjectInfo.BusinessLicenseInfo.PeriodBegin,
  273. PeriodEnd: req.SubjectInfo.BusinessLicenseInfo.PeriodEnd,
  274. }
  275. mreq.SubjectInfo.IdentityInfo = &pb_v1.WxIdentityInfo{
  276. IdHolderType: "LEGAL",
  277. IdDocType: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocType,
  278. Owner: req.SubjectInfo.IdentityInfo.Owner,
  279. }
  280. if req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocType == "IDENTIFICATION_TYPE_IDCARD" {
  281. mreq.SubjectInfo.IdentityInfo.IdCardInfo = &pb_v1.WxIdCardInfo{
  282. IdCardCopy: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocCopy,
  283. IdCardNational: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocCopyBack,
  284. IdCardNumber: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocNumber,
  285. CardPeriodBegin: req.SubjectInfo.IdentityInfo.IdDocInfo.DocPeriodBegin,
  286. CardPeriodEnd: req.SubjectInfo.IdentityInfo.IdDocInfo.DocPeriodEnd,
  287. IdCardName: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocName,
  288. IdCardAddress: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocAddress,
  289. }
  290. } else {
  291. mreq.SubjectInfo.IdentityInfo.IdDocInfo = &pb_v1.WxIdDocInfo{
  292. IdDocCopy: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocCopy,
  293. IdDocCopyBack: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocCopyBack,
  294. IdDocName: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocName,
  295. IdDocNumber: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocNumber,
  296. DocPeriodBegin: req.SubjectInfo.IdentityInfo.IdDocInfo.DocPeriodBegin,
  297. DocPeriodEnd: req.SubjectInfo.IdentityInfo.IdDocInfo.DocPeriodEnd,
  298. IdDocAddress: req.SubjectInfo.IdentityInfo.IdDocInfo.IdDocAddress,
  299. }
  300. }
  301. }
  302. func copyBankInfo(mreq *pb_v1.WxMerchantApplyRequest, req *pb_v1.CompanyWxAccountApplyRequest) {
  303. mreq.BankAccountInfo = &pb_v1.WxBankAccountInfo{
  304. AccountName: req.BankAccountInfo.AccountName,
  305. AccountBank: req.BankAccountInfo.AccountBank,
  306. BankAddressCode: req.BankAccountInfo.BankAddressCode,
  307. BankBranchId: req.BankAccountInfo.BankBranchId,
  308. BankName: req.BankAccountInfo.BankName,
  309. AccountNumber: req.BankAccountInfo.AccountNumber,
  310. BankAccountType: req.BankAccountInfo.BankAccountType,
  311. }
  312. }
  313. func copyBusinessInfo(mreq *pb_v1.WxMerchantApplyRequest, req *pb_v1.CompanyWxAccountApplyRequest) {
  314. mreq.BusinessInfo = &pb_v1.WxBusinessInfo{
  315. MerchantShortname: req.BusinessInfo.MerchantShortname,
  316. ServicePhone: req.BusinessInfo.ServicePhone,
  317. }
  318. }
  319. //
  320. func CompanyWxAccountApply(ctx context.Context, req *pb_v1.CompanyWxAccountApplyRequest) (reply *pb_v1.CompanyWxAccountApplyReply, err error) {
  321. reply = &pb_v1.CompanyWxAccountApplyReply{}
  322. // 捕获各个task中的异常并返回给调用者
  323. defer func() {
  324. if r := recover(); r != nil {
  325. err = fmt.Errorf("%+v", r)
  326. e := &status.Status{}
  327. if er := json.Unmarshal([]byte(err.Error()), e); er != nil {
  328. logger.Error("err",
  329. zap.String("system_err", err.Error()),
  330. zap.Stack("stacktrace"))
  331. }
  332. }
  333. }()
  334. err = checkCompanyWxAccountApplyParam(req)
  335. if err != nil {
  336. return nil, err
  337. }
  338. businessCode := fmt.Sprintf("%s_%d_%d_%s",
  339. parser.Conf.ThirdParty.Wx.AppletMchId, req.Cid, time.Now().Unix(), utils.GenerateRandomStr(16, "mix"))
  340. // 先插入记录
  341. db := database.DB().Begin()
  342. p := dbmodel.TWxMerchantInfo{}
  343. p.Cid = req.Cid
  344. p.BusinessCode = businessCode
  345. p.CreatedAt = time.Now()
  346. p.UpdatedAt = time.Now()
  347. bytes, _ := json.Marshal(req.SubjectInfo)
  348. p.SubjectInfo = string(bytes)
  349. bytes, _ = json.Marshal(req.BusinessInfo)
  350. p.BusinessInfo = string(bytes)
  351. bytes, _ = json.Marshal(req.BankAccountInfo)
  352. p.BankAccountInfo = string(bytes)
  353. bytes, _ = json.Marshal(req.ContactInfo)
  354. p.ContactInfo = string(bytes)
  355. p.StateMsg = "准备中"
  356. p.State = "APPLYMENT_STATE_PREPARE"
  357. if err = p.Insert(db); err != nil {
  358. db.Rollback()
  359. return nil, errors.DataBaseError
  360. }
  361. if p.ID == 0 {
  362. db.Rollback()
  363. return nil, errors.DataBaseError
  364. }
  365. // 调三方接口进件
  366. mreq := &pb_v1.WxMerchantApplyRequest{}
  367. mreq.BusinessCode = businessCode
  368. copyContactInfo(mreq, req)
  369. copySubjectInfo(mreq, req)
  370. copyBankInfo(mreq, req)
  371. copyBusinessInfo(mreq, req)
  372. mreply, err := pb.Thirdparty.WxMerchantApply(ctx, mreq)
  373. if err != nil {
  374. db.Rollback()
  375. return nil, err
  376. }
  377. db.Commit()
  378. // 更新记录
  379. where := map[string]interface{}{
  380. "id": p.ID,
  381. }
  382. values := map[string]interface{}{
  383. "apply_id": mreply.ApplymentId,
  384. "state": "APPLYMENT_STATE_AUDITING",
  385. }
  386. // 此处不管失败情况
  387. _ = p.Update(database.DB(), where, values)
  388. utils.RobotMsg(fmt.Sprintf("微信商户(%s)提交审核,请关注", req.BusinessInfo.MerchantShortname))
  389. return reply, nil
  390. }