浏览代码

Merge remote-tracking branch 'origin/release/v4.10.5' into release/v4.10.5

jianghan 4 月之前
父节点
当前提交
5685aa7253

+ 898 - 69
src/jfw/modules/subscribepay/src/service/userAccountInfo.go

@@ -1,8 +1,11 @@
 package service
 
 import (
-	"app.yhyue.com/moapp/jypkg/common/src/qfw/util/dataexport"
+	jycaptcha "app.yhyue.com/moapp/jybase/captcha"
+	"bytes"
 	"context"
+	"database/sql"
+	"encoding/base64"
 	"fmt"
 	"jy/src/jfw/modules/subscribepay/src/config"
 	"jy/src/jfw/modules/subscribepay/src/entity"
@@ -14,17 +17,24 @@ import (
 	"strings"
 	"time"
 
+	"github.com/gogf/gf/v2/frame/g"
+	"github.com/gogf/gf/v2/util/gconv"
+
 	. "app.yhyue.com/moapp/jybase/api"
 	qutil "app.yhyue.com/moapp/jybase/common"
 	. "app.yhyue.com/moapp/jybase/date"
+	"app.yhyue.com/moapp/jybase/dchest/captcha"
 	"app.yhyue.com/moapp/jybase/encrypt"
+	elastic "app.yhyue.com/moapp/jybase/es"
 	"app.yhyue.com/moapp/jybase/go-xweb/httpsession"
 	"app.yhyue.com/moapp/jybase/go-xweb/xweb"
+	"app.yhyue.com/moapp/jybase/mongodb"
 	"app.yhyue.com/moapp/jybase/redis"
+	"app.yhyue.com/moapp/jybase/usercenter"
+	"app.yhyue.com/moapp/jypkg/common/src/qfw/util/dataexport"
 	"app.yhyue.com/moapp/jypkg/common/src/qfw/util/jy"
 	pcc "bp.jydev.jianyu360.cn/BaseService/powerCheckCenter/rpc/pb"
-	"github.com/gogf/gf/v2/frame/g"
-	"github.com/gogf/gf/v2/util/gconv"
+	"bp.jydev.jianyu360.cn/BaseService/userCenter/rpc/pb"
 )
 
 type UserAccount struct {
@@ -281,6 +291,891 @@ func getAccountVipAttr(session *httpsession.Session, powerRes *pcc.CheckResp) (r
 	return rData
 }
 
+// 我的页面 获取基本信息
+// 手机号 邮箱 头像 昵称 超级订阅 大会员 用户加密id
+func (this *UserAccount) GetAccountInfo() {
+	rData, errMsg := func() (interface{}, error) {
+		sessVal := this.Session().GetMultiple()
+		userId, _ := sessVal["mgoUserId"].(string)
+		//由于超级订阅vip状态需要查库,无法从session中获取,所以直接所有字段从数据库中获取
+		userMsg := util.Compatible.Select(userId, `{"s_m_phone":1,"s_phone":1,"s_myemail":1,"s_nickname":1,"s_headimageurl":1,"s_password":1,"s_company":1,"s_unionid":1}`)
+		if userMsg == nil || len(*userMsg) == 0 {
+			return nil, DBQUERY_ERROR
+		}
+		//微信昵称>手机号>剑鱼昵称
+		nickname := qutil.ObjToString((*userMsg)["s_nickname"])
+		phone := qutil.ObjToString(qutil.If((*userMsg)["s_phone"] != nil, (*userMsg)["s_phone"], (*userMsg)["s_m_phone"]))
+		jyname := qutil.ObjToString((*userMsg)["s_jyname"])
+		isWx := false
+		if unionid, ok := (*userMsg)["s_unionid"]; ok {
+			if len(qutil.ObjToString(unionid)) > 11 { //手机号用户s_unionid为手机号
+				isWx = true
+			}
+		}
+		breakRenewTip := 0
+		orders := util.Mysql.SelectBySql(`SELECT pay_time from dataexport_order where user_id=? and order_status=1 and product_type='VIP订阅' and vip_type is null order by pay_time desc limit 1`, userId)
+		if orders != nil && len(*orders) == 1 {
+			timeA, errorA := time.ParseInLocation(Date_Full_Layout, config.Config.BreakRenewTipTime, time.Local)
+			timeB, errorB := time.ParseInLocation(Date_Full_Layout, qutil.ObjToString((*orders)[0]["pay_time"]), time.Local)
+			if errorA == nil && errorB == nil && timeB.Before(timeA) {
+				breakRenewTip = 1
+			}
+		}
+		b := jy.GetBigVipUserBaseMsg(this.Session(), *config.Middleground)
+		//s从数据导出填写的邮箱或者从数据定制服务这里获取的邮箱
+		_, otherMail := dataexport.GetLastExportPhoneAndMail(util.Mysql, qutil.ObjToString(sessVal["userId"]))
+		if otherMail == "" {
+			saleData, _ := util.MQFW.FindOne("saleLeads", map[string]interface{}{
+				"userid": sessVal["userId"],
+				"mail": map[string]interface{}{
+					"$exists": true,
+				},
+			})
+			if saleData != nil && len(*saleData) > 0 {
+				otherMail = qutil.ObjToString((*saleData)["mail"])
+			}
+			if qutil.ObjToString((*userMsg)["s_myemail"]) != "" {
+				otherMail = qutil.ObjToString((*userMsg)["s_myemail"])
+			}
+		}
+		return map[string]interface{}{
+			"userId":        encrypt.EncodeArticleId(userId),
+			"email":         qutil.ObjToString((*userMsg)["s_myemail"]),
+			"phone":         phone,
+			"nickname":      nickname,
+			"jyname":        jyname,
+			"headimageurl":  strings.Replace(qutil.ObjToString((*userMsg)["s_headimageurl"]), "http://", "https://", 1),
+			"bigmemberVip":  b.Status,
+			"subscribeVip":  b.VipStatus,
+			"hasPwd":        qutil.ObjToString((*userMsg)["s_password"]) != "",
+			"company":       qutil.ObjToString((*userMsg)["s_company"]),
+			"isWx":          isWx, //是否是微信账号
+			"breakRenewTip": breakRenewTip,
+			"otherMail":     otherMail, //从数据导出填写的邮箱或者从数据定制服务这里获取的邮箱
+		}, nil
+	}()
+	this.ServeJson(NewResult(rData, errMsg))
+}
+
+// 推送我感兴趣开关
+func (this *UserAccount) Interested(doType string) {
+	sessVal := this.Session().GetMultiple()
+	userId, _ := sessVal["userId"].(string)
+	rData, errMsg := func() (interface{}, error) {
+		var defValue int = 1 //默认设置为开 1开 -1关
+		if doType == "switch" {
+			setValue, _ := this.GetInteger("value")
+			if !(setValue == -1 || setValue == 1) {
+				return nil, PARAM_ERROR
+			}
+			updateOk := util.Compatible.Update(userId, map[string]interface{}{
+				"$set": map[string]interface{}{
+					"i_interested": setValue,
+				},
+			})
+			if !updateOk {
+				return nil, DBUPDATE_ERROR
+			}
+			defValue = setValue
+		} else if doType == "query" {
+			var uData *map[string]interface{}
+			util.Compatible.Select(userId, `{"i_interested":1}`)
+			if uData == nil || len(*uData) == 0 {
+				return nil, DBQUERY_ERROR
+			}
+			if qutil.IntAll((*uData)["i_interested"]) == -1 {
+				defValue = -1
+			}
+		} else {
+			return nil, OPERATION_ERROR
+		}
+		return map[string]interface{}{
+			"valueSet": defValue,
+		}, nil
+	}()
+	if errMsg != nil {
+		log.Printf("%s UserAccount Interested %s异常:%s\n", userId, doType, errMsg.Error())
+	}
+	this.ServeJson(NewResult(rData, errMsg))
+}
+
+// 绑定邮箱&更改邮箱【绑定邮箱和更改邮箱流程一样】
+func (this *UserAccount) MailSet(doType string) {
+	sessVal := this.Session().GetMultiple()
+	userId, _ := sessVal["mgoUserId"].(string)
+	rData, errMsg := func() (interface{}, error) {
+		step, _ := this.GetInteger("step")
+		mail := this.GetString("mail")
+		code := this.GetString("code")
+		if doType == "change" && step == 1 { //仅发送验证码 校验是否通过身份验证
+			if pass, data := authenticationCheck(this.GetSession(mailAuthPassSessionKey)); !pass {
+				return data, nil
+			}
+		}
+		mailVerify, err := mailStep(this.Session(), step, mail, code, "bind")
+		if err != nil {
+			return nil, err
+		}
+		//校验验证码成功【校验是否需要合并】
+		if step == 2 {
+			if !util.Compatible.Update(userId, map[string]interface{}{
+				"$set": map[string]interface{}{
+					"s_myemail": mailVerify,
+				},
+			}) {
+				return nil, DBUPDATE_ERROR
+			}
+			userMsg, _ := util.MQFW.FindById("user", userId, `{"l_registedate":1}`)
+			isNew := false
+			if userMsg != nil && len(*userMsg) > 0 {
+				l_registedate := qutil.Int64All((*userMsg)["l_registedate"])
+				isNew = l_registedate > config.Config.TaskStartTime //是否注册时间处于新手任务开始时间
+			}
+			jy.Publish(util.Mgo_log, config.Config.Nsq, config.Config.Nsq_Topic, "task", userId, jy.Jyweb_node2, map[string]interface{}{
+				"code":       qutil.If(isNew, 1009, 1016),
+				"types":      "bindMail",
+				"num":        50,
+				"baseUserId": sessVal["base_user_id"],
+				"positionId": sessVal["positionId"],
+			})
+			this.Session().Del(mailAuthPassSessionKey)
+		}
+		return map[string]interface{}{
+			"state": 1,
+		}, nil
+	}()
+	if errMsg != nil {
+		log.Printf("%s UserAccount MailSet %s 邮箱异常:%s\n", userId, doType, errMsg.Error())
+	}
+	this.ServeJson(NewResult(rData, errMsg))
+}
+
+// 公司名称联想
+func (this *UserAccount) CompanyAssociation() {
+	rData, errMsg := func() (interface{}, error) {
+		name := this.GetString("name")
+		count := config.Config.CompanySize
+		companyCount, _ := this.GetInt("companyCount")
+		if companyCount <= count*2 && companyCount > 0 {
+			count = companyCount
+		}
+		length, _ := this.GetInteger("len") //P416 电销要求公司名称可以两字联想,剑鱼业务还是三字
+		if length < 2 {
+			length = 3
+		}
+		list := []string{}
+		if len([]rune(name)) >= length {
+			query := fmt.Sprintf(`{"query": {"match_phrase": {"name": "%s"}},"_source": ["name"],"size": %d}`, name, count)
+			r := elastic.Get("qyxy", "qyxy", query)
+			if r != nil {
+				for _, v := range *r {
+					list = append(list, qutil.ObjToString(v["name"]))
+				}
+			}
+		}
+		return list, nil
+	}()
+	this.ServeJson(NewResult(rData, errMsg))
+}
+
+// 设置公司名称
+func (this *UserAccount) SetCompany() {
+	userId, _ := this.GetSession("mgoUserId").(string)
+	rData, errMsg := func() (interface{}, error) {
+		companyName := strings.TrimSpace(this.GetString("name"))
+		if companyName == "" {
+			return nil, fmt.Errorf("公司名称不能为空")
+		}
+		if !util.Compatible.Update(userId, map[string]interface{}{
+			"$set": map[string]interface{}{
+				"s_company": companyName,
+			},
+		}) {
+			return nil, DBUPDATE_ERROR
+		}
+		return map[string]interface{}{
+			"state": 1,
+		}, nil
+	}()
+	if errMsg != nil {
+		log.Printf("%s UserAccount ChangeCompany 设置公司名称:%s\n", userId, errMsg.Error())
+	}
+	this.ServeJson(NewResult(rData, errMsg))
+}
+
+// 获取图形验证码
+func (this *UserAccount) PhoneCaptcha() {
+	userId, _ := this.GetSession("mgoUserId").(string)
+	rData, errMsg := func() (interface{}, error) {
+		rData := map[string]interface{}{}
+		if redis.GetInt("other", fmt.Sprintf(redisPhoneCaptchaFilter, userId, time.Now().Day())) < phoneSendNoImgFreeTime {
+			rData["needVerify"] = false
+		} else {
+			id := captcha.NewLen(4)
+			buf := bytes.NewBuffer(nil)
+			if err := captcha.WriteImage(buf, id, 90, 30); err != nil {
+				return nil, fmt.Errorf("生成图形验证码出错")
+			}
+			base64Img := base64.StdEncoding.EncodeToString(buf.Bytes())
+			this.SetSession("UserAccountPhoneCaptcha", id)
+			rData["imageData"] = base64Img
+			rData["needVerify"] = true
+		}
+		return rData, nil
+	}()
+	if errMsg != nil {
+		log.Printf("%s UserAccount PhoneCaptcha 手机短信图形验证码获取异常:%s\n", userId, errMsg.Error())
+	}
+	this.ServeJson(NewResult(rData, errMsg))
+}
+
+// 我的页面身份验证【绑定、更改手机号之前需要先进行身份验证 15分钟内无须重复验证】
+func (this *UserAccount) Authentication(doType string) {
+	sessVal := this.Session().GetMultiple()
+	userId, _ := sessVal["mgoUserId"].(string)
+	rData, errMsg := func() (interface{}, error) {
+		step, _ := this.GetInteger("step")
+		code := this.GetString("code")
+		if doType == "mail" { //邮箱安全校验
+			var uMail string
+			if step == 1 {
+				uMail = getUserMail(userId)
+				if uMail == "" {
+					return nil, fmt.Errorf("未查询到绑定邮箱")
+				}
+			}
+			if _, err := mailStep(this.Session(), step, uMail, code, "check"); err != nil {
+				return nil, err
+			}
+			if step == 2 {
+				this.SetSession(mailAuthPassSessionKey, time.Now().Unix())
+			}
+			return map[string]interface{}{
+				"state": 1,
+			}, nil
+		} else if doType == "phone" { //手机号安全校验
+			if step == 1 {
+				//校验图形验证码
+				if pass, err := imgCaptchaCheck(this.Session(), userId, code); !pass || err != nil {
+					return nil, err
+				}
+			}
+			phone, _ := sessVal["s_phone"].(string)
+			if phone == "" {
+				phone = getPhoneByUserId(userId)
+			}
+			if phone == "" {
+				return nil, fmt.Errorf("未绑定手机号")
+			}
+			if _, err := phoneStep(this.Request, this.Session(), step, phone, code, "auth"); err != nil {
+				return nil, err
+			}
+			//存入session 15分钟不用重复验证
+			if step == 2 {
+				this.SetSession(phoneAuthPassSessionKey, time.Now().Unix())
+			}
+			return map[string]interface{}{
+				"state": 1,
+			}, nil
+		}
+		return nil, OPERATION_ERROR
+	}()
+	if errMsg != nil {
+		log.Printf("%s UserAccount Authentication %s 身份验证异常:%s\n", userId, doType, errMsg.Error())
+	}
+	this.ServeJson(NewResult(rData, errMsg))
+}
+
+//绑定手机号
+//微信用户
+//规则 1.绑定的手机号为全新手机号
+//    2.绑定的手机号非全新手机账号。对应已存在手机账户A。(完成后建立关联关系)
+//       A未绑定微信
+//       A账户已绑定微信,此微信必须与当前账户绑定的微信一致
+
+func (this *UserAccount) PhoneBind() {
+	log.Println("绑定手机号开始1")
+	sessVal := this.Session().GetMultiple()
+	userId, _ := sessVal["mgoUserId"].(string)
+	step, _ := this.GetInteger("step")
+	rData, errMsg := func() (interface{}, error) {
+		phone := this.GetString("phone")
+		code := this.GetString("code")
+		mode := this.GetString("mode")
+		email := this.GetString("email")
+		if step == 1 {
+			//校验图形验证码
+			if pass, err := imgCaptchaCheck(this.Session(), userId, code); !pass || err != nil {
+				return nil, err
+			}
+			if getPhoneByUserId(userId) != "" {
+				return nil, fmt.Errorf("已绑定手机号,请勿重复绑定")
+			}
+		}
+		var (
+			phoneVerify string
+			err         error
+		)
+		captchaKey := this.GetString("captchaKey") //图形验证码key
+		if captchaKey != "" {
+			if strings.TrimSpace(phone) == "" {
+				return nil, fmt.Errorf("手机号异常")
+			}
+			if getPhoneByUserId(userId) != "" {
+				return nil, fmt.Errorf("已绑定手机号,请勿重复绑定")
+			}
+			identCode := this.GetString("identCode") //短信验证码
+			phoneVerify = jy.CheckPhoneIdent(this.Session(), identCode, fmt.Sprintf(jycaptcha.SendCodeKey, captchaKey, phone))
+			if phone != phoneVerify { //验证码不正确
+				return nil, fmt.Errorf("验证码错误")
+			}
+		} else {
+			//发送验证码&校验验证码逻辑
+			phoneVerify, err = phoneStep(this.Request, this.Session(), step, phone, code, "bind")
+			if err != nil {
+				return nil, err
+			}
+		}
+		rData := map[string]interface{}{
+			"state": 1,
+		}
+		//校验验证码成功【校验是否需要合并】
+		if step == 2 {
+			uData := util.Compatible.Select(userId, `{"s_unionid":1,"s_name":1,"s_nickname":1,"s_headimageurl":1,"l_registedate":1,"i_ispush":1,"i_applystatus":1,"i_sex":1,"s_country":1,"s_province":1,"s_city":1,"s_m_openid":1,"a_m_openid":1,"base_user_id":1,"s_newsource":1}`)
+			if uData == nil && len(*uData) == 0 {
+				return nil, DBQUERY_ERROR
+			}
+			unionid := qutil.ObjToString((*uData)["s_unionid"])
+			if unionid == "" {
+				return "", fmt.Errorf("获取账户信息异常")
+			}
+			exists, relationPhoneId, _ := jy.NewPhoneUtil(util.MQFW).BindPhoneIsOccupy(userId, unionid, phoneVerify)
+			log.Println("绑定手机号开始2", exists, relationPhoneId)
+			if exists { //再次校验是否使用
+				return nil, fmt.Errorf("手机号已被绑定")
+			}
+			if mode == "" || relationPhoneId == "" { //主动绑定-建立双向绑定关系||跳转绑定新手机号
+				if relationPhoneId == "" {
+					//船新用户 查看是否有邀请关系 shareRedisName
+					go func(openId string) {
+						newSource := qutil.ObjToString((*uData)["s_newsource"])
+						var shareUid string
+						shareKey := fmt.Sprintf("rec_%s", openId)
+						if openId != "" {
+							shareUid = redis.GetStr(config.Config.ShareRedisName, shareKey)
+						}
+						entity.NewShareFission(userId, shareUid, newSource).AddPower(this.Request)
+						if shareUid != "" {
+							redis.Del(config.Config.ShareRedisName, shareKey)
+						}
+					}(qutil.ObjToString((*uData)["s_m_openid"]))
+					//绑定手机号和关注公众号
+					jy.Publish(util.Mgo_log, config.Config.Nsq, config.Config.Nsq_Topic, "task", userId, jy.Jyweb_node2, map[string]interface{}{
+						"code":       1008,
+						"types":      "followWx",
+						"num":        50,
+						"baseUserId": sessVal["base_user_id"],
+						"positionId": sessVal["positionId"],
+					})
+				}
+				data := map[string]interface{}{
+					"s_unionid":      (*uData)["s_unionid"],
+					"s_name":         (*uData)["s_name"],
+					"s_nickname":     (*uData)["s_nickname"],
+					"s_headimageurl": (*uData)["s_headimageurl"],
+				}
+				// 新用户注册 记录来源
+				sourceLabel := redis.GetStr("limitation", fmt.Sprintf("firstVisitTagByWX_%s", this.Session().Id()))
+				if sourceLabel != "" {
+					data["s_rsource"] = sourceLabel
+				} else {
+					activity := this.GetString("activity")
+					if activity != "" {
+						data["s_rsource"] = activity
+					}
+				}
+				if jy.IsEmail(email) {
+					data["s_email"] = email
+				}
+				//建立关联关系
+				if bindErr := jy.NewPhoneUtil(util.MQFW).BindPhone(userId, relationPhoneId, phoneVerify, email, &data); bindErr != nil {
+					return nil, bindErr
+				}
+				//用户中台存储  开始
+				if uinfo := jy.GetInfoForBaseUser(util.MQFW, userId); uinfo != nil {
+					jy.UpdateUser(util.MQFW, userId, *uinfo, *config.Middleground)
+				}
+				//用户中台存储  结束
+				jy.CreateUserMerge(util.MQFW, util.Mysql, this.Session(), config.Middleground).FlushSession(userId) //刷新session
+
+			} else if mode == "mergeBind" { //跳转绑定-不合并绑定无效
+				if qutil.Int64All((*uData)["l_registedate"]) > config.AutoMergeTimeStamp { //新微信直接绑定
+					data := map[string]interface{}{
+						"s_unionid":      (*uData)["s_unionid"],
+						"s_name":         (*uData)["s_name"],
+						"s_nickname":     (*uData)["s_nickname"],
+						"s_headimageurl": (*uData)["s_headimageurl"],
+						"i_ispush":       (*uData)["i_ispush"],
+						"i_applystatus":  (*uData)["i_applystatus"],
+						"i_sex":          (*uData)["i_sex"],
+						"s_country":      (*uData)["s_country"],
+						"s_province":     (*uData)["s_province"],
+						"s_city":         (*uData)["s_country"],
+						"s_m_openid":     (*uData)["s_m_openid"],
+						"a_m_openid":     (*uData)["a_m_openid"],
+						"o_jy.i_wxpush":  1,
+					}
+					if jy.IsEmail(email) {
+						data["s_email"] = email
+					}
+					log.Println("绑定手机号开始3", relationPhoneId, data)
+					if util.Compatible.Update(relationPhoneId, map[string]interface{}{
+						"$set": data,
+					}) {
+						//用户中台存储  开始
+						if uinfo := jy.GetInfoForBaseUser(util.MQFW, mongodb.BsonIdToSId(relationPhoneId)); uinfo != nil {
+							jy.UpdateUser(util.MQFW, mongodb.BsonIdToSId(relationPhoneId), *uinfo, *config.Middleground)
+						}
+						//删除user
+						base_userid_del := usercenter.GetBaseUserId(util.MQFW, userId)
+						jy.DelUser(util.MQFW, userId, pb.UserIdReq{
+							Appid: "10000",
+							Id:    base_userid_del,
+						}, *config.Middleground)
+						//备份用户
+						if backUser, _ := util.MQFW.FindById("user", userId, nil); backUser != nil && len(*backUser) > 0 {
+							backData := map[string]interface{}{
+								"l_mergeData":   time.Now().Unix(),
+								"s_userId":      userId,
+								"s_save_userId": relationPhoneId,
+								"mergeType":     "autoMerge",
+							}
+
+							for k, v := range *backUser {
+								if k == "_id" {
+									continue
+								}
+								backData[k] = v
+							}
+							log.Println("绑定手机号开始4", backData)
+
+							if util.MQFW.Save("user_merge", backData) != "" {
+								util.MQFW.Del("user", map[string]interface{}{"_id": mongodb.StringTOBsonId(userId)})
+								jy.CreateUserMerge(util.MQFW, util.Mysql, this.Session(), config.Middleground).FlushSession(relationPhoneId)
+								redis.Put("session", fmt.Sprintf("usermerge_delete_%s", userId), relationPhoneId, 7*24*60*60)
+							}
+						}
+						//用户中台存储  结束
+						return rData, nil
+					}
+					return nil, fmt.Errorf("账号自动合并异常")
+				} else { //老微信用户选择合并
+					go util.CollectPhone(userId, phoneVerify) //增加user表验证手机号
+					rData["state"] = 2                        //跳转合并页面
+					rData["token"] = jy.MergeEncrypt.Encode2Hex(userId + "&&" + relationPhoneId)
+				}
+			}
+		}
+		return rData, nil
+	}()
+	if errMsg != nil {
+		log.Printf("%s UserAccount PhoneBind 绑定手机号异常:%s\n", userId, errMsg.Error())
+	} else if step == 2 {
+		newSessVal := this.Session().GetMultiple()
+		newUserId, _ := newSessVal["mgoUserId"].(string)
+		jy.Publish(util.Mgo_log, config.Config.Nsq, config.Config.Nsq_Topic, "task", newUserId, jy.Jyweb_node2, map[string]interface{}{
+			"code":       1007,
+			"types":      "bindPhone",
+			"num":        50,
+			"baseUserId": newSessVal["base_user_id"],
+			"positionId": newSessVal["positionId"],
+			"isOnlyBind": true,
+		})
+	}
+	this.ServeJson(NewResult(rData, errMsg))
+}
+
+// 手机号绑定流程
+// 绑定=bind 更改=change
+// 更改手机号
+//
+//	当需要合并时 可以绑定存在的手机号账户
+//	当不需要合并时 只能更改新手机号
+//
+// 解绑完成后 需要解除关联关系
+func (this *UserAccount) PhoneChange() {
+	userId, _ := this.GetSession("mgoUserId").(string)
+	rData, errMsg := func() (interface{}, error) {
+		step, _ := this.GetInteger("step")
+		phone := this.GetString("phone")
+		code := this.GetString("code")
+		if step == 1 { //仅发送验证码 校验是否通过身份验证
+			if pass, data := authenticationCheck(this.GetSession(phoneAuthPassSessionKey)); !pass {
+				return data, nil
+			}
+			//校验图形验证码
+			if pass, err := imgCaptchaCheck(this.Session(), userId, code); !pass || err != nil {
+				return nil, err
+			}
+			//发送短信验证码前,校验手机号是否存在
+			wId, _, _, err := jy.CreateUserMerge(util.MQFW, util.Mysql, this.Session(), config.Middleground).MergeQuery()
+			if err != nil {
+				return nil, err
+			}
+			if exists, _ := jy.NewPhoneUtil(util.MQFW).ChangePhoneIsOccupy(phone, wId != "" && userId == wId); exists {
+				return nil, fmt.Errorf("手机号已被使用")
+			}
+		}
+		//发送验证码&校验验证码逻辑
+		phoneVerify, err := phoneStep(this.Request, this.Session(), step, phone, code, "change")
+		if err != nil {
+			return nil, err
+		}
+		//校验验证码成功绑定手机号
+		if step == 2 {
+			//更换手机号前,再次校验是否存在手机号
+			wId, _, _, err := jy.CreateUserMerge(util.MQFW, util.Mysql, this.Session(), config.Middleground).MergeQuery()
+			if err != nil {
+				return nil, err
+			}
+			exists, relationPhoneId := jy.NewPhoneUtil(util.MQFW).ChangePhoneIsOccupy(phoneVerify, wId != "" && userId == wId)
+			if exists {
+				return nil, fmt.Errorf("手机号已被使用")
+			}
+			uData := util.Compatible.Select(userId, `{"s_phone":1,"s_m_phone":1,"s_unionid":1,"s_name":1,"s_nickname":1,"s_headimageurl":1}`)
+			if uData == nil || len(*uData) == 0 {
+				return nil, DBQUERY_ERROR
+			}
+			beforePhone, _ := qutil.If((*uData)["s_phone"] != nil, (*uData)["s_phone"], (*uData)["s_m_phone"]).(string)
+			if beforePhone == "" {
+				return nil, fmt.Errorf("获取手机号异常")
+			}
+			updateMap := map[string]interface{}{}
+			//商机管理更改手机号逻辑entniche
+			if !util.Mysql.ExecTx("更换管理员老手机号", func(tx *sql.Tx) bool {
+				update_1, update_2 := true, true
+				if util.Mysql.CountBySql("select id from entniche_user where phone=? ", beforePhone) > 0 {
+					update_1 = util.Mysql.UpdateOrDeleteBySqlByTx(tx, `update entniche_user set phone=? where phone=? `, phoneVerify, beforePhone) > -1
+				}
+				if util.Mysql.CountBySql("select id from entniche_info where phone=? ", beforePhone) > 0 {
+					update_2 = util.Mysql.UpdateOrDeleteBySqlByTx(tx, `update entniche_info set phone=? where phone=?`, phoneVerify, beforePhone) > -1
+				}
+				changePhone := map[string]interface{}{}
+				if (*uData)["s_phone"] != nil {
+					changePhone["s_phone"] = phoneVerify
+				}
+				if (*uData)["s_m_phone"] != nil {
+					changePhone["s_m_phone"] = phoneVerify
+				}
+				updateMap["$set"] = changePhone //更改手机号字段
+
+				//解除关联关系
+				//手机号端
+				//   删除微信相关字段
+				//微信端(不查询)
+				//   不做更改
+				phoneUnset := map[string]interface{}{
+					"s_unionid": 1, "s_name": 1, "s_nickname": 1, "s_headimageurl": 1,
+				}
+				wId, pId, err := jy.NewPhoneUtil(util.MQFW).QueryRelation(beforePhone)
+				if err != nil {
+					log.Printf("%s UserAccount ChangePhone 更改手机号异常 %v \n", userId, err)
+					return false
+				}
+				if wId != "" && pId != "" {
+					if userId == wId { //当前为微信账户
+						if err := jy.NewPhoneUtil(util.MQFW).UnBindWeixin(pId, "", "PhoneChange"); err != nil {
+							log.Printf("%s UserAccount ChangePhone 更改手机号异常 %v \n", userId, err)
+							return false
+						}
+					} else {
+						updateMap["$unset"] = phoneUnset
+					}
+				}
+				//记录日志
+				jy.NewPhoneUtil(util.MQFW).Log(userId, "PhoneChange", phoneVerify)
+				return update_1 && update_2
+			}) {
+				return nil, fmt.Errorf("商机管理手机号更改出错")
+			}
+			//设置新号关联关系
+			if relationPhoneId != "" {
+				upsetMap := map[string]interface{}{
+					"s_unionid":      (*uData)["s_unionid"],
+					"s_name":         (*uData)["s_name"],
+					"s_nickname":     (*uData)["s_nickname"],
+					"s_headimageurl": (*uData)["s_headimageurl"],
+				}
+				if err := jy.NewPhoneUtil(util.MQFW).BindWeixin(relationPhoneId, "", "", &upsetMap, "PhoneChange"); err != nil {
+					log.Printf("%s UserAccount ChangePhone 更改手机号异常 %v \n", userId, err)
+				}
+			}
+			if !util.Compatible.Update(userId, updateMap) {
+				log.Printf("%s UserAccount ChangePhone 数据更改手机号异常 \n", userId)
+				return nil, fmt.Errorf("数据更新异常")
+			}
+			//用户中台存储  开始
+			func(phone, userId string) {
+				if uinfo := jy.GetInfoForBaseUser(util.MQFW, userId); uinfo != nil {
+					jy.UpdateUser(util.MQFW, userId, *uinfo, *config.Middleground)
+				}
+			}(phoneVerify, userId)
+			//用户中台存储  结束
+			this.Session().Del(phoneAuthPassSessionKey)
+			jy.CreateUserMerge(util.MQFW, util.Mysql, this.Session(), config.Middleground).FlushSession(userId) //刷新session
+		}
+		return map[string]interface{}{
+			"state": 1,
+		}, nil
+	}()
+	if errMsg != nil {
+		log.Printf("%s UserAccount ChangePhone 更改手机号异常:%s\n", userId, errMsg.Error())
+	}
+	this.ServeJson(NewResult(rData, errMsg))
+}
+
+// 查询是否绑定微信
+// 仅手机号存在才能(绑定|解绑)微信
+func (this *UserAccount) WxBindCheck() {
+	userId, _ := this.GetSession("mgoUserId").(string)
+	rData, errMsg := func() (interface{}, error) {
+		thisUser := util.Compatible.Select(userId, `{"s_unionid":1,"s_phone":1}`)
+		if thisUser == nil || len(*thisUser) == 0 {
+			return nil, DBQUERY_ERROR
+		}
+		binded, showWxBind := false, false
+		if unionid, ok := (*thisUser)["s_unionid"]; ok {
+			if len(qutil.ObjToString(unionid)) > 11 { //手机号用户s_unionid为手机号
+				binded = true
+			}
+		}
+		if phone, ok := (*thisUser)["s_phone"].(string); ok && phone != "" {
+			showWxBind = true
+		}
+		return map[string]interface{}{
+			"binded":     binded,
+			"showWxBind": showWxBind,
+		}, nil
+	}()
+	if errMsg != nil {
+		log.Printf("%s UserAccount WxBindCheck 微信绑定查询出错:%s\n", userId, errMsg.Error())
+	}
+	this.ServeJson(NewResult(rData, errMsg))
+}
+
+// 绑定微信
+// app手机号用户
+// 规则 1.绑定的微信为全新微信
+//
+//	2.绑定的微信非全新微信账号。对应已存在账户A.(完成后建立关联关系)
+//	   A未绑定手机号(绑定后,此账户需要添加关联关系,即设置s_m_phone)
+//	   A账户已绑定手机号,此手机号必须与当前账户手机号一致
+func (this *UserAccount) WxBind() {
+	userId, _ := this.GetSession("mgoUserId").(string)
+	rData, errMsg := func() (interface{}, error) {
+		//认证校验
+		//if pass, data := authenticationCheck(this.GetSession(phoneAuthPassSessionKey)); !pass {
+		//	return data, nil
+		//}
+		uBind, err := jy.GetMsgFromWxSign(this.GetString("param"))
+		if err != nil {
+			return nil, PARAM_ERROR
+		}
+		thisUser := util.Compatible.Select(userId, `{"s_unionid":1,"s_phone":1}`)
+		if thisUser == nil || len(*thisUser) == 0 {
+			return false, DBQUERY_ERROR
+		}
+		//是否绑定
+		if unionid, ok := (*thisUser)["s_unionid"]; ok {
+			if len(qutil.ObjToString(unionid)) > 11 { //手机号用户s_unionid为手机号
+				return nil, fmt.Errorf("请勿重复绑定")
+			}
+		}
+		//仅手机号登录s_phone能绑定&解绑
+		thisPhone, _ := (*thisUser)["s_phone"].(string)
+		if thisPhone == "" {
+			return nil, fmt.Errorf("非法操作")
+		}
+		//绑定微信条件
+		//1.全新微信
+		//2.非全新微信。此号无绑定手机号,或者绑定手机号和当前手机号一致(绑定后此非全新微信,也需要添加手机号字段;建立双向关联关系)
+		exists, relationWeixinId := jy.NewPhoneUtil(util.MQFW).BindWeixinIsOccupy(thisPhone, uBind.UnionId)
+		if exists {
+			return nil, fmt.Errorf("微信被已绑定")
+		}
+		upDateMap := &map[string]interface{}{
+			"s_name":         uBind.Nickname,
+			"s_nickname":     uBind.Nickname,
+			"s_headimageurl": uBind.HeadImageUrl,
+			"s_unionid":      uBind.UnionId,
+		}
+		//建立绑定关系
+		if bindErr := jy.NewPhoneUtil(util.MQFW).BindWeixin(userId, relationWeixinId, thisPhone, upDateMap); bindErr != nil {
+			return nil, bindErr
+		}
+
+		//关联用户中台base_user_id  双向关系
+
+		if uinfo := jy.GetInfoForBaseUser(util.MQFW, userId); uinfo != nil {
+			jy.UpdateUser(util.MQFW, userId, *uinfo, *config.Middleground)
+		}
+		if uinfo := jy.GetInfoForBaseUser(util.MQFW, relationWeixinId); uinfo != nil {
+			jy.UpdateUser(util.MQFW, relationWeixinId, *uinfo, *config.Middleground)
+		}
+		//刷新session
+		jy.CreateUserMerge(util.MQFW, util.Mysql, this.Session(), config.Middleground).FlushSession(userId)
+		return map[string]interface{}{
+			"state": 1,
+		}, nil
+	}()
+	if errMsg != nil {
+		log.Printf("%s UserAccount WxBind 微信绑定出错:%s\n", userId, errMsg.Error())
+	}
+	this.ServeJson(NewResult(rData, errMsg))
+}
+
+// 解绑微信
+// app手机号用户
+// 规则 1.当前账户解绑的微信存在公众号关注用户,则去关后会产生新的公众号用户;否则(已取关,或不存在公众号用户),则直接删除此微信相关字段
+//
+//	2.解绑的微信存在已绑定的手机号(存在关联关系),解绑后解除关联关系
+func (this *UserAccount) WxUnBind() {
+	userId, _ := this.GetSession("mgoUserId").(string)
+	rData, errMsg := func() (interface{}, error) {
+		//认证校验
+		//if pass, data := authenticationCheck(this.GetSession(phoneAuthPassSessionKey)); !pass {
+		//	return data, nil
+		//}
+		unsetMap := util.Compatible.Select(userId, `{"s_m_phone":1,"s_phone":1,"s_unionid":1,"s_name":1,"s_nickname":1,"i_sex":1,"s_country":1,"s_province":1,"s_city":1,"s_headimageurl":1,"s_m_openid":1,"a_m_openid":1,"i_ispush":1,"i_applystatus":1,"s_rsource":1}`)
+		if unsetMap == nil || len(*unsetMap) == 0 {
+			return nil, DBQUERY_ERROR
+		}
+		unionid := qutil.ObjToString((*unsetMap)["s_unionid"])
+		if unionid == "" {
+			return nil, fmt.Errorf("未查询到绑定信息")
+		}
+		//解绑微信必须 仅手机号登录
+		phone, _ := (*unsetMap)["s_phone"].(string)
+		if phone == "" {
+			return nil, fmt.Errorf("非法操作")
+		}
+		s_m_openid, flushWxsession := qutil.ObjToString((*unsetMap)["s_m_openid"]), false
+		if s_m_openid != "" && qutil.IntAll((*unsetMap)["i_ispush"]) == 1 {
+			delete(*unsetMap, "s_m_phone")
+			delete(*unsetMap, "s_phone")
+			(*unsetMap)["l_registedate"] = time.Now().Unix()
+			(*unsetMap)["i_appid"] = 2
+			(*unsetMap)["i_ts_guide"] = 2
+			(*unsetMap)["o_jy"] = map[string]interface{}{
+				"i_wxpush":   1,
+				"i_ratemode": 2,
+				// "i_newfree":  1, //新免费用户=>新订阅设置页面 20211122
+			}
+			userAddReq := &pb.UserAddReq{
+				Appid:   "10000",
+				Unionid: qutil.ObjToString((*unsetMap)["s_unionid"]),
+				SOpenid: s_m_openid,
+			}
+			if qutil.ObjToString((*unsetMap)["s_nickname"]) != "" {
+				userAddReq.Nickname = qutil.ObjToString((*unsetMap)["s_nickname"])
+			}
+			if qutil.ObjToString((*unsetMap)["s_headimageurl"]) != "" {
+				userAddReq.Headimg = qutil.ObjToString((*unsetMap)["s_headimageurl"])
+			}
+			if qutil.ObjToString((*unsetMap)["a_m_openid"]) != "" {
+				userAddReq.AOpenid = qutil.ObjToString((*unsetMap)["a_m_openid"])
+			}
+			if resp := config.Middleground.UserCenter.UserAdd(*userAddReq); resp != nil && resp.Data.Id > 0 {
+				(*unsetMap)["base_user_id"] = resp.Data.Id
+			} else {
+				log.Println(qutil.ObjToString((*unsetMap)["s_unionid"]), "调用usercenter失败")
+				return nil, DBUPDATE_ERROR
+			}
+			//公众号微信关注移至另一用户中  此时微信公众号用户session问题错乱
+			saveId := util.MQFW.Save("user", unsetMap)
+			if saveId == "" {
+				return nil, DBUPDATE_ERROR
+			}
+			go jy.SaveUserLog(util.Mgo_log, saveId, phone, "phone", "wx", "wx", "", "", gconv.String(this.GetSession("RSource")), this.Request.Proto, this.UserAgent(), gconv.String(qutil.If(gconv.String((*unsetMap)["s_rsource"]) == "jyzbw", "jyzbw", "jybx")), "")
+
+			flushWxsession = true
+		}
+
+		//查询是否存在关联账户
+		wId, _, err := jy.NewPhoneUtil(util.MQFW).QueryRelation(phone)
+		if err != nil {
+			return nil, err
+		}
+		//解除绑定
+		if err := jy.NewPhoneUtil(util.MQFW).UnBindWeixin(userId, wId); err != nil {
+			return nil, err
+		}
+		//解除baseuser绑定
+		if uinfo := jy.GetInfoForBaseUser(util.MQFW, userId); uinfo != nil {
+			jy.UpdateUser(util.MQFW, userId, *uinfo, *config.Middleground)
+		}
+
+		//微信解绑成功
+		//1.微信账户刷新session
+		if flushWxsession {
+			redis.Put("session", fmt.Sprintf("accountInfo_unbindWx_%s_%s", userId, s_m_openid), fmt.Sprintf("%d", time.Now().Unix()), 7*24*60*60)
+		}
+		//清除session中企业账户信息
+		for _, entSessionKey := range []string{"entId", "entName", "entUserId", "frameworkEntId", "frameworkEntName"} {
+			this.DelSession(entSessionKey)
+		}
+
+		//2.刷新当前账户session
+		jy.CreateUserMerge(util.MQFW, util.Mysql, this.Session(), config.Middleground).FlushSession(userId)
+		return map[string]interface{}{
+			"state": 1,
+		}, nil
+	}()
+	if errMsg != nil {
+		log.Printf("%s UserAccount WxBind 微信解绑出错:%s\n", userId, errMsg.Error())
+	}
+	this.ServeJson(NewResult(rData, errMsg))
+}
+
+func (u *UserAccount) ShareUserId() {
+	userId, _ := u.GetSession("userId").(string)
+	rData, errMsg := func() (interface{}, error) {
+		if userId == "" {
+			return nil, DBQUERY_ERROR
+		}
+		return map[string]interface{}{
+			"shareUserId": encrypt.SE.Encode2Hex(userId),
+			"webdomain":   config.Config.WebDomain,
+		}, nil
+	}()
+	u.ServeJson(NewResult(rData, errMsg))
+}
+
+//util---------------------------------
+
+// 图形验证码校验
+func imgCaptchaCheck(sess *httpsession.Session, userId, code string) (bool, error) {
+	if redis.GetInt("other", fmt.Sprintf(redisPhoneCaptchaFilter, userId, time.Now().Day())) >= phoneSendNoImgFreeTime { //需要验证图形验证码
+		sessValue, _ := sess.Get("UserAccountPhoneCaptcha").(string)
+		sess.Del("UserAccountPhoneCaptcha") //验证一次即清除
+		if !captcha.VerifyString(sessValue, code) {
+			return false, fmt.Errorf("图形验证码错误")
+		}
+	}
+	return true, nil
+}
+
+// 身份验证
+func authenticationCheck(authStampInf interface{}) (bool, map[string]interface{}) {
+	authStamp := qutil.Int64All(authStampInf)
+	if authStamp != 0 && time.Now().Before(time.Unix(authStamp, 0).Add(time.Minute*15)) {
+		return true, nil
+	}
+	return false, map[string]interface{}{
+		"state": -1,
+	}
+}
+
 // 手机号绑定流程
 // 绑定=bind 更改=change
 func phoneStep(r *http.Request, sess *httpsession.Session, step int, phone, code, sign string) (string, error) {
@@ -397,72 +1292,6 @@ func getPhoneByUserId(userId string) (phone string) {
 	return ""
 }
 
-// 我的页面 获取基本信息
-// 手机号 邮箱 头像 昵称 超级订阅 大会员 用户加密id
-func (this *UserAccount) GetAccountInfo() {
-	rData, errMsg := func() (interface{}, error) {
-		sessVal := this.Session().GetMultiple()
-		userId, _ := sessVal["mgoUserId"].(string)
-		//由于超级订阅vip状态需要查库,无法从session中获取,所以直接所有字段从数据库中获取
-		userMsg := util.Compatible.Select(userId, `{"s_m_phone":1,"s_phone":1,"s_myemail":1,"s_nickname":1,"s_headimageurl":1,"s_password":1,"s_company":1,"s_unionid":1}`)
-		if userMsg == nil || len(*userMsg) == 0 {
-			return nil, DBQUERY_ERROR
-		}
-		//微信昵称>手机号>剑鱼昵称
-		nickname := qutil.ObjToString((*userMsg)["s_nickname"])
-		phone := qutil.ObjToString(qutil.If((*userMsg)["s_phone"] != nil, (*userMsg)["s_phone"], (*userMsg)["s_m_phone"]))
-		jyname := qutil.ObjToString((*userMsg)["s_jyname"])
-		isWx := false
-		if unionid, ok := (*userMsg)["s_unionid"]; ok {
-			if len(qutil.ObjToString(unionid)) > 11 { //手机号用户s_unionid为手机号
-				isWx = true
-			}
-		}
-		breakRenewTip := 0
-		orders := util.Mysql.SelectBySql(`SELECT pay_time from dataexport_order where user_id=? and order_status=1 and product_type='VIP订阅' and vip_type is null order by pay_time desc limit 1`, userId)
-		if orders != nil && len(*orders) == 1 {
-			timeA, errorA := time.ParseInLocation(Date_Full_Layout, config.Config.BreakRenewTipTime, time.Local)
-			timeB, errorB := time.ParseInLocation(Date_Full_Layout, qutil.ObjToString((*orders)[0]["pay_time"]), time.Local)
-			if errorA == nil && errorB == nil && timeB.Before(timeA) {
-				breakRenewTip = 1
-			}
-		}
-		b := jy.GetBigVipUserBaseMsg(this.Session(), *config.Middleground)
-		//s从数据导出填写的邮箱或者从数据定制服务这里获取的邮箱
-		_, otherMail := dataexport.GetLastExportPhoneAndMail(util.Mysql, qutil.ObjToString(sessVal["userId"]))
-		if otherMail == "" {
-			saleData, _ := util.MQFW.FindOne("saleLeads", map[string]interface{}{
-				"userid": sessVal["userId"],
-				"mail": map[string]interface{}{
-					"$exists": true,
-				},
-			})
-			if saleData != nil && len(*saleData) > 0 {
-				otherMail = qutil.ObjToString((*saleData)["mail"])
-			}
-			if qutil.ObjToString((*userMsg)["s_myemail"]) != "" {
-				otherMail = qutil.ObjToString((*userMsg)["s_myemail"])
-			}
-		}
-		return map[string]interface{}{
-			"userId":        encrypt.EncodeArticleId(userId),
-			"email":         qutil.ObjToString((*userMsg)["s_myemail"]),
-			"phone":         phone,
-			"nickname":      nickname,
-			"jyname":        jyname,
-			"headimageurl":  strings.Replace(qutil.ObjToString((*userMsg)["s_headimageurl"]), "http://", "https://", 1),
-			"bigmemberVip":  b.Status,
-			"subscribeVip":  b.VipStatus,
-			"hasPwd":        qutil.ObjToString((*userMsg)["s_password"]) != "",
-			"company":       qutil.ObjToString((*userMsg)["s_company"]),
-			"isWx":          isWx, //是否是微信账号
-			"breakRenewTip": breakRenewTip,
-			"otherMail":     otherMail, //从数据导出填写的邮箱或者从数据定制服务这里获取的邮箱
-		}, nil
-	}()
-	this.ServeJson(NewResult(rData, errMsg))
-}
-
 // 获取用户unionid
 func getWxUnionId(userId string) (uninoId string) {
 	if userId != "" {

+ 1 - 1
src/jfw/modules/subscribepay/src/service/vipGift.go

@@ -111,7 +111,7 @@ func (t *VipGift) InformInfo() {
 	chatIdInt := gconv.Int64(encrypt.SE.Decode4HexByCheck(giftId))
 	data := util.Mysql.FindOne("vip_gift_records", map[string]interface{}{
 		"id": chatIdInt,
-	}, "giftUserPhone,createTime,recipientUserPhone,duration,nickname,vipStartTime,vipEndTime,areacount,itype", "")
+	}, "giftUserPhone,createTime,recipientUserPhone,recipientUserId,duration,nickname,vipStartTime,vipEndTime,areacount,itype", "")
 	if data != nil && len(*data) > 0 {
 		giftUserPhone := gconv.String((*data)["giftUserPhone"])
 		(*data)["giftUserPhone"] = string(giftUserPhone[0:3]) + "****" + string(giftUserPhone[len(giftUserPhone)-4:])