You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
772 lines
21 KiB
772 lines
21 KiB
package service |
|
|
|
import ( |
|
"bufio" |
|
"bytes" |
|
"context" |
|
"crypto/md5" |
|
"encoding/hex" |
|
"fmt" |
|
"image" |
|
"image/jpeg" |
|
"image/png" |
|
"io/ioutil" |
|
mrand "math/rand" |
|
"os" |
|
"strconv" |
|
"strings" |
|
"syscall" |
|
"time" |
|
|
|
"go-common/app/admin/main/member/conf" |
|
"go-common/app/admin/main/member/model" |
|
memmdl "go-common/app/service/main/member/model" |
|
"go-common/library/ecode" |
|
"go-common/library/log" |
|
"go-common/library/net/metadata" |
|
"go-common/library/queue/databus/report" |
|
|
|
"github.com/nfnt/resize" |
|
"github.com/pkg/errors" |
|
) |
|
|
|
// consts |
|
const ( |
|
_512KiloBytes = 512 * 1024 |
|
) |
|
|
|
// RealnameList . |
|
func (s *Service) RealnameList(ctx context.Context, arg *model.ArgRealnameList) (list []*model.RespRealnameApply, total int, err error) { |
|
list = make([]*model.RespRealnameApply, 0) |
|
switch arg.Channel { |
|
case model.ChannelMain: |
|
return s.realnameMainList(ctx, arg) |
|
case model.ChannelAlipay: |
|
return s.realnameAlipayList(ctx, arg) |
|
} |
|
return |
|
} |
|
|
|
func (s *Service) realnameMainList(ctx context.Context, arg *model.ArgRealnameList) (list []*model.RespRealnameApply, total int, err error) { |
|
var ( |
|
dl []*model.DBRealnameApply |
|
) |
|
if arg.Card != "" { |
|
// 如果查询证件号, 则通过证件号MD5 realname_info 查询到 对应的 mids |
|
var ( |
|
infos []*model.DBRealnameInfo |
|
mids []int64 |
|
md5 = cardMD5(arg.Card, arg.DBCardType(), arg.Country) |
|
) |
|
log.Info("realnameMainList card: %s, md5: %s", arg.Card, md5) |
|
if infos, err = s.dao.RealnameInfoByCardMD5(ctx, md5, arg.State.DBStatus(), model.ChannelMain.DBChannel()); err != nil { |
|
return |
|
} |
|
log.Info("realnameMainList infos : %+v", infos) |
|
if len(infos) <= 0 { |
|
return |
|
} |
|
for _, i := range infos { |
|
log.Info("realnameMainList info: %+v", i) |
|
mids = append(mids, i.MID) |
|
} |
|
log.Info("realnameMainList mids: %+v", mids) |
|
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 { |
|
return |
|
} |
|
} else { |
|
var ( |
|
mids []int64 |
|
) |
|
if arg.MID > 0 { |
|
mids = append(mids, arg.MID) |
|
} |
|
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 { |
|
return |
|
} |
|
} |
|
var ( |
|
midMap = make(map[int64]int) // map[mid]count |
|
mids []int64 |
|
imgIDs []int64 |
|
) |
|
// 审核 db 数据解析进 list |
|
for _, d := range dl { |
|
midMap[d.MID] = 0 |
|
var ( |
|
r = &model.RespRealnameApply{} |
|
) |
|
r.ParseDBMainApply(d) |
|
imgIDs = append(imgIDs, d.HandIMG, d.FrontIMG, d.BackIMG) |
|
list = append(list, r) |
|
} |
|
// 没有数据则返回 |
|
if len(midMap) <= 0 { |
|
return |
|
} |
|
// 获取实名申请次数 |
|
for mid := range midMap { |
|
if midMap[mid], err = s.dao.RealnameApplyCount(ctx, mid); err != nil { |
|
return |
|
} |
|
mids = append(mids, mid) |
|
} |
|
// 获取mid的昵称 & 等级信息 |
|
var ( |
|
memsArg = &memmdl.ArgMemberMids{ |
|
Mids: mids, |
|
} |
|
memMap map[int64]*memmdl.Member |
|
imgMap map[int64]*model.DBRealnameApplyIMG |
|
) |
|
if memMap, err = s.memberRPC.Members(ctx, memsArg); err != nil { |
|
err = errors.WithStack(err) |
|
return |
|
} |
|
// 获取证件照信息 |
|
if imgMap, err = s.dao.RealnameApplyIMG(ctx, imgIDs); err != nil { |
|
return |
|
} |
|
for _, ra := range list { |
|
if mem, ok := memMap[ra.MID]; ok { |
|
ra.ParseMember(mem) |
|
} |
|
for _, id := range ra.IMGIDs { |
|
if img, ok := imgMap[id]; ok { |
|
ra.ParseDBApplyIMG(img.IMGData) |
|
} |
|
} |
|
ra.Times = midMap[ra.MID] |
|
} |
|
return |
|
} |
|
|
|
func (s *Service) realnameAlipayList(ctx context.Context, arg *model.ArgRealnameList) (list []*model.RespRealnameApply, total int, err error) { |
|
var ( |
|
dl []*model.DBRealnameAlipayApply |
|
) |
|
if arg.Card != "" { |
|
// 如果查询证件号, 则通过证件号MD5 realname_info 查询到 对应的 mids |
|
var ( |
|
infos []*model.DBRealnameInfo |
|
mids []int64 |
|
md5 = cardMD5(arg.Card, arg.DBCardType(), arg.Country) |
|
) |
|
log.Info("realnameAlipayList card: %s, md5: %s", arg.Card, md5) |
|
if infos, err = s.dao.RealnameInfoByCardMD5(ctx, md5, arg.State.DBStatus(), model.ChannelAlipay.DBChannel()); err != nil { |
|
return |
|
} |
|
log.Info("realnameAlipayList infos : %+v", infos) |
|
if len(infos) <= 0 { |
|
return |
|
} |
|
for _, i := range infos { |
|
log.Info("realnameAlipayList info: %+v", i) |
|
mids = append(mids, i.MID) |
|
} |
|
log.Info("realnameAlipayList mids: %+v", mids) |
|
if dl, total, err = s.dao.RealnameAlipayList(ctx, mids, 0, 0, arg.State.DBStatus(), arg.PN, arg.PS, arg.IsDesc); err != nil { |
|
return |
|
} |
|
} else { |
|
var ( |
|
mids []int64 |
|
) |
|
if arg.MID > 0 { |
|
mids = append(mids, arg.MID) |
|
} |
|
if dl, total, err = s.dao.RealnameAlipayList(ctx, mids, arg.TSFrom, arg.TSTo, arg.State.DBStatus(), arg.PN, arg.PS, arg.IsDesc); err != nil { |
|
return |
|
} |
|
} |
|
log.Info("realnameAlipayList dl: %+v, total: %d", dl, total) |
|
var ( |
|
midMap = make(map[int64]int) |
|
) |
|
// append to list |
|
for _, d := range dl { |
|
midMap[d.MID] = 0 |
|
var ( |
|
r = &model.RespRealnameApply{} |
|
) |
|
r.ParseDBAlipayApply(d) |
|
list = append(list, r) |
|
} |
|
if len(midMap) <= 0 { |
|
return |
|
} |
|
var mids []int64 |
|
for mid := range midMap { |
|
if midMap[mid], err = s.dao.RealnameApplyCount(ctx, mid); err != nil { |
|
return |
|
} |
|
mids = append(mids, mid) |
|
} |
|
var ( |
|
memsArg = &memmdl.ArgMemberMids{ |
|
Mids: mids, |
|
} |
|
memMap map[int64]*memmdl.Member |
|
) |
|
if memMap, err = s.memberRPC.Members(ctx, memsArg); err != nil { |
|
err = errors.WithStack(err) |
|
return |
|
} |
|
for _, ra := range list { |
|
if mem, ok := memMap[ra.MID]; ok { |
|
ra.ParseMember(mem) |
|
} |
|
ra.Times = midMap[ra.MID] |
|
} |
|
return |
|
} |
|
|
|
func cardMD5(card string, cardType int, country int) (res string) { |
|
if card == "" || cardType < 0 || country < 0 { |
|
return |
|
} |
|
var ( |
|
lowerCode = strings.ToLower(card) |
|
key = fmt.Sprintf("%s_%s_%d_%d", model.RealnameSalt, lowerCode, cardType, country) |
|
) |
|
return fmt.Sprintf("%x", md5.Sum([]byte(key))) |
|
} |
|
|
|
// RealnamePendingList . |
|
func (s *Service) RealnamePendingList(ctx context.Context, arg *model.ArgRealnamePendingList) (list []*model.RespRealnameApply, total int, err error) { |
|
var ( |
|
larg = &model.ArgRealnameList{ |
|
Channel: arg.Channel, |
|
State: model.RealnameApplyStatePending, |
|
TSFrom: time.Now().Add(-time.Hour * 24 * 7).Unix(), |
|
PS: arg.PS, |
|
PN: arg.PN, |
|
} |
|
) |
|
return s.RealnameList(ctx, larg) |
|
} |
|
|
|
// RealnameAuditApply . |
|
func (s *Service) RealnameAuditApply(ctx context.Context, arg *model.ArgRealnameAuditApply, adminName string, adminID int64) (err error) { |
|
var ( |
|
mid int64 |
|
) |
|
// 1. check the apply state |
|
switch arg.Channel { |
|
case model.ChannelMain: |
|
var apply *model.DBRealnameApply |
|
if apply, err = s.dao.RealnameMainApply(ctx, arg.ID); err != nil { |
|
return |
|
} |
|
if apply.IsPassed() { |
|
return |
|
} |
|
mid = apply.MID |
|
case model.ChannelAlipay: |
|
var apply *model.DBRealnameAlipayApply |
|
if apply, err = s.dao.RealnameAlipayApply(ctx, arg.ID); err != nil { |
|
return |
|
} |
|
if apply.Status == model.RealnameApplyStateNone.DBStatus() || apply.Status == model.RealnameApplyStateRejective.DBStatus() { |
|
return |
|
} |
|
mid = apply.MID |
|
} |
|
|
|
var ( |
|
state = 0 |
|
msgTitle = "" |
|
msgContent = "" |
|
mc = "2_2_1" |
|
expNotify = false |
|
) |
|
switch arg.Action { |
|
case model.RealnameActionPass: |
|
state = model.RealnameApplyStatePassed.DBStatus() |
|
msgTitle = "您提交的实名认证已审核通过" |
|
msgContent = "恭喜,您提交的实名认证已通过审核" |
|
expNotify = true |
|
case model.RealnameActionReject: |
|
state = model.RealnameApplyStateRejective.DBStatus() |
|
msgTitle = "您提交的实名认证未通过审核" |
|
msgContent = fmt.Sprintf(`抱歉,您提交的实名认证未通过审核,驳回原因:%s。请修改后重新提交实名认证。`, arg.Reason) |
|
default: |
|
err = ecode.RequestErr |
|
return |
|
} |
|
|
|
// 2. do something |
|
switch arg.Channel { |
|
case model.ChannelMain: |
|
if err = s.dao.UpdateOldRealnameApply(ctx, arg.ID, state, adminName, adminID, time.Now(), arg.Reason); err != nil { |
|
return |
|
} |
|
case model.ChannelAlipay: |
|
if err = s.dao.UpdateRealnameAlipayApply(ctx, arg.ID, adminID, adminName, state, arg.Reason); err != nil { |
|
return |
|
} |
|
if err = s.dao.UpdateRealnameInfo(ctx, mid, state, arg.Reason); err != nil { |
|
return |
|
} |
|
} |
|
go func() { |
|
if err := s.dao.RawMessage(context.Background(), mc, msgTitle, msgContent, []int64{mid}); err != nil { |
|
log.Error("%+v", err) |
|
} |
|
if expNotify { |
|
expMsg := &model.AddExpMsg{ |
|
Event: "identify", |
|
Mid: mid, |
|
IP: metadata.String(ctx, metadata.RemoteIP), |
|
Ts: time.Now().Unix(), |
|
} |
|
if err := s.dao.PubExpMsg(ctx, expMsg); err != nil { |
|
log.Error("%+v", err) |
|
} |
|
} |
|
}() |
|
return |
|
} |
|
|
|
// RealnameReasonList . |
|
func (s *Service) RealnameReasonList(ctx context.Context, arg *model.ArgRealnameReasonList) (list []string, total int, err error) { |
|
return s.dao.RealnameReasonList(ctx) |
|
} |
|
|
|
// RealnameSetReason . |
|
func (s *Service) RealnameSetReason(ctx context.Context, arg *model.ArgRealnameSetReason) (err error) { |
|
return s.dao.UpdateRealnameReason(ctx, arg.Reasons) |
|
} |
|
|
|
// RealnameSearchCard . |
|
func (s *Service) RealnameSearchCard(ctx context.Context, cards []string, cardType int, country int) (data map[string]int64, err error) { |
|
var ( |
|
hashmap = make(map[string]string) //map[hash]card |
|
hashes = make([]string, 0) |
|
list []*model.DBRealnameInfo |
|
) |
|
for _, card := range cards { |
|
hash := cardMD5(card, cardType, country) |
|
hashmap[hash] = card |
|
hashes = append(hashes, hash) |
|
} |
|
if list, err = s.dao.RealnameSearchCards(ctx, hashes); err != nil { |
|
return |
|
} |
|
data = make(map[string]int64) |
|
for _, l := range list { |
|
if rawCode, ok := hashmap[l.CardMD5]; ok { |
|
data[rawCode] = l.MID |
|
} |
|
} |
|
return |
|
} |
|
|
|
// RealnameUnbind is. |
|
func (s *Service) RealnameUnbind(ctx context.Context, mid int64, adminName string, adminID int64) (err error) { |
|
var ( |
|
info *model.DBRealnameInfo |
|
) |
|
if info, err = s.dao.RealnameInfo(ctx, mid); err != nil { |
|
return |
|
} |
|
if info == nil { |
|
err = ecode.RealnameAlipayApplyInvalid |
|
return |
|
} |
|
if info.Status != model.RealnameApplyStatePassed.DBStatus() { |
|
return |
|
} |
|
if err = s.dao.UpdateRealnameInfo(ctx, mid, model.RealnameApplyStateRejective.DBStatus(), "管理后台解绑"); err != nil { |
|
return |
|
} |
|
switch info.Channel { |
|
case model.ChannelMain.DBChannel(): |
|
if err = s.dao.RejectRealnameMainApply(ctx, mid, adminName, adminID, "管理后台解绑"); err != nil { |
|
return |
|
} |
|
case model.ChannelAlipay.DBChannel(): |
|
if err = s.dao.RejectRealnameAlipayApply(ctx, mid, adminName, adminID, "管理后台解绑"); err != nil { |
|
return |
|
} |
|
default: |
|
log.Warn("Failed to reject realname apply: unrecognized channel: %+v", info) |
|
} |
|
|
|
go func() { |
|
r := &report.ManagerInfo{ |
|
Uname: adminName, |
|
UID: adminID, |
|
Business: model.RealnameManagerLogID, |
|
Type: 0, |
|
Oid: mid, |
|
Action: model.LogActionRealnameUnbind, |
|
Ctime: time.Now(), |
|
} |
|
if err = report.Manager(r); err != nil { |
|
log.Error("Send manager log failed : %+v , report : %+v", err, r) |
|
err = nil |
|
return |
|
} |
|
log.Info("Send manager log success report : %+v", r) |
|
}() |
|
return |
|
} |
|
|
|
// RealnameImage return img |
|
func (s *Service) RealnameImage(ctx context.Context, token string) ([]byte, error) { |
|
filePath := fmt.Sprintf("%s/%s.txt", conf.Conf.Realname.DataDir, token) |
|
_, err := os.Stat(filePath) |
|
if os.IsNotExist(err) { |
|
log.Info("file : %s , not found", filePath) |
|
return nil, ecode.RequestErr |
|
} |
|
file, err := os.Open(filePath) |
|
if err != nil { |
|
return nil, errors.WithStack(err) |
|
} |
|
defer file.Close() |
|
img, err := ioutil.ReadAll(file) |
|
if err != nil { |
|
return nil, errors.WithStack(err) |
|
} |
|
return s.mainCryptor.IMGDecrypt(img) |
|
} |
|
|
|
// FetchRealnameImage is |
|
func (s *Service) FetchRealnameImage(ctx context.Context, token string) ([]byte, error) { |
|
img, err := s.dao.GetRealnameImageCache(ctx, asIMGData(token)) |
|
if err == nil && len(img) > 0 { |
|
return img, nil |
|
} |
|
if err != nil { |
|
log.Warn("Failed to get realname image from cache: %s: %+v", token, err) |
|
} |
|
|
|
img, err = s.RealnameImage(ctx, token) |
|
if err != nil { |
|
return nil, err |
|
} |
|
|
|
if len(img) <= _512KiloBytes { |
|
return img, nil |
|
} |
|
|
|
striped, err := StripImage(img) |
|
if err != nil { |
|
log.Warn("Failed to strip image: %+v", err) |
|
return img, nil |
|
} |
|
return striped, nil |
|
} |
|
|
|
// RealnameImagePreview return preview img |
|
func (s *Service) RealnameImagePreview(ctx context.Context, token string, borderSize uint) (data []byte, err error) { |
|
var ( |
|
src []byte |
|
) |
|
if src, err = s.RealnameImage(ctx, token); err != nil { |
|
return |
|
} |
|
if len(src) == 0 { |
|
return |
|
} |
|
var ( |
|
img image.Image |
|
imgWidth, imgHeight int |
|
imgFormat string |
|
sr = bytes.NewReader(src) |
|
) |
|
if img, imgFormat, err = image.Decode(sr); err != nil { |
|
log.Warn("Failed to decode image: %+v, return origin image data directly", err) |
|
return src, nil |
|
} |
|
imgWidth, imgHeight = img.Bounds().Dx(), img.Bounds().Dy() |
|
log.Info("Decode img : %s , format : %s , width : %d , height : %d ", token, imgFormat, imgWidth, imgHeight) |
|
if imgFormat != "png" && imgFormat != "jpg" && imgFormat != "jpeg" { |
|
return |
|
} |
|
if imgWidth > imgHeight { |
|
img = resize.Resize(borderSize, 0, img, resize.Lanczos3) |
|
} else { |
|
img = resize.Resize(0, borderSize, img, resize.Lanczos3) |
|
} |
|
var ( |
|
bb bytes.Buffer |
|
bw = bufio.NewWriter(&bb) |
|
) |
|
switch imgFormat { |
|
case "jpg", "jpeg": |
|
if err = jpeg.Encode(bw, img, nil); err != nil { |
|
err = errors.WithStack(err) |
|
return |
|
} |
|
case "png": |
|
if err = png.Encode(bw, img); err != nil { |
|
err = errors.WithStack(err) |
|
return |
|
} |
|
} |
|
data = bb.Bytes() |
|
return |
|
} |
|
|
|
// RealnameExcel export user realname info |
|
func (s *Service) RealnameExcel(ctx context.Context, mids []int64) ([]*model.RealnameExport, error) { |
|
infos, err := s.dao.BatchRealnameInfo(ctx, mids) |
|
if err != nil { |
|
log.Warn("Failed to get realname info with mids: %+v: %+v", mids, err) |
|
// keep an empty infos |
|
infos = make(map[int64]*model.DBRealnameInfo) |
|
} |
|
|
|
pinfos, err := s.dao.PassportQueryByMidsChunked(ctx, mids, 100) |
|
if err != nil { |
|
log.Warn("Failed to get passport query by mids: %+v: %+v", mids, err) |
|
// keep an empty infos |
|
pinfos = make(map[int64]*model.PassportQueryByMidResult) |
|
} |
|
|
|
res := make([]*model.RealnameExport, 0, len(mids)) |
|
for _, mid := range mids { |
|
export := &model.RealnameExport{ |
|
Mid: mid, |
|
} |
|
|
|
// passport |
|
func() { |
|
p, ok := pinfos[mid] |
|
if !ok { |
|
log.Warn("Failed to get passport info with mid: %d", mid) |
|
return |
|
} |
|
export.UserID = p.Userid |
|
export.Uname = p.Name |
|
export.Tel = p.Tel |
|
}() |
|
|
|
// realname |
|
func() { |
|
info, ok := infos[mid] |
|
if !ok { |
|
log.Warn("Failed to get realname info with mid: %d", mid) |
|
return |
|
} |
|
export.Realname = info.Realname |
|
export.CardType = info.CardType |
|
|
|
cardDecode, err := model.CardDecrypt(info.Card) |
|
if err != nil { |
|
log.Error("Failed to decrypt card: %s: %+v", info.Card, err) |
|
return |
|
} |
|
export.CardNum = cardDecode |
|
}() |
|
|
|
res = append(res, export) |
|
} |
|
return res, nil |
|
} |
|
|
|
// RealnameSubmit is |
|
func (s *Service) RealnameSubmit(ctx context.Context, arg *model.ArgRealnameSubmit) error { |
|
encryptedCardNum, err := s.realnameCrypto.CardEncrypt([]byte(arg.CardNum)) |
|
if err != nil { |
|
return err |
|
} |
|
|
|
_ = func() error { |
|
front := &model.DBRealnameApplyIMG{IMGData: asIMGData(arg.FrontImageToken)} |
|
if err := s.dao.AddRealnameIMG(ctx, front); err != nil { |
|
return err |
|
} |
|
back := &model.DBRealnameApplyIMG{IMGData: asIMGData(arg.BackImageToken)} |
|
if err := s.dao.AddRealnameIMG(ctx, back); err != nil { |
|
return err |
|
} |
|
apply := &model.DBRealnameApply{ |
|
MID: arg.Mid, |
|
Realname: arg.Realname, |
|
Country: arg.Country, |
|
CardType: arg.CardType, |
|
CardNum: string(encryptedCardNum), |
|
CardMD5: cardMD5(arg.CardNum, int(arg.CardType), int(arg.Country)), |
|
FrontIMG: front.ID, |
|
BackIMG: back.ID, |
|
Status: model.RealnameApplyStatePassed.DBStatus(), |
|
Operator: arg.Operator, |
|
OperatorID: arg.OperatorID, |
|
OperatorTime: time.Now(), |
|
} |
|
if arg.HandImageToken != "" { |
|
hand := &model.DBRealnameApplyIMG{IMGData: asIMGData(arg.HandImageToken)} |
|
if err := s.dao.AddRealnameIMG(ctx, hand); err != nil { |
|
return err |
|
} |
|
apply.HandIMG = hand.ID |
|
} |
|
if err := s.dao.AddRealnameApply(ctx, apply); err != nil { |
|
return err |
|
} |
|
info := &model.DBRealnameInfo{ |
|
MID: apply.MID, |
|
Channel: model.ChannelMain.DBChannel(), |
|
Realname: apply.Realname, |
|
Country: apply.Country, |
|
CardType: apply.CardType, |
|
Card: apply.CardNum, |
|
CardMD5: apply.CardMD5, |
|
Status: model.RealnameApplyStatePassed.DBStatus(), |
|
Reason: fmt.Sprintf("管理后台提交:%s", arg.Remark), |
|
} |
|
return s.dao.SubmitRealnameInfo(ctx, info) |
|
} |
|
|
|
toOld := func() error { |
|
front := &model.DeDeIdentificationCardApplyImg{IMGData: asIMGData(arg.FrontImageToken)} |
|
if err := s.dao.AddOldRealnameIMG(ctx, front); err != nil { |
|
return err |
|
} |
|
back := &model.DeDeIdentificationCardApplyImg{IMGData: asIMGData(arg.BackImageToken)} |
|
if err := s.dao.AddOldRealnameIMG(ctx, back); err != nil { |
|
return err |
|
} |
|
apply := &model.DeDeIdentificationCardApply{ |
|
MID: arg.Mid, |
|
Realname: arg.Realname, |
|
Type: arg.CardType, |
|
CardData: string(encryptedCardNum), |
|
CardForSearch: cardMD5(arg.CardNum, int(arg.CardType), int(arg.Country)), |
|
FrontImg: front.ID, |
|
BackImg: back.ID, |
|
ApplyTime: int32(time.Now().Unix()), |
|
Operator: arg.Operator, |
|
OperatorTime: int32(time.Now().Unix()), |
|
Status: int8(model.RealnameApplyStatePassed.DBStatus()), |
|
Remark: fmt.Sprintf("管理后台提交:%s", arg.Remark), |
|
} |
|
if arg.HandImageToken != "" { |
|
hand := &model.DeDeIdentificationCardApplyImg{IMGData: asIMGData(arg.HandImageToken)} |
|
if err := s.dao.AddOldRealnameIMG(ctx, hand); err != nil { |
|
return err |
|
} |
|
apply.FrontImg2 = hand.ID |
|
} |
|
if err := s.dao.AddOldRealnameApply(ctx, apply); err != nil { |
|
return err |
|
} |
|
info := &model.DBRealnameInfo{ |
|
MID: apply.MID, |
|
Channel: model.ChannelMain.DBChannel(), |
|
Realname: apply.Realname, |
|
Country: arg.Country, |
|
CardType: arg.CardType, |
|
Card: apply.CardData, |
|
CardMD5: apply.CardForSearch, |
|
Status: model.RealnameApplyStatePassed.DBStatus(), |
|
Reason: fmt.Sprintf("管理后台提交:%s", arg.Remark), |
|
} |
|
return s.dao.SubmitRealnameInfo(ctx, info) |
|
} |
|
|
|
if err := toOld(); err != nil { |
|
return err |
|
} |
|
|
|
report.Manager(&report.ManagerInfo{ |
|
Uname: arg.Operator, |
|
UID: arg.OperatorID, |
|
Business: model.RealnameManagerLogID, |
|
Type: 0, |
|
Oid: arg.Mid, |
|
Action: model.LogActionRealnameSubmit, |
|
Ctime: time.Now(), |
|
}) |
|
|
|
return nil |
|
} |
|
|
|
// RealnameFileUpload is |
|
func (s *Service) RealnameFileUpload(ctx context.Context, mid int64, data []byte) (src string, err error) { |
|
var ( |
|
md5Engine = md5.New() |
|
hashMID string |
|
hashRand string |
|
fileName string |
|
dirPath string |
|
dateStr string |
|
) |
|
md5Engine.Write([]byte(strconv.FormatInt(mid, 10))) |
|
hashMID = hex.EncodeToString(md5Engine.Sum(nil)) |
|
md5Engine.Reset() |
|
md5Engine.Write([]byte(strconv.FormatInt(time.Now().Unix(), 10))) |
|
md5Engine.Write([]byte(strconv.FormatInt(mrand.Int63n(1000000), 10))) |
|
hashRand = hex.EncodeToString(md5Engine.Sum(nil)) |
|
fileName = fmt.Sprintf("%s_%s.txt", hashMID[:6], hashRand) |
|
dateStr = time.Now().Format("20060102") |
|
dirPath = fmt.Sprintf("%s/%s/", s.c.Realname.DataDir, dateStr) |
|
|
|
var ( |
|
dataFile *os.File |
|
writeFileSize int |
|
encrptedData []byte |
|
) |
|
|
|
_, err = os.Stat(dirPath) |
|
if os.IsNotExist(err) { |
|
mask := syscall.Umask(0) |
|
defer syscall.Umask(mask) |
|
if err = os.MkdirAll(dirPath, 0777); err != nil { |
|
err = errors.WithStack(err) |
|
return |
|
} |
|
} |
|
if encrptedData, err = s.mainCryptor.IMGEncrypt(data); err != nil { |
|
err = errors.WithStack(err) |
|
return |
|
} |
|
if dataFile, err = os.Create(dirPath + fileName); err != nil { |
|
err = errors.Wrapf(err, "create file %s failed", dirPath+fileName) |
|
return |
|
} |
|
defer dataFile.Close() |
|
if writeFileSize, err = dataFile.Write(encrptedData); err != nil { |
|
err = errors.Wrapf(err, "write file %s size %d failed", dirPath+fileName, len(encrptedData)) |
|
return |
|
} |
|
if writeFileSize != len(encrptedData) { |
|
err = errors.Errorf("Write file data to %s , expected %d actual %d", dirPath+fileName, len(encrptedData), writeFileSize) |
|
return |
|
} |
|
src = fmt.Sprintf("%s/%s", dateStr, strings.TrimSuffix(fileName, ".txt")) |
|
return |
|
} |
|
|
|
func asIMGData(imgToken string) string { |
|
return model.RealnameImgPrefix + imgToken + model.RealnameImgSuffix |
|
} |
|
|
|
func asIMGToken(IMGData string) string { |
|
token := strings.TrimPrefix(IMGData, "/idenfiles/") |
|
token = strings.TrimSuffix(token, ".txt") |
|
return token |
|
} |
|
|
|
// StripImage is |
|
func StripImage(raw []byte) ([]byte, error) { |
|
i, format, err := image.Decode(bytes.NewReader(raw)) |
|
if err != nil { |
|
return nil, errors.WithStack(err) |
|
} |
|
|
|
out := &bytes.Buffer{} |
|
switch format { |
|
case "jpg", "jpeg": |
|
if err := jpeg.Encode(out, i, &jpeg.Options{Quality: jpeg.DefaultQuality}); err != nil { |
|
return nil, errors.WithStack(err) |
|
} |
|
default: |
|
return nil, errors.Errorf("Unsupported type: %s", format) |
|
} |
|
return out.Bytes(), nil |
|
}
|
|
|