diff --git a/api/consts/consts.go b/api/consts/consts.go index 33b879dd..bc8bef41 100644 --- a/api/consts/consts.go +++ b/api/consts/consts.go @@ -58,6 +58,11 @@ const ( StreamerScoreFormulaKey = "streamer_score_formula" HvyogoSingleDistributeChargePercentageKey = "hvyogo_single_distribute_charge_percentage" AutoResponseKey = "auto_response" + QuestionMapKey = "question_map" + AgeScore2IQResultMapKey = "age_score_2_IQ_result_map" + ClassScore2ClassResultMapKey = "class_score_2_class_result_map" + IQResultMapKey = "IQ_result_map" + ClassResultMapKey = "class_result_map" ) // del_flag diff --git a/api/consts/predicate.go b/api/consts/predicate.go index 58366d5c..380bc8c9 100644 --- a/api/consts/predicate.go +++ b/api/consts/predicate.go @@ -28,3 +28,28 @@ const ( ContactCustomerService_FromUser = 0 ContactCustomerService_FromSupportor = 1 ) + +// Raven_IQ_test 年龄段谓词 +const ( + Age1 = 1 + Age2 = 2 + Age3 = 3 + Age4 = 4 + Age5 = 5 + Age6 = 6 + Age7 = 7 + Age8 = 8 + Age9 = 9 + Age10 = 10 +) + +// Raven_IQ_test 智商段谓词 +const ( + IQSegment1 = 1 + IQSegment2 = 2 + IQSegment3 = 3 + IQSegment4 = 4 + IQSegment5 = 5 + IQSegment6 = 6 + IQSegment7 = 7 +) diff --git a/api/errcode/errcode.go b/api/errcode/errcode.go index dd833d5c..f160f619 100644 --- a/api/errcode/errcode.go +++ b/api/errcode/errcode.go @@ -10,6 +10,7 @@ var ErrCodeMsgMap = map[ErrCode]string{ ErrCodeApolloReadFail: "Apollo配置读取失败,请立即联系管理员", ErrCodeApolloVersionFormatError: "Apollo版本号配置格式错误", ErrCodeUserVersionFormatError: "用户上送版本号格式错误", + ErrCodeIndexOutOfRange: "数组越界", ErrCodeAssertionFail: "严重错误:类型断言异常,请立即联系管理员", ErrCodeSelfOnlyOperation: "权限不足:该操作仅可对当前登录用户执行", ErrCodeRolePrivilegesNotEnough: "权限不足:当前用户角色禁止执行该操作", @@ -241,6 +242,10 @@ var ErrCodeMsgMap = map[ErrCode]string{ ErrCodeVideoModerationSrvFail: "视频审核服务错误", ErrCodeVideoModerationNotExist: "视频审核不存在", + + ErrCodeRavenIQTestSrvFail: "瑞文智商测试表服务错误", + ErrCodeRavenIQTestNotExist: "瑞文智商测试表不存在", + ErrCodeRavenIQTestQuestionNotExist: "瑞文智商测试表题目不存在", } const ( @@ -253,6 +258,7 @@ const ( ErrCodeAssertionFail ErrCode = -12 // 类型断言异常 ErrCodeApolloVersionFormatError ErrCode = -13 // Apollo版本号配置格式错误 ErrCodeUserVersionFormatError ErrCode = -14 // 用户上送版本号格式错误 + ErrCodeIndexOutOfRange ErrCode = -15 // 数组越界 ErrCodeSelfOnlyOperation ErrCode = -20 // 权限不足:该操作仅可对当前登录用户执行 ErrCodeRolePrivilegesNotEnough ErrCode = -21 // 权限不足: 当前用户角色禁止执行该操作 ErrCodeOpRoleOnlyOperation ErrCode = -22 // 权限不足: 后台系统仅允许超级管理员和运营操作 @@ -586,4 +592,9 @@ const ( // Websocket: 1xxxxx ErrCodeHandleWsFail ErrCode = -100001 // 长链连接失败 + // RavenIQTest: 101xxx + ErrCodeRavenIQTestSrvOk ErrCode = ErrCodeOk + ErrCodeRavenIQTestSrvFail ErrCode = -101001 // 瑞文智商测试表服务错误 + ErrCodeRavenIQTestNotExist ErrCode = -101002 // 瑞文智商测试表不存在 + ErrCodeRavenIQTestQuestionNotExist ErrCode = -101003 // 瑞文智商测试表题目不存在 ) diff --git a/api/proto/Raven_IQ_test/proto/Raven_IQ_test_api.go b/api/proto/Raven_IQ_test/proto/Raven_IQ_test_api.go new file mode 100644 index 00000000..e0ca724e --- /dev/null +++ b/api/proto/Raven_IQ_test/proto/Raven_IQ_test_api.go @@ -0,0 +1,36 @@ +package proto + +import ( + "service/api/base" + "service/dbstruct" +) + +// op 创建 +type ApiCreateReq struct { + base.BaseRequest + *dbstruct.RavenIQTest +} + +type ApiCreateData struct { + Id int64 `json:"id"` +} + +type ApiCreateResp struct { + base.BaseResponse + Data *ApiCreateData `json:"data"` +} + +// op 列表 +type ApiListReq struct { + base.BaseRequest + Id *int64 `json:"id"` +} + +type ApiListData struct { + *ApiListVO +} + +type ApiListResp struct { + base.BaseResponse + Data *ApiListData `json:"data"` +} diff --git a/api/proto/Raven_IQ_test/proto/Raven_IQ_test_op.go b/api/proto/Raven_IQ_test/proto/Raven_IQ_test_op.go new file mode 100644 index 00000000..3f9c3601 --- /dev/null +++ b/api/proto/Raven_IQ_test/proto/Raven_IQ_test_op.go @@ -0,0 +1,66 @@ +package proto + +import ( + "service/api/base" + "service/dbstruct" +) + +// op 创建 +type OpCreateReq struct { + base.BaseRequest + *dbstruct.RavenIQTest +} + +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.RavenIQTest +} + +type OpUpdateData struct { +} + +type OpUpdateResp struct { + base.BaseResponse + Data *OpUpdateData `json:"data"` +} + +// op 列表 +type OpListReq struct { + base.BaseRequest + Offset int `json:"offset"` + Limit int `json:"limit"` +} + +type OpListData struct { + List []*dbstruct.RavenIQTest `json:"list"` + Offset int `json:"offset"` + More int `json:"more"` +} + +type OpListResp struct { + base.BaseResponse + Data *OpListData `json:"data"` +} diff --git a/api/proto/Raven_IQ_test/proto/Raven_IQ_test_vo_api.go b/api/proto/Raven_IQ_test/proto/Raven_IQ_test_vo_api.go new file mode 100644 index 00000000..df072688 --- /dev/null +++ b/api/proto/Raven_IQ_test/proto/Raven_IQ_test_vo_api.go @@ -0,0 +1,47 @@ +package proto + +import ( + "service/apollostruct" + "service/dbstruct" +) + +type ApiListVO struct { + Id int64 `json:"id"` // 瑞文智商测试表id + UserId string `json:"user_id"` // 用户id + Age int64 `json:"age"` // 年龄 + TotalScore float64 `json:"total_score"` // 总得分 + IQ int64 `json:"IQ"` // 智商值 + *apollostruct.IQResult + ClassScoreList []*ApiClassScoreVO `json:"class_score_list"` // 大类得分list +} + +type ApiClassScoreVO struct { + ClassBlockId int64 `json:"class_block_id"` // 大类得分key + Score int64 `json:"score"` // 总分 + *apollostruct.ClassInfo + *apollostruct.ClassResult +} + +func (vo *ApiListVO) CopyRavenIQTest(test *dbstruct.RavenIQTest) *ApiListVO { + if test == nil { + return vo + } + vo.IQResult = &apollostruct.IQResult{} + vo.Id = test.GetId() + vo.UserId = test.GetUserId() + vo.Age = test.GetAge() + vo.TotalScore = test.GetTotalScore() + vo.IQ = test.GetIQ() + vo.IQBlockId = test.GetIQBlockId() + vo.ClassScoreList = make([]*ApiClassScoreVO, 0) + for _, score := range test.ClassScoreList { + vo.ClassScoreList = append(vo.ClassScoreList, &ApiClassScoreVO{ + ClassBlockId: score.GetClassBlockId(), + Score: score.GetConvertedScore(), + ClassInfo: &apollostruct.ClassInfo{ + ClassId: score.GetClassId(), + }, + }) + } + return vo +} diff --git a/api/proto/Raven_IQ_test/proto/not_null_def_api.go b/api/proto/Raven_IQ_test/proto/not_null_def_api.go new file mode 100644 index 00000000..bcc08211 --- /dev/null +++ b/api/proto/Raven_IQ_test/proto/not_null_def_api.go @@ -0,0 +1,20 @@ +package proto + +import ( + "service/library/validator" +) + +// api 查询 +func (p *ApiCreateReq) ProvideNotNullValue() (params []*validator.JsonParam) { + params = make([]*validator.JsonParam, 0) + params = append(params, validator.NewInt64PtrParam("请提供您的年龄!", p.Age)) + params = append(params, validator.NewStructSliceParam("请提供您的答案!", len(p.RavenIQTest.AnswerList))) + return params +} + +// api 经验增长 +func (p *ApiListReq) ProvideNotNullValue() (params []*validator.JsonParam) { + params = make([]*validator.JsonParam, 0) + params = append(params, validator.NewInt64PtrParam("请提供待查询的id!", p.Id)) + return params +} diff --git a/apollostruct/RavenIQtest.go b/apollostruct/RavenIQtest.go new file mode 100644 index 00000000..d5f4744f --- /dev/null +++ b/apollostruct/RavenIQtest.go @@ -0,0 +1,65 @@ +package apollostruct + +type QuestionMapCfg struct { + Map map[int64]*RavenIQTestQuestion `json:"map"` + ClassList []int64 `json:"class_list"` +} + +type RavenIQTestQuestion struct { + QuestionId int64 `json:"question_id" bson:"question_id"` // 问题Id + ClassId int64 `json:"class_id" bson:"class_id"` // 大类id + CorrectOption int64 `json:"correct_option" bson:"correct_option"` // 正确选项 +} + +type IQResult struct { + IQBlockId int64 `json:"IQ_Block_Id"` // IQ分段id + IQGrade string `json:"IQ_grade"` // 智商等级 + Percentile int64 `json:"percentile"` // 百分位 + IQLowerBound int64 `json:"IQ_lower_bound"` // IQ下界 + IQUpperBound int64 `json:"IQ_upper_bound"` // IQ上界 + Description string `json:"description"` // 描述 + Suggestions []string `json:"suggestions"` // 建议 +} + +type AgeScore2IQResultMapCfg struct { + AgeBlockIndex2ScoreBlockListMap map[int][]float64 `json:"age_block_index_2_score_block_list_map"` // 年龄分段到分数分布区域的映射map + AgeBlockList []int64 `json:"age_block_list"` // 年龄段区域,以年龄在该年龄段中第一次出现或最后一个比他小的位置作为年龄段索引 + ScoreBlockIndex2IQBlockIdMap map[int]int64 `json:"score_block_index_2_IQ_block_id_map"` // 分数分段到IQ范围id的映射map +} + +type ClassScore2ClassResultMapCfg struct { + Map map[int64][]int64 `json:"map" bson:"map"` // 大类Id到大类分数分布区域的映射map +} + +type IQResultMapCfg struct { + Map map[int64]*IQResult `json:"map"` +} + +type ClassResult struct { + Description string `json:"description"` // 描述 + Analysis string `json:"analysis"` // 成绩分析 + Suggestions []*Suggestion `json:"suggestions"` // 建议 +} + +type ClassInfo struct { + ClassId int64 `json:"class_id"` // 大类id + ClassName string `json:"class_name"` // 大类名称 + ClassFullScore int64 `json:"class_full_score"` // 大类满分 +} + +type Suggestion struct { + Title string `json:"title"` + Tips []*Tip `json:"tips"` +} + +type Tip struct { + Summary string `json:"summary"` // 概括 + Content string `json:"content"` // 内容 +} + +type ClassResultMapCfg struct { + Map map[int64]*ClassResult `json:"map"` + ClassInfoMap map[int64]*ClassInfo `json:"class_info_map"` + FullScore int64 `json:"full_score"` // 满分 + TotalScore int64 `json:"total_score"` // 总分 +} diff --git a/app/mix/controller/Raven_IQ_test_api.go b/app/mix/controller/Raven_IQ_test_api.go new file mode 100644 index 00000000..643275e2 --- /dev/null +++ b/app/mix/controller/Raven_IQ_test_api.go @@ -0,0 +1,42 @@ +package controller + +import ( + "service/api/errcode" + Raven_IQ_testproto "service/api/proto/Raven_IQ_test/proto" + "service/app/mix/service" + "service/bizcommon/util" + "service/library/logger" + + "github.com/gin-gonic/gin" +) + +func ApiCreateRavenIQTest(ctx *gin.Context) { + req := ctx.MustGet("client_req").(*Raven_IQ_testproto.ApiCreateReq) + id, ec := service.DefaultService.ApiCreateRavenIQTest(ctx, req) + if ec != errcode.ErrCodeRavenIQTestSrvOk { + logger.Error("ApiCreateRavenIQTest fail, req: %v, ec: %v", util.ToJson(req), ec) + ReplyErrorMsg(ctx, "server error") + return + } + + data := &Raven_IQ_testproto.ApiCreateData{ + Id: id, + } + ReplyOk(ctx, data) +} + +func ApiGetRavenIQTestList(ctx *gin.Context) { + req := ctx.MustGet("client_req").(*Raven_IQ_testproto.ApiListReq) + + vo, ec := service.DefaultService.ApiGetRavenIQTestList(ctx, req) + if ec != errcode.ErrCodeRavenIQTestSrvOk { + logger.Error("ApiGetRavenIQTestList fail, req: %v, ec: %v", util.ToJson(req), ec) + ReplyErrCodeMsg(ctx, ec) + return + } + + data := &Raven_IQ_testproto.ApiListData{ + ApiListVO: vo, + } + ReplyOk(ctx, data) +} diff --git a/app/mix/controller/init.go b/app/mix/controller/init.go index ddb633e0..756b1676 100644 --- a/app/mix/controller/init.go +++ b/app/mix/controller/init.go @@ -19,6 +19,7 @@ import ( "service/api/base" "service/api/consts" "service/api/errcode" + Raven_IQ_testproto "service/api/proto/Raven_IQ_test/proto" accountproto "service/api/proto/account/proto" account_cancellationproto "service/api/proto/account_cancellation/proto" accountpunishmentproto "service/api/proto/accountpunishment/proto" @@ -286,6 +287,11 @@ func Init(r *gin.Engine) { apiHvyogoGroup.POST("single_distribute", middleware.JSONParamValidator(hvyogoproto.ApiSingleDistributeReq{}), middleware.JwtAuthenticator(), middleware.RequestDecryptor(), ApiHvyogoSingleDistribute) apiHvyogoGroup.POST("worker_update", middleware.JSONParamValidator(hvyogoproto.ApiWorkerUpdateReq{}), middleware.JwtAuthenticator(), middleware.RequestDecryptor(), ApiHvyogoWorkerUpdate) + // 瑞文智商测试表 + apiRavenIQTestGroup := r.Group("/api/Raven_IQ_test", PrepareToC()) + apiRavenIQTestGroup.POST("create", middleware.JSONParamValidator(Raven_IQ_testproto.ApiCreateReq{}), ApiCreateRavenIQTest) + apiRavenIQTestGroup.POST("list", middleware.JSONParamValidator(Raven_IQ_testproto.ApiListReq{}), ApiGetRavenIQTestList) + // =============================== 以下是服务,只允许内网调用 =============================== // op相关,直接调用服务,不调用gateway diff --git a/app/mix/dao/mongo.go b/app/mix/dao/mongo.go index 001cbbe2..8cf77f79 100644 --- a/app/mix/dao/mongo.go +++ b/app/mix/dao/mongo.go @@ -17,6 +17,7 @@ import ( "go.mongodb.org/mongo-driver/bson" options2 "go.mongodb.org/mongo-driver/mongo/options" + Raven_IQ_testproto "service/api/proto/Raven_IQ_test/proto" accountproto "service/api/proto/account/proto" account_cancellationproto "service/api/proto/account_cancellation/proto" accountpunishmentproto "service/api/proto/accountpunishment/proto" @@ -235,6 +236,9 @@ const ( DBSingleDistributeHis = "single_distribute_his" COLSingleDistributeHis = "single_distribute_his" COLSingleDistributeLock = "single_distribute_lock" + + DBRavenIQTest = "Raven_IQ_test" + COLRavenIQTest = "Raven_IQ_test" ) // 商品表 @@ -611,6 +615,11 @@ func (m *Mongo) getColSingleDistributeLock() *qmgo.Collection { return m.clientMix.Database(DBSingleDistributeHis).Collection(COLSingleDistributeLock) } +// 瑞文智商测试表表 +func (m *Mongo) getColRavenIQTest() *qmgo.Collection { + return m.clientMix.Database(DBRavenIQTest).Collection(COLRavenIQTest) +} + // 商品相关 func (m *Mongo) CreateProduct(ctx *gin.Context, product *dbstruct.Product) error { col := m.getColProduct() @@ -6021,3 +6030,61 @@ func (m *Mongo) GetSingleDistributeHisById(ctx *gin.Context, id string) (*dbstru } return one, err } + +// 瑞文智商测试表相关 +func (m *Mongo) CreateRavenIQTest(ctx *gin.Context, Raven_IQ_test *dbstruct.RavenIQTest) error { + col := m.getColRavenIQTest() + _, err := col.InsertOne(ctx, Raven_IQ_test) + return err +} + +func (m *Mongo) UpdateRavenIQTest(ctx *gin.Context, Raven_IQ_test *dbstruct.RavenIQTest) error { + col := m.getColRavenIQTest() + set := util.EntityToM(Raven_IQ_test) + set["ut"] = time.Now().Unix() + up := qmgo.M{ + "$set": set, + } + err := col.UpdateId(ctx, Raven_IQ_test.Id, up) + return err +} + +func (m *Mongo) DeleteRavenIQTest(ctx *gin.Context, id int64) error { + col := m.getColRavenIQTest() + update := qmgo.M{ + "$set": qmgo.M{ + "del_flag": 1, + }, + } + err := col.UpdateId(ctx, id, update) + return err +} + +func (m *Mongo) GetRavenIQTestList(ctx *gin.Context, req *Raven_IQ_testproto.OpListReq) ([]*dbstruct.RavenIQTest, error) { + list := make([]*dbstruct.RavenIQTest, 0) + col := m.getColRavenIQTest() + query := qmgo.M{ + "del_flag": 0, + } + err := col.Find(ctx, query).Sort("-ct").Skip(int64(req.Offset)).Limit(int64(req.Limit)).All(&list) + if err == qmgo.ErrNoSuchDocuments { + err = nil + return list, err + } + return list, err +} + +func (m *Mongo) GetRavenIQTestById(ctx *gin.Context, id int64) (*dbstruct.RavenIQTest, error) { + test := &dbstruct.RavenIQTest{} + col := m.getColRavenIQTest() + query := qmgo.M{ + "_id": id, + "del_flag": 0, + } + err := col.Find(ctx, query).One(test) + if err == qmgo.ErrNoSuchDocuments { + err = nil + return nil, err + } + return test, err +} diff --git a/app/mix/dao/mongo_idseq.go b/app/mix/dao/mongo_idseq.go index 3e34532a..75a8bb2a 100644 --- a/app/mix/dao/mongo_idseq.go +++ b/app/mix/dao/mongo_idseq.go @@ -64,6 +64,9 @@ const ( COLImageAuditRTI = "image_audit_rti" COLTextAuditRTI = "text_audit_rti" COLVideoModerationRTI = "video_moderation_rti" + + DBRavenIQTestIdSeq = "Raven_IQ_test_id_seq" + COLRavenIQTestIdSeq = "Raven_IQ_test_id_seq" ) // UserIdSeq序列表 @@ -171,6 +174,11 @@ func (m *Mongo) getColVideoModerationRTI() *qmgo.Collection { return m.clientMix.Database(DBContentAuditRTI).Collection(COLVideoModerationRTI) } +// AccountIdSeq序列表 +func (m *Mongo) getColRavenIQTestIdSeq() *qmgo.Collection { + return m.clientMix.Database(DBRavenIQTestIdSeq).Collection(COLRavenIQTestIdSeq) +} + // account_id发号器 func (m *Mongo) GetAndUpdateAccountIdSeq(ctx *gin.Context) (accountIdSeq *dbstruct.AccountIdSeq, err error) { col := m.getColAccountIdSeq() @@ -619,3 +627,22 @@ func (m *Mongo) GetVideoModerationBatchId(ctx *gin.Context) (videoModerationBatc return } + +// Raven_IQ_test_id发号器 +func (m *Mongo) GetAndUpdateRavenIQTestIdSeq(ctx *gin.Context) (Raven_IQ_testIdSeq *dbstruct.RavenIQTestIdSeq, err error) { + col := m.getColRavenIQTestIdSeq() + + change := qmgo.Change{ + Update: qmgo.M{"$inc": qmgo.M{"seq": 1}}, + Upsert: true, + ReturnNew: false, + } + + instance := dbstruct.RavenIQTestIdSeq{} + if err = col.Find(ctx, qmgo.M{"_id": "Raven_IQ_test_id_seq_id"}).Apply(change, &instance); err != nil { + logger.Error("change error : %v", err) + return + } + + return &instance, err +} diff --git a/app/mix/service/apiservice.go b/app/mix/service/apiservice.go index 89e0428e..ef880788 100644 --- a/app/mix/service/apiservice.go +++ b/app/mix/service/apiservice.go @@ -2,6 +2,7 @@ package service import ( "fmt" + "math" "service/api/base" "service/api/consts" "service/api/errcode" @@ -53,6 +54,8 @@ import ( "strings" "time" + Raven_IQ_testproto "service/api/proto/Raven_IQ_test/proto" + "go.mongodb.org/mongo-driver/mongo" goproto "google.golang.org/protobuf/proto" @@ -3987,3 +3990,139 @@ func (s *Service) ApiHvyogoWorkerUpdate(ctx *gin.Context, req *hvyogoproto.ApiWo return } + +// RavenIQTest +func (s *Service) ApiCreateRavenIQTest(ctx *gin.Context, req *Raven_IQ_testproto.ApiCreateReq) (id int64, ec errcode.ErrCode) { + ec = errcode.ErrCodeRavenIQTestSrvOk + + // 缓存数据准备 + questionMapcfg := apollostruct.QuestionMapCfg{} + ageScore2IQResultMapCfg := apollostruct.AgeScore2IQResultMapCfg{} + classScore2ClassResultMapCfg := apollostruct.ClassScore2ClassResultMapCfg{} + IQResultMpcfg := apollostruct.IQResultMapCfg{} + classResultMpCfg := apollostruct.ClassResultMapCfg{} + keys := []string{consts.QuestionMapKey, consts.AgeScore2IQResultMapKey, consts.ClassScore2ClassResultMapKey, consts.IQResultMapKey, consts.ClassResultMapKey} + cfgs := []any{&questionMapcfg, &ageScore2IQResultMapCfg, &classScore2ClassResultMapCfg, &IQResultMpcfg, &classResultMpCfg} + err := apollo.GetJsons(keys, cfgs, apollo.ApolloOpts().SetNamespace("Raven_IQ_test")) + if err != nil { + logger.Error("Apollo read failed : %v", err) + return -1, errcode.ErrCodeApolloReadFail + } + + // 计算大类得分、总得分情况 + classScoreMap := make(map[int64]int64) + for _, classId := range questionMapcfg.ClassList { + classScoreMap[classId] = 0 + } + for _, answer := range req.AnswerList { + question, ok := questionMapcfg.Map[answer.GetQuestionId()] + if !ok { + return -1, errcode.ErrCodeRavenIQTestQuestionNotExist + } + if answer.GetSelectedOption() == question.CorrectOption { + classScoreMap[question.ClassId] += 1 + } + } + + req.ClassScoreList = make([]*dbstruct.RavenIQTestClassScore, 0) + totalScore := float64(0) + for classId, score := range classScoreMap { + classInfo := classResultMpCfg.ClassInfoMap[classId] + covertedScore := int64(math.Ceil(((float64(score) / float64(classResultMpCfg.TotalScore) * float64(classResultMpCfg.FullScore)) + float64(classInfo.ClassFullScore)) / 2)) + req.ClassScoreList = append(req.ClassScoreList, &dbstruct.RavenIQTestClassScore{ + ClassId: goproto.Int64(classId), + Score: goproto.Int64(score), + ConvertedScore: goproto.Int64(covertedScore), + }) + totalScore += float64(score) + } + req.TotalScore = goproto.Float64(totalScore) + + // 获取IQ分组id + ageBlockIdx := util.GetLastLessOrEqualForInt64(ageScore2IQResultMapCfg.AgeBlockList, req.GetAge()) + list := ageScore2IQResultMapCfg.AgeBlockIndex2ScoreBlockListMap[ageBlockIdx] + scoreBlockIdx := util.GetLastLessOrEqualForFloat64(list, req.GetTotalScore()) + if scoreBlockIdx == -1 || scoreBlockIdx >= len(list)-1 { + logger.Error("array index out of bound...") + return -1, errcode.ErrCodeIndexOutOfRange + } + IQBlockId := ageScore2IQResultMapCfg.ScoreBlockIndex2IQBlockIdMap[scoreBlockIdx] + + for _, classScore := range req.ClassScoreList { + classScoreBlockList := classScore2ClassResultMapCfg.Map[classScore.GetClassId()] + classScoreBlockIdx := util.GetLastLessOrEqualForInt64(classScoreBlockList, classScore.GetScore()) + classScore.ClassBlockId = goproto.Int64(classScore.GetClassId()*100 + int64(classScoreBlockIdx)) // 100 * 大类id + 偏移量 + } + + req.IQBlockId = goproto.Int64(IQBlockId) + + // 计算智商 + totalScoreLowerBound := list[scoreBlockIdx] + totalScoreUpperBound := list[scoreBlockIdx+1] - 1 + IQLowerBound := float64(IQResultMpcfg.Map[IQBlockId].IQLowerBound) + IQUpperBound := float64(IQResultMpcfg.Map[IQBlockId].IQUpperBound) + IQ := int64(math.Ceil(float64(totalScore-totalScoreLowerBound)/float64(totalScoreUpperBound-totalScoreLowerBound)*(IQUpperBound-IQLowerBound) + IQLowerBound)) + if IQ < 70 { + IQ = 70 + } + req.IQ = goproto.Int64(IQ) + + err = _DefaultRavenIQTest.OpCreate(ctx, &Raven_IQ_testproto.OpCreateReq{ + BaseRequest: req.BaseRequest, + RavenIQTest: req.RavenIQTest, + }) + if err != nil { + logger.Error("ApiCreate fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeRavenIQTestSrvFail + return + } + + id = req.RavenIQTest.GetId() + return +} + +func (s *Service) ApiGetRavenIQTestList(ctx *gin.Context, req *Raven_IQ_testproto.ApiListReq) (vo *Raven_IQ_testproto.ApiListVO, ec errcode.ErrCode) { + ec = errcode.ErrCodeRavenIQTestSrvOk + + vo = &Raven_IQ_testproto.ApiListVO{} + + test, err := _DefaultRavenIQTest.GetById(ctx, util.DerefInt64(req.Id)) + if err != nil { + logger.Error("ApiGetRavenIQTest fail, req: %v, err: %v", util.ToJson(req), err) + ec = errcode.ErrCodeRavenIQTestSrvFail + return + } + if test == nil { + logger.Error("No RavenIQTest entity was found, req: %v", util.ToJson(req)) + ec = errcode.ErrCodeRavenIQTestNotExist + return + } + + vo.CopyRavenIQTest(test) + + // 填充业务数据 + IQResultMpcfg := apollostruct.IQResultMapCfg{} + err = apollo.GetJson(consts.IQResultMapKey, &IQResultMpcfg, apollo.ApolloOpts().SetNamespace("Raven_IQ_test")) + if err != nil { + logger.Error("Apollo read failed : %v", err) + ec = errcode.ErrCodeApolloReadFail + return + } + + classResultMpCfg := apollostruct.ClassResultMapCfg{} + err = apollo.GetJson(consts.ClassResultMapKey, &classResultMpCfg, apollo.ApolloOpts().SetNamespace("Raven_IQ_test")) + if err != nil { + logger.Error("Apollo read failed : %v", err) + ec = errcode.ErrCodeApolloReadFail + return + } + + vo.IQResult = IQResultMpcfg.Map[vo.IQBlockId] + + for _, classScore := range vo.ClassScoreList { + classScore.ClassResult = classResultMpCfg.Map[classScore.ClassBlockId] + classScore.ClassInfo = classResultMpCfg.ClassInfoMap[classScore.ClassInfo.ClassId] + } + + return +} diff --git a/app/mix/service/logic/RavenIQTest.go b/app/mix/service/logic/RavenIQTest.go new file mode 100644 index 00000000..b807ec52 --- /dev/null +++ b/app/mix/service/logic/RavenIQTest.go @@ -0,0 +1,81 @@ +package logic + +import ( + "service/api/consts" + Raven_IQ_testproto "service/api/proto/Raven_IQ_test/proto" + "service/app/mix/dao" + "service/dbstruct" + "service/library/logger" + "time" + + "github.com/gin-gonic/gin" + goproto "google.golang.org/protobuf/proto" +) + +type RavenIQTest struct { + store *dao.Store +} + +func NewRavenIQTest(store *dao.Store) (a *RavenIQTest) { + a = &RavenIQTest{ + store: store, + } + return +} + +func (p *RavenIQTest) OpCreate(ctx *gin.Context, req *Raven_IQ_testproto.OpCreateReq) error { + + //产生mid + idSeq, err := p.store.GetAndUpdateRavenIQTestIdSeq(ctx) + if err != nil { + logger.Error("GetAndUpdateRavenIQTestIdSeq failed : %v", err) + return err + } + + req.RavenIQTest.Id = goproto.Int64(idSeq.Seq) + req.RavenIQTest.Ct = goproto.Int64(time.Now().Unix()) + req.RavenIQTest.Ut = goproto.Int64(time.Now().Unix()) + req.RavenIQTest.DelFlag = goproto.Int64(consts.Exist) + err = p.store.CreateRavenIQTest(ctx, req.RavenIQTest) + if err != nil { + logger.Error("CreateRavenIQTest fail, err: %v", err) + return err + } + return nil +} + +func (p *RavenIQTest) OpUpdate(ctx *gin.Context, req *Raven_IQ_testproto.OpUpdateReq) error { + err := p.store.UpdateRavenIQTest(ctx, req.RavenIQTest) + if err != nil { + logger.Error("UpdateRavenIQTest fail, err: %v", err) + return err + } + return nil +} + +func (p *RavenIQTest) OpDelete(ctx *gin.Context, id int64) error { + err := p.store.DeleteRavenIQTest(ctx, id) + if err != nil { + logger.Error("DeleteRavenIQTest fail, err: %v", err) + return err + } + return nil +} + +func (p *RavenIQTest) OpList(ctx *gin.Context, req *Raven_IQ_testproto.OpListReq) ([]*dbstruct.RavenIQTest, error) { + list, err := p.store.GetRavenIQTestList(ctx, req) + if err != nil { + logger.Error("GetRavenIQTestList fail, err: %v", err) + return make([]*dbstruct.RavenIQTest, 0), err + } + return list, nil +} + +func (p *RavenIQTest) GetById(ctx *gin.Context, id int64) (*dbstruct.RavenIQTest, error) { + test, err := p.store.GetRavenIQTestById(ctx, id) + if err != nil { + logger.Error("GetRavenIQTestListByIds fail, id: %v, err: %v", id, err) + return nil, err + } + return test, nil +} diff --git a/app/mix/service/service.go b/app/mix/service/service.go index 62e9f491..92ff3ec0 100644 --- a/app/mix/service/service.go +++ b/app/mix/service/service.go @@ -150,6 +150,7 @@ var ( _DefaultWorkerId *logic.WorkerId _DefaultSingleDistributeHis *logic.SingleDistributeHis _DefaultAutoResponseCreateTimes *logic.AutoResponseCreateTimes + _DefaultRavenIQTest *logic.RavenIQTest ) type Service struct { @@ -250,6 +251,7 @@ func (s *Service) Init(c any) (err error) { _DefaultWorkerId = logic.NewWorkerId(store) _DefaultSingleDistributeHis = logic.NewSingleDistributeHis(store) _DefaultAutoResponseCreateTimes = logic.NewAutoResponseCreateTimes(store) + _DefaultRavenIQTest = logic.NewRavenIQTest(store) _DefaultVas = logic.NewVas(store, _DefaultStreamer, _DefaultAccount, _DefaultZone, _DefaultZoneThirdPartner, _DefaultZoneCollaborator) _DefaultStreamerAcct = logic.NewStreamerAcct(store) diff --git a/bizcommon/util/util.go b/bizcommon/util/util.go index ff51e8c3..d8899cbb 100644 --- a/bizcommon/util/util.go +++ b/bizcommon/util/util.go @@ -337,3 +337,39 @@ func IsInt64SliceEqualAsSet(s1 []int64, s2 []int64) bool { } return true } + +func GetLastLessOrEqualForInt64(arr []int64, target int64) int { + low, high := 0, len(arr)-1 + result := -1 + + for low <= high { + mid := (low + high) / 2 + + if arr[mid] <= target { + result = mid // 记录当前找到的索引 + low = mid + 1 // 继续在右半部分查找 + } else { + high = mid - 1 // 目标值在左半部分,缩小搜索范围 + } + } + + return result +} + +func GetLastLessOrEqualForFloat64(arr []float64, target float64) int { + low, high := 0, len(arr)-1 + result := -1 + + for low <= high { + mid := (low + high) / 2 + + if arr[mid] <= target { + result = mid // 记录当前找到的索引 + low = mid + 1 // 继续在右半部分查找 + } else { + high = mid - 1 // 目标值在左半部分,缩小搜索范围 + } + } + + return result +} diff --git a/codecreate/codecreate.go b/codecreate/codecreate.go index 645bf5ac..723a640e 100644 --- a/codecreate/codecreate.go +++ b/codecreate/codecreate.go @@ -9,10 +9,10 @@ import ( func main() { genSource := &generator.GenSource{ - EntityName: "SingleDistributeHis", - ModuleName: "single_distribute_his", - EntityCNName: "慧用工下发打款历史表", - ErrCodeSeq: "42", + EntityName: "RavenIQTest", + ModuleName: "Raven_IQ_test", + EntityCNName: "瑞文智商测试表", + ErrCodeSeq: "101", } generator.CreateFileDirectory(genSource) diff --git a/codecreate/resource/EntityDefine.xlsx b/codecreate/resource/EntityDefine.xlsx index d65c315b..45cf4458 100644 Binary files a/codecreate/resource/EntityDefine.xlsx and b/codecreate/resource/EntityDefine.xlsx differ diff --git a/dbstruct/RavenIQTest.go b/dbstruct/RavenIQTest.go new file mode 100644 index 00000000..3c221061 --- /dev/null +++ b/dbstruct/RavenIQTest.go @@ -0,0 +1,111 @@ +package dbstruct + +type RavenIQTest struct { + Id *int64 `json:"id" bson:"_id"` // 瑞文智商测试表id + UserId *string `json:"user_id" bson:"user_id"` // 用户id + Age *int64 `json:"age" bson:"age"` // 年龄 + TotalScore *float64 `json:"total_score" bson:"total_score"` // 总得分 + IQBlockId *int64 `json:"IQ_block_id" bson:"IQ_block_id"` // 智商值key + IQ *int64 `json:"IQ" bson:"IQ"` // 智商值 + AnswerList []*RavenIQTestAnswer `json:"answer_list" bson:"answer_list"` // 答案list + ClassScoreList []*RavenIQTestClassScore `json:"class_score_list" bson:"class_score_list"` // 大类得分list + Ct *int64 `json:"ct" bson:"ct"` // 创建时间 + Ut *int64 `json:"ut" bson:"ut"` // 更新时间 + DelFlag *int64 `json:"del_flag" bson:"del_flag"` // 删除标记 +} + +type RavenIQTestAnswer struct { + QuestionId *int64 `json:"question_id" bson:"question_id"` // 问题Id + SelectedOption *int64 `json:"selected_option" bson:"selected_option"` // 选择的选项 +} + +type RavenIQTestClassScore struct { + ClassId *int64 `json:"class_id" bson:"class_id"` // 大类id + Score *int64 `json:"score" bson:"score"` // 得分 + ClassBlockId *int64 `json:"class_block_id" bson:"class_block_id"` // 大类得分key + ConvertedScore *int64 `json:"converted_score" bson:"converted_score"` // 折算后得分 +} + +func (p *RavenIQTest) GetId() int64 { + if p == nil || p.Id == nil { + return -1 + } + return *p.Id +} + +func (p *RavenIQTest) GetUserId() string { + if p == nil || p.UserId == nil { + return "" + } + return *p.UserId +} + +func (p *RavenIQTest) GetAge() int64 { + if p == nil || p.Age == nil { + return 0 + } + return *p.Age +} + +func (p *RavenIQTest) GetTotalScore() float64 { + if p == nil || p.TotalScore == nil { + return 0 + } + return *p.TotalScore +} + +func (p *RavenIQTest) GetIQBlockId() int64 { + if p == nil || p.IQBlockId == nil { + return 0 + } + return *p.IQBlockId +} + +func (p *RavenIQTest) GetIQ() int64 { + if p == nil || p.IQ == nil { + return 0 + } + return *p.IQ +} + +func (p *RavenIQTestAnswer) GetQuestionId() int64 { + if p == nil || p.QuestionId == nil { + return 0 + } + return *p.QuestionId +} + +func (p *RavenIQTestAnswer) GetSelectedOption() int64 { + if p == nil || p.SelectedOption == nil { + return 0 + } + return *p.SelectedOption +} + +func (p *RavenIQTestClassScore) GetClassId() int64 { + if p == nil || p.ClassId == nil { + return 0 + } + return *p.ClassId +} + +func (p *RavenIQTestClassScore) GetScore() int64 { + if p == nil || p.Score == nil { + return 0 + } + return *p.Score +} + +func (p *RavenIQTestClassScore) GetConvertedScore() int64 { + if p == nil || p.ConvertedScore == nil { + return 0 + } + return *p.ConvertedScore +} + +func (p *RavenIQTestClassScore) GetClassBlockId() int64 { + if p == nil || p.ClassBlockId == nil { + return 0 + } + return *p.ClassBlockId +} diff --git a/dbstruct/idSeq.go b/dbstruct/idSeq.go index 3c95ea0a..d73d5974 100644 --- a/dbstruct/idSeq.go +++ b/dbstruct/idSeq.go @@ -88,3 +88,7 @@ type TextAuditBatchId struct { type VideoModerationBatchId struct { BatchId string `json:"batch_id" bson:"batch_id"` //视频审核批次号 } + +type RavenIQTestIdSeq struct { + Seq int64 //用户Id序列号 +} diff --git a/etc/mix/mix-local.yaml b/etc/mix/mix-local.yaml index 60c0afd6..d78d5a0b 100644 --- a/etc/mix/mix-local.yaml +++ b/etc/mix/mix-local.yaml @@ -85,7 +85,7 @@ apollo: app_id: "wishpal_live_service" cluster: "dev" ip: "http://localhost:8080" - namespace_name: "application,tag,account_init,platform,support_wx_id" + namespace_name: "application,tag,account_init,platform,support_wx_id,Raven_IQ_test" secret: "" is_back_up_config: true sync_server_timeout: 2 diff --git a/etc/mix/mix-prod-offline.yaml b/etc/mix/mix-prod-offline.yaml index 0659f17a..f653da51 100644 --- a/etc/mix/mix-prod-offline.yaml +++ b/etc/mix/mix-prod-offline.yaml @@ -95,7 +95,7 @@ apollo: app_id: "wishpal_live_service" cluster: "dev" ip: "http://172.16.0.186:8080" - namespace_name: "application,tag,account_init,platform,support_wx_id,version,zone" + namespace_name: "application,tag,account_init,platform,support_wx_id,version,zone,Raven_IQ_test" secret: "" is_back_up_config: true sync_server_timeout: 2 diff --git a/etc/mix/mix-prod.yaml b/etc/mix/mix-prod.yaml index d174a3b4..acf6c27c 100644 --- a/etc/mix/mix-prod.yaml +++ b/etc/mix/mix-prod.yaml @@ -95,7 +95,7 @@ apollo: app_id: "wishpal_live_service" cluster: "dev" ip: "http://172.16.0.186:8080" - namespace_name: "application,tag,account_init,platform,support_wx_id,version,zone" + namespace_name: "application,tag,account_init,platform,support_wx_id,version,zone,Raven_IQ_test" secret: "" is_back_up_config: true sync_server_timeout: 2 diff --git a/etc/mix/mix-test-offline.yaml b/etc/mix/mix-test-offline.yaml index c40006f5..a8d401a0 100644 --- a/etc/mix/mix-test-offline.yaml +++ b/etc/mix/mix-test-offline.yaml @@ -97,7 +97,7 @@ apollo: app_id: "wishpal_live_service" cluster: "dev" ip: "http://localhost:8080" - namespace_name: "application,tag,account_init,platform,support_wx_id,version,zone" + namespace_name: "application,tag,account_init,platform,support_wx_id,version,zone,Raven_IQ_test" secret: "" is_back_up_config: true sync_server_timeout: 2 diff --git a/etc/mix/mix-test.yaml b/etc/mix/mix-test.yaml index 109ec30f..cc9e7648 100644 --- a/etc/mix/mix-test.yaml +++ b/etc/mix/mix-test.yaml @@ -96,7 +96,7 @@ apollo: app_id: "wishpal_live_service" cluster: "dev" ip: "http://172.31.37.71:8080" - namespace_name: "application,tag,account_init,platform,support_wx_id,version,zone" + namespace_name: "application,tag,account_init,platform,support_wx_id,version,zone,Raven_IQ_test" secret: "" is_back_up_config: true sync_server_timeout: 2 diff --git a/library/apollo/apollo.go b/library/apollo/apollo.go index 87e96a6e..b9b523dc 100644 --- a/library/apollo/apollo.go +++ b/library/apollo/apollo.go @@ -147,3 +147,23 @@ func GetFloat64Value(key string, opts ...*ApolloOptions) (value float64, err err } return } + +func GetJsons(keys []string, vs []interface{}, opts ...*ApolloOptions) (err error) { + opt := mergeApolloOptions(opts...) + for i, key := range keys { + var value string + if opt.GetNamespace() != "" { + value = defaultApolloClient.GetConfig(opt.GetNamespace()).GetStringValue(key, opt.GetDefaultValue()) + } else { + value = defaultApolloClient.GetStringValue(key, opt.GetDefaultValue()) + } + if value != "" { + err = json.Unmarshal([]byte(value), vs[i]) + if err != nil { + return + } + } + } + + return +}