realname.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. package service
  2. import (
  3. "bufio"
  4. "bytes"
  5. "context"
  6. "crypto/md5"
  7. "encoding/hex"
  8. "fmt"
  9. "image"
  10. "image/jpeg"
  11. "image/png"
  12. "io/ioutil"
  13. mrand "math/rand"
  14. "os"
  15. "strconv"
  16. "strings"
  17. "syscall"
  18. "time"
  19. "go-common/app/admin/main/member/conf"
  20. "go-common/app/admin/main/member/model"
  21. memmdl "go-common/app/service/main/member/model"
  22. "go-common/library/ecode"
  23. "go-common/library/log"
  24. "go-common/library/net/metadata"
  25. "go-common/library/queue/databus/report"
  26. "github.com/nfnt/resize"
  27. "github.com/pkg/errors"
  28. )
  29. // consts
  30. const (
  31. _512KiloBytes = 512 * 1024
  32. )
  33. // RealnameList .
  34. func (s *Service) RealnameList(ctx context.Context, arg *model.ArgRealnameList) (list []*model.RespRealnameApply, total int, err error) {
  35. list = make([]*model.RespRealnameApply, 0)
  36. switch arg.Channel {
  37. case model.ChannelMain:
  38. return s.realnameMainList(ctx, arg)
  39. case model.ChannelAlipay:
  40. return s.realnameAlipayList(ctx, arg)
  41. }
  42. return
  43. }
  44. func (s *Service) realnameMainList(ctx context.Context, arg *model.ArgRealnameList) (list []*model.RespRealnameApply, total int, err error) {
  45. var (
  46. dl []*model.DBRealnameApply
  47. )
  48. if arg.Card != "" {
  49. // 如果查询证件号, 则通过证件号MD5 realname_info 查询到 对应的 mids
  50. var (
  51. infos []*model.DBRealnameInfo
  52. mids []int64
  53. md5 = cardMD5(arg.Card, arg.DBCardType(), arg.Country)
  54. )
  55. log.Info("realnameMainList card: %s, md5: %s", arg.Card, md5)
  56. if infos, err = s.dao.RealnameInfoByCardMD5(ctx, md5, arg.State.DBStatus(), model.ChannelMain.DBChannel()); err != nil {
  57. return
  58. }
  59. log.Info("realnameMainList infos : %+v", infos)
  60. if len(infos) <= 0 {
  61. return
  62. }
  63. for _, i := range infos {
  64. log.Info("realnameMainList info: %+v", i)
  65. mids = append(mids, i.MID)
  66. }
  67. log.Info("realnameMainList mids: %+v", mids)
  68. if dl, total, err = s.dao.RealnameMainList(ctx, mids, arg.DBCardType(), arg.DBCountry(), arg.OPName, arg.TSFrom, arg.TSTo, arg.DBState(), arg.PN, arg.PS, arg.IsDesc); err != nil {
  69. return
  70. }
  71. } else {
  72. var (
  73. mids []int64
  74. )
  75. if arg.MID > 0 {
  76. mids = append(mids, arg.MID)
  77. }
  78. if dl, total, err = s.dao.RealnameMainList(ctx, mids, arg.DBCardType(), arg.DBCountry(), arg.OPName, arg.TSFrom, arg.TSTo, arg.DBState(), arg.PN, arg.PS, arg.IsDesc); err != nil {
  79. return
  80. }
  81. }
  82. var (
  83. midMap = make(map[int64]int) // map[mid]count
  84. mids []int64
  85. imgIDs []int64
  86. )
  87. // 审核 db 数据解析进 list
  88. for _, d := range dl {
  89. midMap[d.MID] = 0
  90. var (
  91. r = &model.RespRealnameApply{}
  92. )
  93. r.ParseDBMainApply(d)
  94. imgIDs = append(imgIDs, d.HandIMG, d.FrontIMG, d.BackIMG)
  95. list = append(list, r)
  96. }
  97. // 没有数据则返回
  98. if len(midMap) <= 0 {
  99. return
  100. }
  101. // 获取实名申请次数
  102. for mid := range midMap {
  103. if midMap[mid], err = s.dao.RealnameApplyCount(ctx, mid); err != nil {
  104. return
  105. }
  106. mids = append(mids, mid)
  107. }
  108. // 获取mid的昵称 & 等级信息
  109. var (
  110. memsArg = &memmdl.ArgMemberMids{
  111. Mids: mids,
  112. }
  113. memMap map[int64]*memmdl.Member
  114. imgMap map[int64]*model.DBRealnameApplyIMG
  115. )
  116. if memMap, err = s.memberRPC.Members(ctx, memsArg); err != nil {
  117. err = errors.WithStack(err)
  118. return
  119. }
  120. // 获取证件照信息
  121. if imgMap, err = s.dao.RealnameApplyIMG(ctx, imgIDs); err != nil {
  122. return
  123. }
  124. for _, ra := range list {
  125. if mem, ok := memMap[ra.MID]; ok {
  126. ra.ParseMember(mem)
  127. }
  128. for _, id := range ra.IMGIDs {
  129. if img, ok := imgMap[id]; ok {
  130. ra.ParseDBApplyIMG(img.IMGData)
  131. }
  132. }
  133. ra.Times = midMap[ra.MID]
  134. }
  135. return
  136. }
  137. func (s *Service) realnameAlipayList(ctx context.Context, arg *model.ArgRealnameList) (list []*model.RespRealnameApply, total int, err error) {
  138. var (
  139. dl []*model.DBRealnameAlipayApply
  140. )
  141. if arg.Card != "" {
  142. // 如果查询证件号, 则通过证件号MD5 realname_info 查询到 对应的 mids
  143. var (
  144. infos []*model.DBRealnameInfo
  145. mids []int64
  146. md5 = cardMD5(arg.Card, arg.DBCardType(), arg.Country)
  147. )
  148. log.Info("realnameAlipayList card: %s, md5: %s", arg.Card, md5)
  149. if infos, err = s.dao.RealnameInfoByCardMD5(ctx, md5, arg.State.DBStatus(), model.ChannelAlipay.DBChannel()); err != nil {
  150. return
  151. }
  152. log.Info("realnameAlipayList infos : %+v", infos)
  153. if len(infos) <= 0 {
  154. return
  155. }
  156. for _, i := range infos {
  157. log.Info("realnameAlipayList info: %+v", i)
  158. mids = append(mids, i.MID)
  159. }
  160. log.Info("realnameAlipayList mids: %+v", mids)
  161. if dl, total, err = s.dao.RealnameAlipayList(ctx, mids, 0, 0, arg.State.DBStatus(), arg.PN, arg.PS, arg.IsDesc); err != nil {
  162. return
  163. }
  164. } else {
  165. var (
  166. mids []int64
  167. )
  168. if arg.MID > 0 {
  169. mids = append(mids, arg.MID)
  170. }
  171. if dl, total, err = s.dao.RealnameAlipayList(ctx, mids, arg.TSFrom, arg.TSTo, arg.State.DBStatus(), arg.PN, arg.PS, arg.IsDesc); err != nil {
  172. return
  173. }
  174. }
  175. log.Info("realnameAlipayList dl: %+v, total: %d", dl, total)
  176. var (
  177. midMap = make(map[int64]int)
  178. )
  179. // append to list
  180. for _, d := range dl {
  181. midMap[d.MID] = 0
  182. var (
  183. r = &model.RespRealnameApply{}
  184. )
  185. r.ParseDBAlipayApply(d)
  186. list = append(list, r)
  187. }
  188. if len(midMap) <= 0 {
  189. return
  190. }
  191. var mids []int64
  192. for mid := range midMap {
  193. if midMap[mid], err = s.dao.RealnameApplyCount(ctx, mid); err != nil {
  194. return
  195. }
  196. mids = append(mids, mid)
  197. }
  198. var (
  199. memsArg = &memmdl.ArgMemberMids{
  200. Mids: mids,
  201. }
  202. memMap map[int64]*memmdl.Member
  203. )
  204. if memMap, err = s.memberRPC.Members(ctx, memsArg); err != nil {
  205. err = errors.WithStack(err)
  206. return
  207. }
  208. for _, ra := range list {
  209. if mem, ok := memMap[ra.MID]; ok {
  210. ra.ParseMember(mem)
  211. }
  212. ra.Times = midMap[ra.MID]
  213. }
  214. return
  215. }
  216. func cardMD5(card string, cardType int, country int) (res string) {
  217. if card == "" || cardType < 0 || country < 0 {
  218. return
  219. }
  220. var (
  221. lowerCode = strings.ToLower(card)
  222. key = fmt.Sprintf("%s_%s_%d_%d", model.RealnameSalt, lowerCode, cardType, country)
  223. )
  224. return fmt.Sprintf("%x", md5.Sum([]byte(key)))
  225. }
  226. // RealnamePendingList .
  227. func (s *Service) RealnamePendingList(ctx context.Context, arg *model.ArgRealnamePendingList) (list []*model.RespRealnameApply, total int, err error) {
  228. var (
  229. larg = &model.ArgRealnameList{
  230. Channel: arg.Channel,
  231. State: model.RealnameApplyStatePending,
  232. TSFrom: time.Now().Add(-time.Hour * 24 * 7).Unix(),
  233. PS: arg.PS,
  234. PN: arg.PN,
  235. }
  236. )
  237. return s.RealnameList(ctx, larg)
  238. }
  239. // RealnameAuditApply .
  240. func (s *Service) RealnameAuditApply(ctx context.Context, arg *model.ArgRealnameAuditApply, adminName string, adminID int64) (err error) {
  241. var (
  242. mid int64
  243. )
  244. // 1. check the apply state
  245. switch arg.Channel {
  246. case model.ChannelMain:
  247. var apply *model.DBRealnameApply
  248. if apply, err = s.dao.RealnameMainApply(ctx, arg.ID); err != nil {
  249. return
  250. }
  251. if apply.IsPassed() {
  252. return
  253. }
  254. mid = apply.MID
  255. case model.ChannelAlipay:
  256. var apply *model.DBRealnameAlipayApply
  257. if apply, err = s.dao.RealnameAlipayApply(ctx, arg.ID); err != nil {
  258. return
  259. }
  260. if apply.Status == model.RealnameApplyStateNone.DBStatus() || apply.Status == model.RealnameApplyStateRejective.DBStatus() {
  261. return
  262. }
  263. mid = apply.MID
  264. }
  265. var (
  266. state = 0
  267. msgTitle = ""
  268. msgContent = ""
  269. mc = "2_2_1"
  270. expNotify = false
  271. )
  272. switch arg.Action {
  273. case model.RealnameActionPass:
  274. state = model.RealnameApplyStatePassed.DBStatus()
  275. msgTitle = "您提交的实名认证已审核通过"
  276. msgContent = "恭喜,您提交的实名认证已通过审核"
  277. expNotify = true
  278. case model.RealnameActionReject:
  279. state = model.RealnameApplyStateRejective.DBStatus()
  280. msgTitle = "您提交的实名认证未通过审核"
  281. msgContent = fmt.Sprintf(`抱歉,您提交的实名认证未通过审核,驳回原因:%s。请修改后重新提交实名认证。`, arg.Reason)
  282. default:
  283. err = ecode.RequestErr
  284. return
  285. }
  286. // 2. do something
  287. switch arg.Channel {
  288. case model.ChannelMain:
  289. if err = s.dao.UpdateOldRealnameApply(ctx, arg.ID, state, adminName, adminID, time.Now(), arg.Reason); err != nil {
  290. return
  291. }
  292. case model.ChannelAlipay:
  293. if err = s.dao.UpdateRealnameAlipayApply(ctx, arg.ID, adminID, adminName, state, arg.Reason); err != nil {
  294. return
  295. }
  296. if err = s.dao.UpdateRealnameInfo(ctx, mid, state, arg.Reason); err != nil {
  297. return
  298. }
  299. }
  300. go func() {
  301. if err := s.dao.RawMessage(context.Background(), mc, msgTitle, msgContent, []int64{mid}); err != nil {
  302. log.Error("%+v", err)
  303. }
  304. if expNotify {
  305. expMsg := &model.AddExpMsg{
  306. Event: "identify",
  307. Mid: mid,
  308. IP: metadata.String(ctx, metadata.RemoteIP),
  309. Ts: time.Now().Unix(),
  310. }
  311. if err := s.dao.PubExpMsg(ctx, expMsg); err != nil {
  312. log.Error("%+v", err)
  313. }
  314. }
  315. }()
  316. return
  317. }
  318. // RealnameReasonList .
  319. func (s *Service) RealnameReasonList(ctx context.Context, arg *model.ArgRealnameReasonList) (list []string, total int, err error) {
  320. return s.dao.RealnameReasonList(ctx)
  321. }
  322. // RealnameSetReason .
  323. func (s *Service) RealnameSetReason(ctx context.Context, arg *model.ArgRealnameSetReason) (err error) {
  324. return s.dao.UpdateRealnameReason(ctx, arg.Reasons)
  325. }
  326. // RealnameSearchCard .
  327. func (s *Service) RealnameSearchCard(ctx context.Context, cards []string, cardType int, country int) (data map[string]int64, err error) {
  328. var (
  329. hashmap = make(map[string]string) //map[hash]card
  330. hashes = make([]string, 0)
  331. list []*model.DBRealnameInfo
  332. )
  333. for _, card := range cards {
  334. hash := cardMD5(card, cardType, country)
  335. hashmap[hash] = card
  336. hashes = append(hashes, hash)
  337. }
  338. if list, err = s.dao.RealnameSearchCards(ctx, hashes); err != nil {
  339. return
  340. }
  341. data = make(map[string]int64)
  342. for _, l := range list {
  343. if rawCode, ok := hashmap[l.CardMD5]; ok {
  344. data[rawCode] = l.MID
  345. }
  346. }
  347. return
  348. }
  349. // RealnameUnbind is.
  350. func (s *Service) RealnameUnbind(ctx context.Context, mid int64, adminName string, adminID int64) (err error) {
  351. var (
  352. info *model.DBRealnameInfo
  353. )
  354. if info, err = s.dao.RealnameInfo(ctx, mid); err != nil {
  355. return
  356. }
  357. if info == nil {
  358. err = ecode.RealnameAlipayApplyInvalid
  359. return
  360. }
  361. if info.Status != model.RealnameApplyStatePassed.DBStatus() {
  362. return
  363. }
  364. if err = s.dao.UpdateRealnameInfo(ctx, mid, model.RealnameApplyStateRejective.DBStatus(), "管理后台解绑"); err != nil {
  365. return
  366. }
  367. switch info.Channel {
  368. case model.ChannelMain.DBChannel():
  369. if err = s.dao.RejectRealnameMainApply(ctx, mid, adminName, adminID, "管理后台解绑"); err != nil {
  370. return
  371. }
  372. case model.ChannelAlipay.DBChannel():
  373. if err = s.dao.RejectRealnameAlipayApply(ctx, mid, adminName, adminID, "管理后台解绑"); err != nil {
  374. return
  375. }
  376. default:
  377. log.Warn("Failed to reject realname apply: unrecognized channel: %+v", info)
  378. }
  379. go func() {
  380. r := &report.ManagerInfo{
  381. Uname: adminName,
  382. UID: adminID,
  383. Business: model.RealnameManagerLogID,
  384. Type: 0,
  385. Oid: mid,
  386. Action: model.LogActionRealnameUnbind,
  387. Ctime: time.Now(),
  388. }
  389. if err = report.Manager(r); err != nil {
  390. log.Error("Send manager log failed : %+v , report : %+v", err, r)
  391. err = nil
  392. return
  393. }
  394. log.Info("Send manager log success report : %+v", r)
  395. }()
  396. return
  397. }
  398. // RealnameImage return img
  399. func (s *Service) RealnameImage(ctx context.Context, token string) ([]byte, error) {
  400. filePath := fmt.Sprintf("%s/%s.txt", conf.Conf.Realname.DataDir, token)
  401. _, err := os.Stat(filePath)
  402. if os.IsNotExist(err) {
  403. log.Info("file : %s , not found", filePath)
  404. return nil, ecode.RequestErr
  405. }
  406. file, err := os.Open(filePath)
  407. if err != nil {
  408. return nil, errors.WithStack(err)
  409. }
  410. defer file.Close()
  411. img, err := ioutil.ReadAll(file)
  412. if err != nil {
  413. return nil, errors.WithStack(err)
  414. }
  415. return s.mainCryptor.IMGDecrypt(img)
  416. }
  417. // FetchRealnameImage is
  418. func (s *Service) FetchRealnameImage(ctx context.Context, token string) ([]byte, error) {
  419. img, err := s.dao.GetRealnameImageCache(ctx, asIMGData(token))
  420. if err == nil && len(img) > 0 {
  421. return img, nil
  422. }
  423. if err != nil {
  424. log.Warn("Failed to get realname image from cache: %s: %+v", token, err)
  425. }
  426. img, err = s.RealnameImage(ctx, token)
  427. if err != nil {
  428. return nil, err
  429. }
  430. if len(img) <= _512KiloBytes {
  431. return img, nil
  432. }
  433. striped, err := StripImage(img)
  434. if err != nil {
  435. log.Warn("Failed to strip image: %+v", err)
  436. return img, nil
  437. }
  438. return striped, nil
  439. }
  440. // RealnameImagePreview return preview img
  441. func (s *Service) RealnameImagePreview(ctx context.Context, token string, borderSize uint) (data []byte, err error) {
  442. var (
  443. src []byte
  444. )
  445. if src, err = s.RealnameImage(ctx, token); err != nil {
  446. return
  447. }
  448. if len(src) == 0 {
  449. return
  450. }
  451. var (
  452. img image.Image
  453. imgWidth, imgHeight int
  454. imgFormat string
  455. sr = bytes.NewReader(src)
  456. )
  457. if img, imgFormat, err = image.Decode(sr); err != nil {
  458. log.Warn("Failed to decode image: %+v, return origin image data directly", err)
  459. return src, nil
  460. }
  461. imgWidth, imgHeight = img.Bounds().Dx(), img.Bounds().Dy()
  462. log.Info("Decode img : %s , format : %s , width : %d , height : %d ", token, imgFormat, imgWidth, imgHeight)
  463. if imgFormat != "png" && imgFormat != "jpg" && imgFormat != "jpeg" {
  464. return
  465. }
  466. if imgWidth > imgHeight {
  467. img = resize.Resize(borderSize, 0, img, resize.Lanczos3)
  468. } else {
  469. img = resize.Resize(0, borderSize, img, resize.Lanczos3)
  470. }
  471. var (
  472. bb bytes.Buffer
  473. bw = bufio.NewWriter(&bb)
  474. )
  475. switch imgFormat {
  476. case "jpg", "jpeg":
  477. if err = jpeg.Encode(bw, img, nil); err != nil {
  478. err = errors.WithStack(err)
  479. return
  480. }
  481. case "png":
  482. if err = png.Encode(bw, img); err != nil {
  483. err = errors.WithStack(err)
  484. return
  485. }
  486. }
  487. data = bb.Bytes()
  488. return
  489. }
  490. // RealnameExcel export user realname info
  491. func (s *Service) RealnameExcel(ctx context.Context, mids []int64) ([]*model.RealnameExport, error) {
  492. infos, err := s.dao.BatchRealnameInfo(ctx, mids)
  493. if err != nil {
  494. log.Warn("Failed to get realname info with mids: %+v: %+v", mids, err)
  495. // keep an empty infos
  496. infos = make(map[int64]*model.DBRealnameInfo)
  497. }
  498. pinfos, err := s.dao.PassportQueryByMidsChunked(ctx, mids, 100)
  499. if err != nil {
  500. log.Warn("Failed to get passport query by mids: %+v: %+v", mids, err)
  501. // keep an empty infos
  502. pinfos = make(map[int64]*model.PassportQueryByMidResult)
  503. }
  504. res := make([]*model.RealnameExport, 0, len(mids))
  505. for _, mid := range mids {
  506. export := &model.RealnameExport{
  507. Mid: mid,
  508. }
  509. // passport
  510. func() {
  511. p, ok := pinfos[mid]
  512. if !ok {
  513. log.Warn("Failed to get passport info with mid: %d", mid)
  514. return
  515. }
  516. export.UserID = p.Userid
  517. export.Uname = p.Name
  518. export.Tel = p.Tel
  519. }()
  520. // realname
  521. func() {
  522. info, ok := infos[mid]
  523. if !ok {
  524. log.Warn("Failed to get realname info with mid: %d", mid)
  525. return
  526. }
  527. export.Realname = info.Realname
  528. export.CardType = info.CardType
  529. cardDecode, err := model.CardDecrypt(info.Card)
  530. if err != nil {
  531. log.Error("Failed to decrypt card: %s: %+v", info.Card, err)
  532. return
  533. }
  534. export.CardNum = cardDecode
  535. }()
  536. res = append(res, export)
  537. }
  538. return res, nil
  539. }
  540. // RealnameSubmit is
  541. func (s *Service) RealnameSubmit(ctx context.Context, arg *model.ArgRealnameSubmit) error {
  542. encryptedCardNum, err := s.realnameCrypto.CardEncrypt([]byte(arg.CardNum))
  543. if err != nil {
  544. return err
  545. }
  546. _ = func() error {
  547. front := &model.DBRealnameApplyIMG{IMGData: asIMGData(arg.FrontImageToken)}
  548. if err := s.dao.AddRealnameIMG(ctx, front); err != nil {
  549. return err
  550. }
  551. back := &model.DBRealnameApplyIMG{IMGData: asIMGData(arg.BackImageToken)}
  552. if err := s.dao.AddRealnameIMG(ctx, back); err != nil {
  553. return err
  554. }
  555. apply := &model.DBRealnameApply{
  556. MID: arg.Mid,
  557. Realname: arg.Realname,
  558. Country: arg.Country,
  559. CardType: arg.CardType,
  560. CardNum: string(encryptedCardNum),
  561. CardMD5: cardMD5(arg.CardNum, int(arg.CardType), int(arg.Country)),
  562. FrontIMG: front.ID,
  563. BackIMG: back.ID,
  564. Status: model.RealnameApplyStatePassed.DBStatus(),
  565. Operator: arg.Operator,
  566. OperatorID: arg.OperatorID,
  567. OperatorTime: time.Now(),
  568. }
  569. if arg.HandImageToken != "" {
  570. hand := &model.DBRealnameApplyIMG{IMGData: asIMGData(arg.HandImageToken)}
  571. if err := s.dao.AddRealnameIMG(ctx, hand); err != nil {
  572. return err
  573. }
  574. apply.HandIMG = hand.ID
  575. }
  576. if err := s.dao.AddRealnameApply(ctx, apply); err != nil {
  577. return err
  578. }
  579. info := &model.DBRealnameInfo{
  580. MID: apply.MID,
  581. Channel: model.ChannelMain.DBChannel(),
  582. Realname: apply.Realname,
  583. Country: apply.Country,
  584. CardType: apply.CardType,
  585. Card: apply.CardNum,
  586. CardMD5: apply.CardMD5,
  587. Status: model.RealnameApplyStatePassed.DBStatus(),
  588. Reason: fmt.Sprintf("管理后台提交:%s", arg.Remark),
  589. }
  590. return s.dao.SubmitRealnameInfo(ctx, info)
  591. }
  592. toOld := func() error {
  593. front := &model.DeDeIdentificationCardApplyImg{IMGData: asIMGData(arg.FrontImageToken)}
  594. if err := s.dao.AddOldRealnameIMG(ctx, front); err != nil {
  595. return err
  596. }
  597. back := &model.DeDeIdentificationCardApplyImg{IMGData: asIMGData(arg.BackImageToken)}
  598. if err := s.dao.AddOldRealnameIMG(ctx, back); err != nil {
  599. return err
  600. }
  601. apply := &model.DeDeIdentificationCardApply{
  602. MID: arg.Mid,
  603. Realname: arg.Realname,
  604. Type: arg.CardType,
  605. CardData: string(encryptedCardNum),
  606. CardForSearch: cardMD5(arg.CardNum, int(arg.CardType), int(arg.Country)),
  607. FrontImg: front.ID,
  608. BackImg: back.ID,
  609. ApplyTime: int32(time.Now().Unix()),
  610. Operator: arg.Operator,
  611. OperatorTime: int32(time.Now().Unix()),
  612. Status: int8(model.RealnameApplyStatePassed.DBStatus()),
  613. Remark: fmt.Sprintf("管理后台提交:%s", arg.Remark),
  614. }
  615. if arg.HandImageToken != "" {
  616. hand := &model.DeDeIdentificationCardApplyImg{IMGData: asIMGData(arg.HandImageToken)}
  617. if err := s.dao.AddOldRealnameIMG(ctx, hand); err != nil {
  618. return err
  619. }
  620. apply.FrontImg2 = hand.ID
  621. }
  622. if err := s.dao.AddOldRealnameApply(ctx, apply); err != nil {
  623. return err
  624. }
  625. info := &model.DBRealnameInfo{
  626. MID: apply.MID,
  627. Channel: model.ChannelMain.DBChannel(),
  628. Realname: apply.Realname,
  629. Country: arg.Country,
  630. CardType: arg.CardType,
  631. Card: apply.CardData,
  632. CardMD5: apply.CardForSearch,
  633. Status: model.RealnameApplyStatePassed.DBStatus(),
  634. Reason: fmt.Sprintf("管理后台提交:%s", arg.Remark),
  635. }
  636. return s.dao.SubmitRealnameInfo(ctx, info)
  637. }
  638. if err := toOld(); err != nil {
  639. return err
  640. }
  641. report.Manager(&report.ManagerInfo{
  642. Uname: arg.Operator,
  643. UID: arg.OperatorID,
  644. Business: model.RealnameManagerLogID,
  645. Type: 0,
  646. Oid: arg.Mid,
  647. Action: model.LogActionRealnameSubmit,
  648. Ctime: time.Now(),
  649. })
  650. return nil
  651. }
  652. // RealnameFileUpload is
  653. func (s *Service) RealnameFileUpload(ctx context.Context, mid int64, data []byte) (src string, err error) {
  654. var (
  655. md5Engine = md5.New()
  656. hashMID string
  657. hashRand string
  658. fileName string
  659. dirPath string
  660. dateStr string
  661. )
  662. md5Engine.Write([]byte(strconv.FormatInt(mid, 10)))
  663. hashMID = hex.EncodeToString(md5Engine.Sum(nil))
  664. md5Engine.Reset()
  665. md5Engine.Write([]byte(strconv.FormatInt(time.Now().Unix(), 10)))
  666. md5Engine.Write([]byte(strconv.FormatInt(mrand.Int63n(1000000), 10)))
  667. hashRand = hex.EncodeToString(md5Engine.Sum(nil))
  668. fileName = fmt.Sprintf("%s_%s.txt", hashMID[:6], hashRand)
  669. dateStr = time.Now().Format("20060102")
  670. dirPath = fmt.Sprintf("%s/%s/", s.c.Realname.DataDir, dateStr)
  671. var (
  672. dataFile *os.File
  673. writeFileSize int
  674. encrptedData []byte
  675. )
  676. _, err = os.Stat(dirPath)
  677. if os.IsNotExist(err) {
  678. mask := syscall.Umask(0)
  679. defer syscall.Umask(mask)
  680. if err = os.MkdirAll(dirPath, 0777); err != nil {
  681. err = errors.WithStack(err)
  682. return
  683. }
  684. }
  685. if encrptedData, err = s.mainCryptor.IMGEncrypt(data); err != nil {
  686. err = errors.WithStack(err)
  687. return
  688. }
  689. if dataFile, err = os.Create(dirPath + fileName); err != nil {
  690. err = errors.Wrapf(err, "create file %s failed", dirPath+fileName)
  691. return
  692. }
  693. defer dataFile.Close()
  694. if writeFileSize, err = dataFile.Write(encrptedData); err != nil {
  695. err = errors.Wrapf(err, "write file %s size %d failed", dirPath+fileName, len(encrptedData))
  696. return
  697. }
  698. if writeFileSize != len(encrptedData) {
  699. err = errors.Errorf("Write file data to %s , expected %d actual %d", dirPath+fileName, len(encrptedData), writeFileSize)
  700. return
  701. }
  702. src = fmt.Sprintf("%s/%s", dateStr, strings.TrimSuffix(fileName, ".txt"))
  703. return
  704. }
  705. func asIMGData(imgToken string) string {
  706. return model.RealnameImgPrefix + imgToken + model.RealnameImgSuffix
  707. }
  708. func asIMGToken(IMGData string) string {
  709. token := strings.TrimPrefix(IMGData, "/idenfiles/")
  710. token = strings.TrimSuffix(token, ".txt")
  711. return token
  712. }
  713. // StripImage is
  714. func StripImage(raw []byte) ([]byte, error) {
  715. i, format, err := image.Decode(bytes.NewReader(raw))
  716. if err != nil {
  717. return nil, errors.WithStack(err)
  718. }
  719. out := &bytes.Buffer{}
  720. switch format {
  721. case "jpg", "jpeg":
  722. if err := jpeg.Encode(out, i, &jpeg.Options{Quality: jpeg.DefaultQuality}); err != nil {
  723. return nil, errors.WithStack(err)
  724. }
  725. default:
  726. return nil, errors.Errorf("Unsupported type: %s", format)
  727. }
  728. return out.Bytes(), nil
  729. }