From 400cda2cc34fa4ec15f79ebc12920795fff6bd60 Mon Sep 17 00:00:00 2001 From: Leufolium Date: Sat, 15 Jun 2024 13:41:03 +0800 Subject: [PATCH] by Robin at 20240615 --- api/consts/status.go | 6 ++ api/errcode/errcode.go | 8 ++ api/proto/hvyogo/proto/hvyogo_ext.go | 6 ++ api/proto/hvyogo/proto/hvyogo_vo.go | 8 ++ api/proto/worker_id/proto/worker_id_op.go | 65 ++++++++++++ app/mix/controller/hvyogo_callback.go | 27 +++++ app/mix/controller/init.go | 4 + app/mix/dao/mongo.go | 53 ++++++++++ app/mix/service/logic/worker_id.go | 65 ++++++++++++ app/mix/service/service.go | 100 ++++++++++++++++++ codecreate/codecreate.go | 8 +- codecreate/resource/EntityDefine.xlsx | Bin 52290 -> 53381 bytes dbstruct/worker_id.go | 11 ++ etc/mix/mix-test.yaml | 5 + library/configcenter/configcenter.go | 10 +- library/configcenter/cryptoconfigcenter.go | 8 ++ library/idgenerator/genid.go | 7 ++ library/mycrypto/aesCrypto.go | 8 ++ library/mycrypto/cryptoService.go | 13 +++ library/mycrypto/rsaCrypto.go | 29 +++++ .../taginterceptor/decryptTagInterceptor.go | 2 + .../taginterceptor/encryptTagInterceptor.go | 2 + 22 files changed, 437 insertions(+), 8 deletions(-) create mode 100644 api/proto/hvyogo/proto/hvyogo_ext.go create mode 100644 api/proto/hvyogo/proto/hvyogo_vo.go create mode 100644 api/proto/worker_id/proto/worker_id_op.go create mode 100644 app/mix/controller/hvyogo_callback.go create mode 100644 app/mix/service/logic/worker_id.go create mode 100644 dbstruct/worker_id.go diff --git a/api/consts/status.go b/api/consts/status.go index 2300f9ca..06f03c66 100644 --- a/api/consts/status.go +++ b/api/consts/status.go @@ -247,3 +247,9 @@ const ( IsCreatingPaidText_No = 0 //否 IsCreatingPaidText_Yes = 1 //是 ) + +const ( + HygAgreeState_Fail = "-1" // 签约失败 + HygAgreeState_Waiting = "1" // 待生成电子签 + HygAgreeState_Success = "2" // 签约成功 +) diff --git a/api/errcode/errcode.go b/api/errcode/errcode.go index 2165e353..7ecd0142 100644 --- a/api/errcode/errcode.go +++ b/api/errcode/errcode.go @@ -211,6 +211,9 @@ var ErrCodeMsgMap = map[ErrCode]string{ ErrCodeDailyStatementZoneInfoSrvFail: "空间相关每日报表服务错误", ErrCodeDailyStatementZoneInfoNotExist: "空间相关每日报表不存在", + + ErrCodeWorkerIdSrvFail: "用户职业者id映射表服务错误", + ErrCodeWorkerIdNotExist: "用户职业者id映射表不存在", } const ( @@ -505,6 +508,11 @@ const ( ErrCodeDailyStatementZoneInfoSrvFail ErrCode = -40001 // 空间相关每日报表服务错误 ErrCodeDailyStatementZoneInfoNotExist ErrCode = -40002 // 空间相关每日报表不存在 + // WorkerId: 41xxx + ErrCodeWorkerIdSrvOk ErrCode = ErrCodeOk + ErrCodeWorkerIdSrvFail ErrCode = -41001 // 用户职业者id映射表服务错误 + ErrCodeWorkerIdNotExist ErrCode = -41002 // 用户职业者id映射表不存在 + // Media: 60xxx ErrCodeMediaSrvOk ErrCode = ErrCodeOk ErrCodeMediaSrvFail ErrCode = -60001 // 媒体服务错误 diff --git a/api/proto/hvyogo/proto/hvyogo_ext.go b/api/proto/hvyogo/proto/hvyogo_ext.go new file mode 100644 index 00000000..aa0ede1d --- /dev/null +++ b/api/proto/hvyogo/proto/hvyogo_ext.go @@ -0,0 +1,6 @@ +package proto + +type HvyogoCallbackReq struct { + CooperatorId string `json:"cooperatorId"` // 商户对接唯一标识,手机号 + BusinessBody string `json:"businessBody" jcrypto:"hyg_aes"` // AES加密后的字符串 +} diff --git a/api/proto/hvyogo/proto/hvyogo_vo.go b/api/proto/hvyogo/proto/hvyogo_vo.go new file mode 100644 index 00000000..8309a420 --- /dev/null +++ b/api/proto/hvyogo/proto/hvyogo_vo.go @@ -0,0 +1,8 @@ +package proto + +type HvyogoVO struct { + WorkerId string `json:"workerId"` // 职业者id + AgreeState string `json:"agreeState"` // 签约状态 + AgreeDesc string `json:"agreeDesc"` // 签约状态描述 + WorkerMobile string `json:"workerMobile"` // 自由职业者手机号 +} diff --git a/api/proto/worker_id/proto/worker_id_op.go b/api/proto/worker_id/proto/worker_id_op.go new file mode 100644 index 00000000..a95e8ada --- /dev/null +++ b/api/proto/worker_id/proto/worker_id_op.go @@ -0,0 +1,65 @@ +package proto + +import ( + "service/api/base" + "service/dbstruct" +) + +// op 创建 +type OpCreateReq struct { + base.BaseRequest + *dbstruct.WorkerId +} + +type OpCreateData struct { +} + +type OpCreateResp struct { + base.BaseResponse + Data *OpCreateData `json:"data"` +} + +// op 删除 +type OpDeleteReq struct { + base.BaseRequest + Id int64 `json:"id"` +} + +type OpDeleteData struct { +} + +type OpDeleteResp struct { + base.BaseResponse + Data *OpDeleteData `json:"data"` +} + +// op 更新 +type OpUpdateReq struct { + base.BaseRequest + *dbstruct.WorkerId +} + +type OpUpdateData struct { +} + +type OpUpdateResp struct { + base.BaseResponse + Data *OpUpdateData `json:"data"` +} + +// op 列表 +type OpListByMidReq struct { + base.BaseRequest + Mid int64 `json:"mid"` +} + +type OpListByMidData struct { + WorkerId *dbstruct.WorkerId `json:"worker_id"` + Offset int `json:"offset"` + More int `json:"more"` +} + +type OpListByMidResp struct { + base.BaseResponse + Data *OpListByMidData `json:"data"` +} diff --git a/app/mix/controller/hvyogo_callback.go b/app/mix/controller/hvyogo_callback.go new file mode 100644 index 00000000..1a0e7e25 --- /dev/null +++ b/app/mix/controller/hvyogo_callback.go @@ -0,0 +1,27 @@ +package controller + +import ( + "service/api/errcode" + "service/api/message/response" + hvyogoproto "service/api/proto/hvyogo/proto" + "service/app/mix/service" + "service/bizcommon/util" + "service/library/logger" + + "github.com/gin-gonic/gin" +) + +func HvyogoCallback(ctx *gin.Context) { + + req := ctx.MustGet("client_req").(*hvyogoproto.HvyogoCallbackReq) + + // 存入数据 + ec := service.DefaultService.SaveHvyogoCallback(ctx, req) + if ec != errcode.ErrCodeWorkerIdSrvOk { + logger.Error("SaveHvyogoCallback fail, req: %v, ec: %v", util.ToJson(req), ec) + response.ReplyErrCodeMsg(ctx, ec) + return + } + + response.ReplyOk(ctx, nil) +} diff --git a/app/mix/controller/init.go b/app/mix/controller/init.go index 1ea583b6..99ba12e5 100644 --- a/app/mix/controller/init.go +++ b/app/mix/controller/init.go @@ -31,6 +31,7 @@ import ( daily_statement_zone_info_proto "service/api/proto/daily_statement_zone_info/proto" feedbackproto "service/api/proto/feedback/proto" footprintproto "service/api/proto/footprint/proto" + hvyogoproto "service/api/proto/hvyogo/proto" loginproto "service/api/proto/login/proto" momentproto "service/api/proto/moment/proto" moment_audit_taskproto "service/api/proto/moment_audit_task/proto" @@ -306,6 +307,9 @@ func Init(r *gin.Engine) { opVasPayGroup.POST("zone_refund_list", middleware.JSONParamValidator(zoneproto.OpZoneRefundListParam{}), OpZoneRefundList) opVasPayGroup.POST("manual_unlock_wechat", middleware.JSONParamValidator(zoneproto.OpManualUnlockWechatParam{}), OpManualUnlockWechat) + extEsbGroup := r.Group("/ext/hvyogo") + extEsbGroup.POST("agree_callback", middleware.FORMParamValidator(hvyogoproto.HvyogoCallbackReq{}), middleware.RequestDecryptor(), HvyogoCallback) + // 验证码 opVeriCodeGroup := r.Group("/op/veri_code", PrepareOp()) opVeriCodeGroup.POST("send", middleware.JSONParamValidator(vericodeproto.OpSendReq{}), middleware.RequestDecryptor(), OpSendVeriCode) diff --git a/app/mix/dao/mongo.go b/app/mix/dao/mongo.go index 3fd64a9a..6bf538e2 100644 --- a/app/mix/dao/mongo.go +++ b/app/mix/dao/mongo.go @@ -42,6 +42,7 @@ import ( userwxaddcheckproto "service/api/proto/userwxaddcheck/proto" vericodeproto "service/api/proto/vericode/proto" video_moderation_task_proto "service/api/proto/video_moderation_task/proto" + workeridproto "service/api/proto/worker_id/proto" zoneproto "service/api/proto/zone/proto" zone_collaborator_proto "service/api/proto/zone_collaborator/proto" zone_third_partner_proto "service/api/proto/zone_third_partner/proto" @@ -213,6 +214,9 @@ const ( DBStreamerScore = "streamer_score" COLStreamerScore = "streamer_score" + + DBWorkerId = "worker_id" + COLWorkerId = "worker_id" ) // 商品表 @@ -539,6 +543,11 @@ func (m *Mongo) getColStreamerScore() *qmgo.Collection { return m.clientMix.Database(DBStreamerScore).Collection(COLStreamerScore) } +// 用户职业者id映射表表 +func (m *Mongo) getColWorkerId() *qmgo.Collection { + return m.clientMix.Database(DBWorkerId).Collection(COLWorkerId) +} + // 商品相关 func (m *Mongo) CreateProduct(ctx *gin.Context, product *dbstruct.Product) error { col := m.getColProduct() @@ -5217,3 +5226,47 @@ func (m *Mongo) SetStreamerScore(ctx *gin.Context, list []*dbstruct.StreamerScor } return err } + +// 用户职业者id映射表相关 +func (m *Mongo) CreateWorkerId(ctx *gin.Context, worker_id *dbstruct.WorkerId) error { + col := m.getColWorkerId() + _, err := col.InsertOne(ctx, worker_id) + return err +} + +func (m *Mongo) UpdateWorkerId(ctx *gin.Context, worker_id *dbstruct.WorkerId) error { + col := m.getColWorkerId() + set := util.EntityToM(worker_id) + set["ut"] = time.Now().Unix() + up := qmgo.M{ + "$set": set, + } + err := col.UpdateId(ctx, worker_id.Id, up) + return err +} + +func (m *Mongo) DeleteWorkerId(ctx *gin.Context, id int64) error { + col := m.getColWorkerId() + update := qmgo.M{ + "$set": qmgo.M{ + "del_flag": 1, + }, + } + err := col.UpdateId(ctx, id, update) + return err +} + +func (m *Mongo) GetWorkerIdByMid(ctx *gin.Context, req *workeridproto.OpListByMidReq) (*dbstruct.WorkerId, error) { + workerId := &dbstruct.WorkerId{} + col := m.getColWorkerId() + query := qmgo.M{ + "mid": req.Mid, + "del_flag": 0, + } + err := col.Find(ctx, query).One(workerId) + if err == qmgo.ErrNoSuchDocuments { + err = nil + return nil, err + } + return workerId, err +} diff --git a/app/mix/service/logic/worker_id.go b/app/mix/service/logic/worker_id.go new file mode 100644 index 00000000..4dd53019 --- /dev/null +++ b/app/mix/service/logic/worker_id.go @@ -0,0 +1,65 @@ +package logic + +import ( + "service/api/consts" + worker_idproto "service/api/proto/worker_id/proto" + "service/app/mix/dao" + "service/dbstruct" + "service/library/idgenerator" + "service/library/logger" + "time" + + "github.com/gin-gonic/gin" + goproto "google.golang.org/protobuf/proto" +) + +type WorkerId struct { + store *dao.Store +} + +func NewWorkerId(store *dao.Store) (a *WorkerId) { + a = &WorkerId{ + store: store, + } + return +} + +func (p *WorkerId) OpCreate(ctx *gin.Context, req *worker_idproto.OpCreateReq) error { + req.WorkerId.Id = goproto.Int64(idgenerator.GenWorkerIdId()) + req.WorkerId.Ct = goproto.Int64(time.Now().Unix()) + req.WorkerId.Ut = goproto.Int64(time.Now().Unix()) + req.WorkerId.DelFlag = goproto.Int64(consts.Exist) + err := p.store.CreateWorkerId(ctx, req.WorkerId) + if err != nil { + logger.Error("CreateWorkerId fail, err: %v", err) + return err + } + return nil +} + +func (p *WorkerId) OpUpdate(ctx *gin.Context, req *worker_idproto.OpUpdateReq) error { + err := p.store.UpdateWorkerId(ctx, req.WorkerId) + if err != nil { + logger.Error("UpdateWorkerId fail, err: %v", err) + return err + } + return nil +} + +func (p *WorkerId) OpDelete(ctx *gin.Context, id int64) error { + err := p.store.DeleteWorkerId(ctx, id) + if err != nil { + logger.Error("DeleteWorkerId fail, err: %v", err) + return err + } + return nil +} + +func (p *WorkerId) OpListByMid(ctx *gin.Context, req *worker_idproto.OpListByMidReq) (*dbstruct.WorkerId, error) { + workerId, err := p.store.GetWorkerIdByMid(ctx, req) + if err != nil { + logger.Error("GetWorkerIdList fail, err: %v", err) + return nil, err + } + return workerId, nil +} diff --git a/app/mix/service/service.go b/app/mix/service/service.go index 93468f13..312a4253 100644 --- a/app/mix/service/service.go +++ b/app/mix/service/service.go @@ -2,6 +2,7 @@ package service import ( "encoding/base64" + "encoding/json" "errors" "fmt" "service/api/base" @@ -21,6 +22,7 @@ import ( daily_statement_zone_info_proto "service/api/proto/daily_statement_zone_info/proto" feedbackproto "service/api/proto/feedback/proto" footprintproto "service/api/proto/footprint/proto" + hvyogoproto "service/api/proto/hvyogo/proto" imageaudittaskproto "service/api/proto/imageaudittask/proto" loginproto "service/api/proto/login/proto" mediaproto "service/api/proto/media/proto" @@ -39,6 +41,7 @@ import ( userwxaddcheckproto "service/api/proto/userwxaddcheck/proto" vasproto "service/api/proto/vas/proto" vericodeproto "service/api/proto/vericode/proto" + workeridproto "service/api/proto/worker_id/proto" zoneproto "service/api/proto/zone/proto" zone_collaborator_proto "service/api/proto/zone_collaborator/proto" zone_third_partner_proto "service/api/proto/zone_third_partner/proto" @@ -131,6 +134,7 @@ var ( _DefaultStreamerAcct *logic.StreamerAcct _DefaultContentAuditRTI *logic.ContentAuditRTI _DefaultStreamerScore *logic.StreamerScore + _DefaultWorkerId *logic.WorkerId ) type Service struct { @@ -222,6 +226,7 @@ func (s *Service) Init(c any) (err error) { _DefaultDailyStatementZoneInfo = logic.NewDailyStatementZoneInfo(store) _DefaultContentAuditRTI = logic.NewContentAuditRTI(store) _DefaultStreamerScore = logic.NewStreamerScore(store) + _DefaultWorkerId = logic.NewWorkerId(store) _DefaultVas = logic.NewVas(store, _DefaultStreamer, _DefaultAccount, _DefaultZone, _DefaultZoneThirdPartner, _DefaultZoneCollaborator) _DefaultStreamerAcct = logic.NewStreamerAcct(store) @@ -4058,3 +4063,98 @@ func (s *Service) OpGetDailyStatementZoneInfoList(ctx *gin.Context, req *daily_s return } + +// WorkerId +func (s *Service) OpCreateWorkerId(ctx *gin.Context, req *workeridproto.OpCreateReq) (ec errcode.ErrCode) { + ec = errcode.ErrCodeWorkerIdSrvOk + err := _DefaultWorkerId.OpCreate(ctx, req) + if err != nil { + logger.Error("OpCreate fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeWorkerIdSrvFail + return + } + return +} + +func (s *Service) OpUpdateWorkerId(ctx *gin.Context, req *workeridproto.OpUpdateReq) (ec errcode.ErrCode) { + ec = errcode.ErrCodeWorkerIdSrvOk + err := _DefaultWorkerId.OpUpdate(ctx, req) + if err == qmgo.ErrNoSuchDocuments { + ec = errcode.ErrCodeWorkerIdNotExist + err = nil + return + } + if err != nil { + logger.Error("OpUpdate fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeWorkerIdSrvFail + return + } + return +} + +func (s *Service) OpDeleteWorkerId(ctx *gin.Context, id int64) (ec errcode.ErrCode) { + ec = errcode.ErrCodeWorkerIdSrvOk + err := _DefaultWorkerId.OpDelete(ctx, id) + if err != nil { + logger.Error("OpDelete fail, id: %v, err: %v", id, err) + ec = errcode.ErrCodeWorkerIdSrvFail + return + } + return +} + +func (s *Service) OpGetWorkerIdByMid(ctx *gin.Context, req *workeridproto.OpListByMidReq) (workerId *dbstruct.WorkerId, ec errcode.ErrCode) { + ec = errcode.ErrCodeWorkerIdSrvOk + workerId, err := _DefaultWorkerId.OpListByMid(ctx, req) + if err != nil { + logger.Error("OpGetWorkerIdByMid fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeWorkerIdSrvFail + return + } + return +} + +func (s *Service) SaveHvyogoCallback(ctx *gin.Context, req *hvyogoproto.HvyogoCallbackReq) (ec errcode.ErrCode) { + ec = errcode.ErrCodeWorkerIdSrvOk + + // 解析BusinessBody + vo := &hvyogoproto.HvyogoVO{} + if err := json.Unmarshal([]byte(req.BusinessBody), vo); err != nil { + logger.Error("Unmarshal fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeAssertionFail + return + } + + if vo.AgreeState != consts.HygAgreeState_Success { + return + } + + // 从手机号查找mid + phonehash := mycrypto.CryptoServiceInstance().SHA256.Encrypt([]byte(req.CooperatorId)) + list, err := _DefaultAccount.OpListByPhoneHash(ctx, phonehash) + if err != nil { + logger.Error("OpListByPhoneHash fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeAccountSrvFail + return + } + if len(list) == 0 { + logger.Error("No account entity was found, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeAccountNotExist + return + } + + // 存入数据 + err = _DefaultWorkerId.OpCreate(ctx, &workeridproto.OpCreateReq{ + WorkerId: &dbstruct.WorkerId{ + Mid: list[0].Mid, + WorkerId: goproto.String(vo.WorkerId), + }, + }) + if err != nil { + logger.Error("_DefaultWorkerId OpCreate fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeWorkerIdSrvFail + return + } + + return +} diff --git a/codecreate/codecreate.go b/codecreate/codecreate.go index 3b7a2bd5..f6020657 100644 --- a/codecreate/codecreate.go +++ b/codecreate/codecreate.go @@ -9,10 +9,10 @@ import ( func main() { genSource := &generator.GenSource{ - EntityName: "DailyStatementZoneInfo", - ModuleName: "daily_statement_zone_info", - EntityCNName: "空间相关每日报表", - ErrCodeSeq: "40", + EntityName: "WorkerId", + ModuleName: "worker_id", + EntityCNName: "用户职业者id映射表", + ErrCodeSeq: "41", } generator.CreateFileDirectory(genSource) diff --git a/codecreate/resource/EntityDefine.xlsx b/codecreate/resource/EntityDefine.xlsx index de6f31c59ae7484f4cd42ba3e2630123adab9241..e87515d0426c21777c673d85ed8ff7d6e88bd830 100644 GIT binary patch delta 10399 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 delta 9504 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 C&6sBZ diff --git a/dbstruct/worker_id.go b/dbstruct/worker_id.go new file mode 100644 index 00000000..07796a20 --- /dev/null +++ b/dbstruct/worker_id.go @@ -0,0 +1,11 @@ +package dbstruct + +type WorkerId struct { + Id *int64 `json:"id" bson:"_id"` // 慧用工职业者id表id + Mid *int64 `json:"mid" bson:"mid"` // 用户id + WorkerId *string `json:"worker_id" bson:"worker_id"` // 职业者id + Ct *int64 `json:"ct" bson:"ct"` // 创建时间 + Ut *int64 `json:"ut" bson:"ut"` // 更新时间 + DelFlag *int64 `json:"del_flag" bson:"del_flag"` // 删除标记 + +} diff --git a/etc/mix/mix-test.yaml b/etc/mix/mix-test.yaml index 26445c97..75c237a5 100644 --- a/etc/mix/mix-test.yaml +++ b/etc/mix/mix-test.yaml @@ -55,6 +55,11 @@ crypto: private_key: "" sha256: salts: "23XSka ZsMTz3 xdnKtT mx229Z aJ3VS7 ett3nV YDEgFB lBm57J 92hgkJ B0u9VF keyNK3 cB16Tm 63r1mX IUs471 ytbW7o ic8Pxw" + hyg_aes: + private_key: "Xbz1145141919810" + hyg_rsa: + private_key: "MIIBOgIBAAJBAMXPIjKV6CMi5O9tIXJWNIfnqXjqOZ1KmRByRAP073DU+gzMLygzEsrztJzbz/K/Julkz6XhheZ8vdz+boAl1HsCAwEAAQJAD5PNRKCLhGDWGIjGjoN9ZYp3zR7c5KcgQKxO8OTUClWHxXWAHSO8WEMDUjYTB9xhTbzyyjfOV7GJf2YFRgQUYQIhANEFkyp7xKLIq284zmvJ+YAqMMqc5Wxkz/0Kv77PERfxAiEA8kRrSX2QcObiQkE2Jxn4VlW8M+bHZMdfabjSkYgKvysCIHGYhweCpjYVut3CUKTR6q/VvhiPNjw3ebW6vsSTQmXxAiBBe4DMQmYyPhXV9q5eb0SSgWX3WV93u/PsLYqsz2qoNQIhAKiqbRvYMzfg+OTBKuuGu3m+QLav4TjYVSSPD8VVf7/z" + alipay: appid: "2021004115647165" diff --git a/library/configcenter/configcenter.go b/library/configcenter/configcenter.go index 2ce526a5..8b6f56df 100644 --- a/library/configcenter/configcenter.go +++ b/library/configcenter/configcenter.go @@ -74,10 +74,12 @@ type WxpayClientConfig struct { // 账号相关验密配置 type CryptoConfig struct { - *AESConfig `json:"aes" yaml:"aes"` //AES - *RSAConfig `json:"rsa" yaml:"rsa"` //RSA - *TokenConfig `json:"token" yaml:"token"` //token签名 - *SHA256Config `json:"sha256" yaml:"sha256"` //SHA256 + *AESConfig `json:"aes" yaml:"aes"` // AES + *RSAConfig `json:"rsa" yaml:"rsa"` // RSA + *TokenConfig `json:"token" yaml:"token"` // token签名 + *SHA256Config `json:"sha256" yaml:"sha256"` // SHA256 + *HygAESConfig `json:"hyg_aes" yml:"hyg_aes"` // 慧用工的AES + *HygRSAConfig `json:"hyg_rsa" yaml:"hyg_rsa"` // 慧用工的RSA } // 支付宝客户端配置 diff --git a/library/configcenter/cryptoconfigcenter.go b/library/configcenter/cryptoconfigcenter.go index b17793da..dc9c248c 100644 --- a/library/configcenter/cryptoconfigcenter.go +++ b/library/configcenter/cryptoconfigcenter.go @@ -15,3 +15,11 @@ type TokenConfig struct { type SHA256Config struct { SHA256Salts string `json:"salts" yaml:"salts"` //SHA256 salts } + +type HygAESConfig struct { + HygAESPrivateKey string `json:"private_key" yaml:"private_key"` //AES私钥 +} + +type HygRSAConfig struct { + HygRSAPrivateKey string `json:"private_key" yaml:"private_key"` //AES私钥 +} diff --git a/library/idgenerator/genid.go b/library/idgenerator/genid.go index 0043b581..806f8171 100644 --- a/library/idgenerator/genid.go +++ b/library/idgenerator/genid.go @@ -53,6 +53,7 @@ const ( NodeZoneSession // node 空间对话表 NodeZoneThirdPartner // node 空间代运营表 NodeDailyStatementZoneInfo // node 空间相关每日报表 + NodeWorkerId // node 用户职业者id映射表 ) func GenIdInt64(node int64) (int64, error) { @@ -252,3 +253,9 @@ func GenDailyStatementZoneInfoId() int64 { id, _ := GenIdInt64(NodeDailyStatementZoneInfo) return id } + +// worker_id +func GenWorkerIdId() int64 { + id, _ := GenIdInt64(NodeWorkerId) + return id +} diff --git a/library/mycrypto/aesCrypto.go b/library/mycrypto/aesCrypto.go index 5e6c52ba..9ed17fdc 100644 --- a/library/mycrypto/aesCrypto.go +++ b/library/mycrypto/aesCrypto.go @@ -23,6 +23,14 @@ func NewAesCrypto(cryptoConfig *configcenter.CryptoConfig) (aesCrypto *AesCrypto return } +func NewAesCryptoFromString(aesPrivateKeyStr string) (aesCrypto *AesCrypto, err error) { + aesCrypto = &AesCrypto{} + + //读取私钥 + aesCrypto.aesPriKey = []byte(aesPrivateKeyStr) + return +} + func (aesCrypto *AesCrypto) Encrypt(msg []byte) (encryptedBytes []byte, err error) { //CBC加密 block, err := aes.NewCipher(aesCrypto.aesPriKey) //block diff --git a/library/mycrypto/cryptoService.go b/library/mycrypto/cryptoService.go index 2ae5174c..718255c1 100644 --- a/library/mycrypto/cryptoService.go +++ b/library/mycrypto/cryptoService.go @@ -22,6 +22,8 @@ type CryptoService struct { RSA *RsaCrypto AES *AesCrypto SHA256 *Sha256Crypto + HygRSA *RsaCrypto + HygAES *AesCrypto } func (cryptoService *CryptoService) Init(cryptoConfig *configcenter.CryptoConfig) (err error) { @@ -39,5 +41,16 @@ func (cryptoService *CryptoService) Init(cryptoConfig *configcenter.CryptoConfig logger.Error("Init SHA256 Service failed! err:%v", err) return } + + if cryptoService.HygRSA, err = NewRsaCryptoFromString(cryptoConfig.HygRSAPrivateKey); err != nil { + logger.Error("Init HYG RSA Service failed! err:%v", err) + return + } + + if cryptoService.HygAES, err = NewAesCryptoFromString(cryptoConfig.HygAESPrivateKey); err != nil { + logger.Error("Init HYG AES Service failed! err:%v", err) + return + } + return } diff --git a/library/mycrypto/rsaCrypto.go b/library/mycrypto/rsaCrypto.go index a023f385..5031bc34 100644 --- a/library/mycrypto/rsaCrypto.go +++ b/library/mycrypto/rsaCrypto.go @@ -28,6 +28,24 @@ func NewRsaCrypto(cryptoConfig *configcenter.CryptoConfig) (rsaCrypto *RsaCrypto return } +func NewRsaCryptoFromString(rsaPrivateKeyStr string) (rsaCrypto *RsaCrypto, err error) { + rsaCrypto = &RsaCrypto{} + + //读取私钥 + rsaPriKey, err := readRSAPriKeyFromString(rsaPrivateKeyStr) + if err != nil { + logger.Error("read rsa primary key failed!", err) + return + } + + //读取公钥 + rsaPubKey := &rsaPriKey.PublicKey + + rsaCrypto.rsaPriKey = rsaPriKey + rsaCrypto.rsaPubKey = rsaPubKey + return +} + func (rsaCrypto *RsaCrypto) Encrypt(msg []byte) (encryptedBytes []byte, err error) { //公钥加密 encryptedBytes, err = rsa.EncryptPKCS1v15(rand.Reader, rsaCrypto.rsaPubKey, msg) @@ -68,3 +86,14 @@ func (rsaCrypto *RsaCrypto) readRSAPriKey(cryptoConfig *configcenter.CryptoConfi } return } + +func readRSAPriKeyFromString(rsaPrivateKeyStr string) (rsaPriKey *rsa.PrivateKey, err error) { + //读取私钥 + rsaPriKeyBytes, _ := base64.StdEncoding.DecodeString(rsaPrivateKeyStr) + rsaPriKey, err = x509.ParsePKCS1PrivateKey(rsaPriKeyBytes) + if err != nil { + logger.Error("Decoding rsa primary key failed, check your config, please!, err: %v", err) + return + } + return +} diff --git a/library/taginterceptor/decryptTagInterceptor.go b/library/taginterceptor/decryptTagInterceptor.go index 38c33d29..9c1b6020 100644 --- a/library/taginterceptor/decryptTagInterceptor.go +++ b/library/taginterceptor/decryptTagInterceptor.go @@ -18,6 +18,8 @@ func newDecryptTagInterceptor() *DecryptTagInterceptor { cryptoFuncMap := make(map[string]cryptoFunc) cryptoFuncMap["aes_cbc"] = mycrypto.CryptoServiceInstance().AES.Decrypt cryptoFuncMap["rsa"] = mycrypto.CryptoServiceInstance().RSA.Decrypt + cryptoFuncMap["hyg_rsa"] = mycrypto.CryptoServiceInstance().HygRSA.Decrypt + cryptoFuncMap["hyg_aes"] = mycrypto.CryptoServiceInstance().HygAES.Decrypt cryptoTagInterceptor := newCryptoTagInterceptor() cryptoTagInterceptor.LoadPrintableCryptoFunc(decryptTagInterceptor.getPrintableCryptoFunc(cryptoFuncMap)) diff --git a/library/taginterceptor/encryptTagInterceptor.go b/library/taginterceptor/encryptTagInterceptor.go index 1e12d224..0a191045 100644 --- a/library/taginterceptor/encryptTagInterceptor.go +++ b/library/taginterceptor/encryptTagInterceptor.go @@ -18,6 +18,8 @@ func newEncryptTagInterceptor() *EncryptTagInterceptor { cryptoFuncMap := make(map[string]cryptoFunc) cryptoFuncMap["aes_cbc"] = mycrypto.CryptoServiceInstance().AES.Encrypt cryptoFuncMap["rsa"] = mycrypto.CryptoServiceInstance().RSA.Encrypt + cryptoFuncMap["hyg_rsa"] = mycrypto.CryptoServiceInstance().HygRSA.Encrypt + cryptoFuncMap["hyg_aes"] = mycrypto.CryptoServiceInstance().HygAES.Encrypt cryptoTagInterceptor := newCryptoTagInterceptor() cryptoTagInterceptor.LoadPrintableCryptoFunc(encryptTagInterceptor.getPrintableCryptoFunc(cryptoFuncMap))