package service import ( "context" "encoding/json" "fmt" "net/url" "strconv" "time" "go-common/app/job/main/member/conf" "go-common/app/job/main/member/model" memmodel "go-common/app/service/main/member/model" "go-common/library/log" "go-common/library/net/ip" "github.com/pkg/errors" ) // constrs for gender const ( _genderMale = "male" _genderFemale = "female" ) // realname alipay polling func (s *Service) realnamealipaycheckproc() { defer func() { if x := recover(); x != nil { log.Error("%+v", errors.WithStack(fmt.Errorf("service.realnamealipaycheckproc panic(%v)", x))) go s.realnamealipaycheckproc() log.Info("service.realnamealipaycheckproc recover") } }() for { var ( to = time.Now() from = to.Add(-2 * time.Duration(conf.Conf.Biz.RealnameAlipayCheckTick)) expiredTime = from startTime = expiredTime.AddDate(0, -1, 0) ) log.Info("realname alipay check start from : %s , to : %s", from, to) s.realnameAlipayCheckHandler(context.Background(), from, to) // to = from // from = to.Add(-2 * time.Duration(conf.Conf.Biz.RealnameAlipayCheckTick)) log.Info("realname alipay handle expired end startTime : %s , expiredTime : %s", startTime, expiredTime) s.realnameAlipayExpiredHandler(context.Background(), startTime, expiredTime) time.Sleep(time.Duration(conf.Conf.Biz.RealnameAlipayCheckTick)) } } // realnameAlipayCheckHandler 轮询时间段 [from,to] 中,未完成阿里实名的实名申请 func (s *Service) realnameAlipayCheckHandler(c context.Context, from, to time.Time) { if conf.Conf.Biz.RealnameAlipayCheckLimit <= 0 { log.Error("conf.Conf.Property.realnameAlipayCheckHandler [%d] <= 0", conf.Conf.Biz.RealnameAlipayCheckLimit) return } var ( applys = make([]*model.RealnameAlipayApply, conf.Conf.Biz.RealnameAlipayCheckLimit) startID int64 err error ) for len(applys) >= conf.Conf.Biz.RealnameAlipayCheckLimit { if startID, applys, err = s.dao.RealnameAlipayApplyList(c, startID, model.RealnameApplyStatusPending, from, to, conf.Conf.Biz.RealnameAlipayCheckLimit); err != nil { log.Error("%+v", err) return } for _, apply := range applys { log.Info("Start check realname alipay apply mid (%d) bizno (%s)", apply.MID, apply.Bizno) if err = s.realnameAlipayConfirm(c, apply); err != nil { log.Error("%+v", err) continue } } } for len(applys) >= conf.Conf.Biz.RealnameAlipayCheckLimit { if startID, applys, err = s.dao.RealnameAlipayApplyList(c, startID, model.RealnameApplyStatusBack, from, to, conf.Conf.Biz.RealnameAlipayCheckLimit); err != nil { log.Error("%+v", err) return } for _, apply := range applys { log.Info("Start check realname alipay apply mid (%d) bizno (%s)", apply.MID, apply.Bizno) if err = s.realnameAlipayConfirm(c, apply); err != nil { log.Error("%+v", err) continue } } } } func (s *Service) realnameAlipayConfirm(c context.Context, apply *model.RealnameAlipayApply) (err error) { if apply.Bizno == "" { return } var ( pass bool reason string ) if pass, reason, err = s.alipayQuery(c, apply.Bizno); err != nil { return } // rpc call var ( rpcConfirmArg = &memmodel.ArgRealnameAlipayConfirm{ MID: apply.MID, Pass: pass, Reason: reason, } ) if err = s.memrpc.RealnameAlipayConfirm(c, rpcConfirmArg); err != nil { return } log.Info("Succeed to confirm realname alipay with arg: %+v", rpcConfirmArg) if pass { expArg := &model.AddExp{ Mid: apply.MID, IP: ip.InternalIP(), Ts: time.Now().Unix(), Event: "identify", } if expErr := s.addExp(context.TODO(), expArg); expErr != nil { log.Error("realname exp error(%+v) ", expErr) return } log.Info("realname exp success(%+v)", expArg) } return } func (s *Service) alipayQuery(c context.Context, bizno string) (pass bool, reason string, err error) { var ( param url.Values biz struct { Bizno string `json:"biz_no"` } ) biz.Bizno = bizno if param, err = s.alipayParam("zhima.customer.certification.query", biz, ""); err != nil { return } if pass, reason, err = s.dao.AlipayQuery(c, param); err != nil { return } return } // alipayParam 构造阿里请求param,biz为 biz_content struct func (s *Service) alipayParam(method string, biz interface{}, returnURL string) (p url.Values, err error) { var ( sign string bizBytes []byte ) if bizBytes, err = json.Marshal(biz); err != nil { err = errors.WithStack(err) return } p = url.Values{} p.Set("app_id", conf.Conf.Biz.RealnameAlipayAppID) p.Set("method", method) p.Set("charset", "utf-8") p.Set("sign_type", "RSA2") p.Set("timestamp", time.Now().Format("2006-01-02 15:04:05")) p.Set("version", "1.0") p.Set("biz_content", string(bizBytes)) if returnURL != "" { p.Set("return_url", returnURL) } if sign, err = s.alipayCryptor.SignParam(p); err != nil { return } p.Set("sign", sign) return } // rejectExpiredRealnameAlipay 自动驳回超过两天还没有通过芝麻认证的实名认证 func (s *Service) realnameAlipayExpiredHandler(c context.Context, startTime, expiredTime time.Time) { if conf.Conf.Biz.RealnameAlipayCheckLimit <= 0 { log.Error("conf.Conf.Property.realnameAlipayCheckHandler [%d] <= 0", conf.Conf.Biz.RealnameAlipayCheckLimit) return } var ( applys []*model.RealnameAlipayApply startID int64 err error ) // 每次查询(一个月里)100条过期的未处理的位处理的芝麻认证数据,进行驳回 for { log.Info("realname handle startID (%d)", startID) startID, applys, err = s.dao.RealnameAlipayApplyList(c, startID, model.RealnameApplyStatusPending, startTime, expiredTime, conf.Conf.Biz.RealnameAlipayCheckLimit) if err != nil { log.Error("realnameAlipayExpiredHandler search err(%+v)", err) return } // 没有查询到预期的过期数据,则停止循环,等待下一次检查 if len(applys) == 0 { log.Error("realnameAlipayExpiredHandler search no row in result") return } // 循环驳回验证超时的芝麻认证 for _, apply := range applys { log.Info("Start expire realname alipay apply mid (%d) bizno (%s)", apply.MID, apply.Bizno) var ( rpcConfirmArg = &memmodel.ArgRealnameAlipayConfirm{ MID: apply.MID, Pass: false, Reason: "超时自动驳回", } ) if err = s.memrpc.RealnameAlipayConfirm(c, rpcConfirmArg); err != nil { log.Error("realnameAlipayExpiredHandler reject err(%+v)", err) continue } } time.Sleep(10 * time.Millisecond) } } // ParseIdentity to birthday and gender func ParseIdentity(id string) (birthday time.Time, gender string, err error) { var ( ystr, mstr, dstr, gstr string y, m, d, g int ) switch len(id) { case 15: ystr, mstr, dstr = "19"+id[6:8], id[8:10], id[10:12] gstr = id[14:15] case 18: ystr, mstr, dstr = id[6:10], id[10:12], id[12:14] gstr = id[16:17] default: err = errors.Errorf("identity id invalid : %s", id) return } if y, err = strconv.Atoi(ystr); err != nil { err = errors.WithStack(err) return } if m, err = strconv.Atoi(mstr); err != nil { err = errors.WithStack(err) return } if d, err = strconv.Atoi(dstr); err != nil { err = errors.WithStack(err) return } if g, err = strconv.Atoi(gstr); err != nil { err = errors.WithStack(err) return } birthday = time.Date(y, time.Month(m), d, 0, 0, 0, 0, time.Local) if g%2 == 1 { gender = _genderMale } else { gender = _genderFemale } return }