app_info.go 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448
  1. package member
  2. import (
  3. "context"
  4. "fmt"
  5. "net/url"
  6. "time"
  7. "unicode/utf8"
  8. "go-common/app/interface/main/account/model"
  9. accModel "go-common/app/service/main/account/model"
  10. coModel "go-common/app/service/main/coin/model"
  11. ftModel "go-common/app/service/main/filter/model/rpc"
  12. locModel "go-common/app/service/main/location/model"
  13. meModel "go-common/app/service/main/member/model"
  14. "go-common/library/ecode"
  15. "go-common/library/log"
  16. "go-common/library/net/metadata"
  17. "go-common/library/queue/databus/report"
  18. "github.com/pkg/errors"
  19. )
  20. var monitoredIPCountry = map[string]string{
  21. "台湾": "台湾 IP",
  22. "香港": "香港 IP",
  23. "美国": "美国 IP",
  24. "加拿大": "加拿大 IP",
  25. }
  26. var monitoredTelCountry = map[int64]string{
  27. 1: "美国/加拿大手机号",
  28. }
  29. var upNameCostCoins = 6.0
  30. // Account get Account info.
  31. func (s *Service) Account(c context.Context, mid int64, ip string) (acc *model.Account, err error) {
  32. var (
  33. nickFree *model.NickFree
  34. mb *meModel.Member
  35. )
  36. marg := &meModel.ArgMemberMid{Mid: mid, RemoteIP: ip}
  37. if mb, err = s.memRPC.Member(c, marg); err != nil {
  38. log.Error("service.memberRPC.MyInfo(%v) error(%v)", marg, err)
  39. return
  40. }
  41. if nickFree, err = s.NickFree(c, mid); err != nil {
  42. return
  43. }
  44. acc = &model.Account{}
  45. acc.Mid = mid
  46. acc.Birthday = mb.Birthday.Time().Format("2006-01-02")
  47. acc.Uname = mb.Name
  48. acc.Face = mb.Face
  49. acc.Sign = mb.Sign
  50. acc.Sex = int8(mb.Sex)
  51. acc.NickFree = nickFree.NickFree
  52. return
  53. }
  54. // UpdateFace Update Face
  55. func (s *Service) UpdateFace(c context.Context, mid int64, faceFile []byte, ftype string) (string, error) {
  56. ip := metadata.String(c, metadata.RemoteIP)
  57. // check
  58. profile, err := s.accRPC.Profile3(c, &accModel.ArgMid{Mid: mid})
  59. if err != nil {
  60. return "", errors.WithStack(err)
  61. }
  62. //判断是否绑定手机号
  63. if !s.validateTelStatus(profile.TelStatus) {
  64. return "", ecode.MemberPhoneRequired
  65. }
  66. if profile.Silence != 0 {
  67. return "", ecode.MemberBlocked
  68. }
  69. //Upload bfs
  70. faceURL, err := s.accDao.UploadImage(c, ftype, faceFile, s.c.FaceBFS)
  71. if err != nil {
  72. log.Error("s.bfsDao.Upload(%d) error(%v)", mid, err)
  73. return "", errors.WithStack(err)
  74. }
  75. URL, err := url.Parse(faceURL)
  76. if err != nil {
  77. return "", errors.WithStack(err)
  78. }
  79. inMonitor := s.ensureMonitor(c, mid, ip)
  80. arg := &meModel.ArgAddPropertyReview{
  81. Mid: mid,
  82. New: URL.Path,
  83. State: meModel.ReviewStateQueuing,
  84. Property: meModel.ReviewPropertyFace,
  85. }
  86. if inMonitor {
  87. arg.State = meModel.ReviewStateWait
  88. return profile.Face, s.memRPC.AddPropertyReview(c, arg)
  89. }
  90. if err := s.memRPC.AddPropertyReview(c, arg); err != nil {
  91. log.Error("s.memRPC.AddPropertyReview(%d,%s) error(%v)", mid, faceFile, err)
  92. return "", errors.WithStack(err)
  93. }
  94. if err := s.memRPC.SetFace(c, &meModel.ArgUpdateFace{Mid: mid, Face: URL.Path}); err != nil {
  95. log.Error("s.memRPC.SetFace(%d,%s) error(%v)", mid, faceURL, err)
  96. return "", errors.WithStack(err)
  97. }
  98. return faceURL, nil
  99. }
  100. // UpdateName .
  101. func (s *Service) UpdateName(c context.Context, mid int64, name, appkey string) error {
  102. ip := metadata.String(c, metadata.RemoteIP)
  103. _, inWhiteList := s.nickFreeAppKeys[appkey]
  104. if inWhiteList {
  105. return s.updateNameWithinWhiteList(c, mid, name, ip)
  106. }
  107. return s.updateName(c, mid, name, ip)
  108. }
  109. // updateNameWithinWhiteList 白名单 appkey 不扣硬币
  110. func (s *Service) updateNameWithinWhiteList(c context.Context, mid int64, name, ip string) error {
  111. if err := s.nameIsValid(c, mid, name, ip); err != nil {
  112. return err
  113. }
  114. profile, err := s.accRPC.Profile3(c, &accModel.ArgMid{Mid: mid})
  115. if err != nil {
  116. return errors.WithStack(err)
  117. }
  118. if err := s.permitName(c, profile, ip); err != nil {
  119. return err
  120. }
  121. if profile.Name == name {
  122. log.Info("Update name is same to origin: mid: %d, name: %s, origin: %s", mid, name, profile.Name)
  123. return nil
  124. }
  125. inMonitor := s.ensureMonitor(c, mid, ip)
  126. remark := "appkey白名单修改昵称"
  127. // 在监控列表里就加入添加审核列表
  128. if inMonitor {
  129. saveUpNameLog(mid, profile.Name, name, remark, inMonitor, ip)
  130. return errors.WithStack(s.memRPC.AddPropertyReview(c, &meModel.ArgAddPropertyReview{
  131. Mid: mid,
  132. New: name,
  133. State: meModel.ReviewStateWait,
  134. Property: meModel.ReviewPropertyName,
  135. Extra: map[string]interface{}{"nick_free": true},
  136. }))
  137. }
  138. //修改昵称
  139. if err := s.passDao.UpdateName(c, mid, name, ip); err != nil {
  140. return errors.WithStack(err)
  141. }
  142. saveUpNameLog(mid, profile.Name, name, remark, inMonitor, ip)
  143. return nil
  144. }
  145. //UpdateName update name.
  146. func (s *Service) updateName(c context.Context, mid int64, name, ip string) error {
  147. if err := s.nameIsValid(c, mid, name, ip); err != nil {
  148. return err
  149. }
  150. profile, err := s.accRPC.Profile3(c, &accModel.ArgMid{Mid: mid})
  151. if err != nil {
  152. return errors.WithStack(err)
  153. }
  154. if err = s.permitName(c, profile, ip); err != nil {
  155. return err
  156. }
  157. if profile.Name == name {
  158. log.Info("Update name is same to origin: mid: %d, name: %s, origin: %s", mid, name, profile.Name)
  159. return nil
  160. }
  161. // 判断是否改昵称免费
  162. nickFree, err := s.NickFree(c, mid)
  163. if err != nil {
  164. return err
  165. }
  166. remark := "快速注册修改昵称"
  167. if !nickFree.NickFree {
  168. coins, coinErr := s.coinRPC.UserCoins(c, &coModel.ArgCoinInfo{Mid: mid, RealIP: ip})
  169. if coinErr != nil {
  170. return errors.WithStack(coinErr)
  171. }
  172. if coins < upNameCostCoins {
  173. return ecode.UpdateUnameMoneyIsNot
  174. }
  175. remark = "修改昵称"
  176. }
  177. inMonitor := s.ensureMonitor(c, mid, ip)
  178. // 在监控列表里就加入添加审核列表
  179. if inMonitor {
  180. saveUpNameLog(mid, profile.Name, name, remark, inMonitor, ip)
  181. return errors.WithStack(s.memRPC.AddPropertyReview(c, &meModel.ArgAddPropertyReview{
  182. Mid: mid,
  183. New: name,
  184. State: meModel.ReviewStateWait,
  185. Property: meModel.ReviewPropertyName,
  186. Extra: map[string]interface{}{"nick_free": nickFree.NickFree},
  187. }))
  188. }
  189. //修改昵称
  190. if err = s.passDao.UpdateName(c, mid, name, ip); err != nil {
  191. return errors.WithStack(err)
  192. }
  193. saveUpNameLog(mid, profile.Name, name, remark, inMonitor, ip)
  194. if nickFree.NickFree {
  195. return errors.WithStack(s.memRPC.SetNickUpdated(c, &meModel.ArgMemberMid{Mid: mid}))
  196. }
  197. //扣除硬币
  198. if _, err = s.coinRPC.ModifyCoin(c, &coModel.ArgModifyCoin{
  199. Mid: mid,
  200. Count: -upNameCostCoins,
  201. Reason: fmt.Sprintf("UPDATE:NICK:%s=>%s", profile.Name, name),
  202. IP: ip,
  203. }); err != nil {
  204. return errors.WithStack(err)
  205. }
  206. return nil
  207. }
  208. func (s *Service) monitorByIP(ctx context.Context, mid int64, ip string) (bool, string) {
  209. IP, err := s.locRPC.Info(ctx, &locModel.ArgIP{IP: ip})
  210. if err != nil || IP == nil {
  211. log.Error("Failed to get ip info with ip: %s: %+v", ip, err)
  212. return false, ""
  213. }
  214. descr, shouldMonitor := monitoredIPCountry[IP.Country]
  215. if !shouldMonitor {
  216. return false, ""
  217. }
  218. return true, descr
  219. }
  220. func (s *Service) monitorByTel(ctx context.Context, mid int64, ip string) (bool, string) {
  221. p, err := s.passDao.QueryByMid(ctx, mid, ip)
  222. if err != nil {
  223. log.Error("Failed to query by mid form pasport: mid: %d: %+v", mid, err)
  224. return false, ""
  225. }
  226. descr, shouldMonitor := monitoredTelCountry[p.CountryCode]
  227. if !shouldMonitor {
  228. return false, ""
  229. }
  230. return true, descr
  231. }
  232. func (s *Service) shouldMonitor(ctx context.Context, mid int64, ip string) (bool, string) {
  233. should, descr := s.monitorByIP(ctx, mid, ip)
  234. if should {
  235. return true, descr
  236. }
  237. should, descr = s.monitorByTel(ctx, mid, ip)
  238. if should {
  239. return true, descr
  240. }
  241. return false, ""
  242. }
  243. func (s *Service) ensureMonitor(ctx context.Context, mid int64, ip string) bool {
  244. inMonitor, _ := s.memRPC.IsInMonitor(ctx, &meModel.ArgMid{Mid: mid})
  245. if inMonitor {
  246. return true
  247. }
  248. should, descr := s.shouldMonitor(ctx, mid, ip)
  249. if !should {
  250. return false
  251. }
  252. if err := s.memRPC.AddUserMonitor(ctx, &meModel.ArgAddUserMonitor{
  253. Mid: mid,
  254. Operator: "system",
  255. Remark: fmt.Sprintf("系统自动导入-%s", descr),
  256. }); err != nil {
  257. log.Error("Failed to add user moniter: mid: %d: %+v", mid, err)
  258. }
  259. return true
  260. }
  261. // UpdateSex update sex.
  262. func (s *Service) UpdateSex(c context.Context, mid, sex int64) (err error) {
  263. ip := metadata.String(c, metadata.RemoteIP)
  264. return s.accDao.UpdateSex(c, mid, sex, ip)
  265. }
  266. //UpdateSign update sign.
  267. func (s *Service) UpdateSign(c context.Context, mid int64, sign string) error {
  268. ip := metadata.String(c, metadata.RemoteIP)
  269. // 签名最长 70 个字符
  270. if utf8.RuneCountInString(sign) > 70 {
  271. return ecode.MemberSignOverLimit
  272. }
  273. // 签名不能包含 emoji
  274. if model.HasEmoji(sign) {
  275. return ecode.MemberSignHasEmoji
  276. }
  277. // 过滤敏感词
  278. res, err := s.filterRPC.Filter(c, &ftModel.ArgFilter{Area: "sign", Message: sign})
  279. if err != nil {
  280. return err
  281. }
  282. // 大于 20 认为包含敏感词
  283. if res.Level >= 20 {
  284. return ecode.MemberSignSensitive
  285. }
  286. // 检查是否绑定手机
  287. profile, err := s.accRPC.Profile3(c, &accModel.ArgMid{Mid: mid})
  288. if err != nil {
  289. return errors.WithStack(err)
  290. }
  291. if !s.validateTelStatus(profile.TelStatus) {
  292. return ecode.MemberPhoneRequired
  293. }
  294. // 检查是否被禁言
  295. if profile.Silence != 0 {
  296. return ecode.MemberBlocked
  297. }
  298. // 如果和老的一模一样就没必要更新了
  299. if profile.Sign == sign {
  300. log.Info("Update sign is same to origin: mid: %d, sign: %s, origin: %s", mid, sign, profile.Sign)
  301. return nil
  302. }
  303. inMonitor := s.ensureMonitor(c, mid, ip)
  304. // 不在监控列表里就直接更新
  305. if !inMonitor {
  306. return errors.WithStack(s.memRPC.SetSign(c, &meModel.ArgUpdateSign{
  307. Mid: mid,
  308. Sign: sign,
  309. RemoteIP: ip,
  310. }))
  311. }
  312. // 否则就加入监控列表
  313. return errors.WithStack(s.memRPC.AddPropertyReview(c, &meModel.ArgAddPropertyReview{
  314. Mid: mid,
  315. New: sign,
  316. State: meModel.ReviewStateWait,
  317. Property: meModel.ReviewPropertySign,
  318. }))
  319. }
  320. // UpdateBirthday update birthday.
  321. func (s *Service) UpdateBirthday(c context.Context, mid int64, birthday string) (err error) {
  322. ip := metadata.String(c, metadata.RemoteIP)
  323. return s.accDao.UpdateBirthday(c, mid, ip, birthday)
  324. }
  325. // NickFree .
  326. func (s *Service) NickFree(c context.Context, mid int64) (nickFree *model.NickFree, err error) {
  327. var (
  328. isRegFast bool
  329. nickUpdated bool
  330. ip = metadata.String(c, metadata.RemoteIP)
  331. )
  332. sarg := &meModel.ArgMemberMid{Mid: mid}
  333. if nickUpdated, err = s.memRPC.NickUpdated(c, sarg); err != nil {
  334. log.Error("s.memRPC.IsUpNickFree(%v) error (%v)", sarg, err)
  335. return
  336. }
  337. nickFree = &model.NickFree{}
  338. if nickUpdated {
  339. return
  340. }
  341. if isRegFast, err = s.passDao.FastReg(c, mid, ip); err != nil {
  342. return
  343. }
  344. if isRegFast {
  345. nickFree.NickFree = true
  346. }
  347. return
  348. }
  349. func saveUpNameLog(mid int64, oName, nName, remark string, isMonitor bool, ip string) {
  350. report.User(&report.UserInfo{
  351. Mid: mid,
  352. Business: model.UpNameLogID,
  353. Action: model.UpNameAction,
  354. IP: ip,
  355. Ctime: time.Now(),
  356. Index: []interface{}{0, 0, 0, oName, nName, remark},
  357. Content: map[string]interface{}{
  358. "is_monitor": isMonitor,
  359. "old_name": oName,
  360. "new_name": nName,
  361. "reason": fmt.Sprintf("修改昵称(原昵称:%s 新昵称:%s)", oName, nName),
  362. "remark": remark,
  363. },
  364. })
  365. }
  366. func (s *Service) permitName(c context.Context, profile *accModel.Profile, ip string) error {
  367. if !s.validateTelStatus(profile.TelStatus) {
  368. return ecode.MemberPhoneRequired
  369. }
  370. // 检查是否被禁言
  371. if profile.Silence != 0 {
  372. return ecode.MemberBlocked
  373. }
  374. //昵称锁定,是否官方认证
  375. if profile.Official.Role != 0 {
  376. log.Info("update name fail, name is official, mid: %d", profile.Mid)
  377. return ecode.UpdateUnameHadOfficial
  378. }
  379. pProfile, err := s.passDao.QueryByMid(c, profile.Mid, ip)
  380. if err != nil {
  381. return err
  382. }
  383. if pProfile.NickLock == 1 {
  384. log.Info("update name fail, name is locked, mid: %d", profile.Mid)
  385. return ecode.UpdateUnameHadLocked
  386. }
  387. return nil
  388. }
  389. func (s *Service) nameIsValid(c context.Context, mid int64, name, ip string) error {
  390. if len(name) > 30 || utf8.RuneCountInString(name) > 16 {
  391. return ecode.UpdateUnameTooLong
  392. }
  393. if utf8.RuneCountInString(name) < 3 {
  394. return ecode.UpdateUnameTooShort
  395. }
  396. if !model.ValidName(name) {
  397. return ecode.UpdateUnameFormat
  398. }
  399. // 判断昵称是否重复
  400. if err := s.passDao.TestUserName(c, name, mid, ip); err != nil {
  401. return err
  402. }
  403. // 过滤敏感词
  404. res, err := s.filterRPC.Filter(c, &ftModel.ArgFilter{Area: "member", Message: name})
  405. if err != nil {
  406. return err
  407. }
  408. // 大于 20 认为包含敏感词
  409. if res.Level >= 20 {
  410. return ecode.UpdateUnameSensitive
  411. }
  412. return nil
  413. }
  414. func (s *Service) validateTelStatus(status int32) bool {
  415. if s.c.Switch.UpdatePropertyPhoneRequired && status == 0 {
  416. return false
  417. }
  418. return true
  419. }