sunsheng 1 år sedan
förälder
incheckning
057a73578e
5 ändrade filer med 330 tillägg och 64 borttagningar
  1. 52 51
      src/api/learnLog.go
  2. 14 9
      src/api/router.go
  3. 262 3
      src/api/statistics.go
  4. 1 0
      src/api/user.go
  5. 1 1
      src/db/model/exeamLog.go

+ 52 - 51
src/api/learnLog.go

@@ -9,11 +9,12 @@ import (
 
 	"github.com/gin-gonic/gin"
 	"go.mongodb.org/mongo-driver/bson"
+	"go.mongodb.org/mongo-driver/bson/primitive"
 )
 
 // 具体数据库中创建
-// 每个用户每个模块有一条记录
-func SyncLearnTime(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+// 每个用户每个模块有多条记录,每次学习生成一条记录
+func CreateLearnLog(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 	db := c.Param("scope")
 	if len(db) == 0 {
 		return nil, errors.New("scope不能为空")
@@ -27,62 +28,62 @@ func SyncLearnTime(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 	if len(learnLog.Cid) < 1 {
 		return nil, errors.New("模块id不能为空")
 	}
-	// 查询该学员的学习记录是否存在
-	// 一年一条记录
-	searchLearnLog := &model.LearnLog{}
-	year := time.Now().Year()                                      // 获取当前年份
-	loc, _ := time.LoadLocation("Asia/Shanghai")                   // 加载中国的时区
-	startTime := time.Date(year, time.January, 1, 0, 0, 0, 0, loc) // 使用中国的时区
-	endTime := time.Date(year+1, time.January, 1, 0, 0, 0, 0, loc) // 使用中国的时区
-	found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
-		Db: db,
-		Query: repo.Map{
-			"uid": apictx.User.ID,
-			"cid": learnLog.Cid,
-			"createTime": repo.Map{
-				"$gte": startTime,
-				"$lt":  endTime,
-			},
-		},
-	}, searchLearnLog)
-	if err != nil {
-		return nil, err
-	}
-	if found {
-		update := bson.M{"$inc": bson.M{"learnTime": 1}, "$set": bson.M{"updateTime": time.Now()}}
-		res, err := repo.RepoUpdateSetDbDocProps(apictx.CreateRepoCtx(), db, repo.CollectionLearnLog, searchLearnLog.Id.Hex(), update)
-		if err != nil {
-			return nil, err
-		}
-		if res.ModifiedCount > 0 {
-			return true, nil
-		}
-		return false, nil
-	}
+	// // 查询该学员的学习记录是否存在
+	// // 一年一条记录
+	// searchLearnLog := &model.LearnLog{}
+	// year := time.Now().Year()                                      // 获取当前年份
+	// loc, _ := time.LoadLocation("Asia/Shanghai")                   // 加载中国的时区
+	// startTime := time.Date(year, time.January, 1, 0, 0, 0, 0, loc) // 使用中国的时区
+	// endTime := time.Date(year+1, time.January, 1, 0, 0, 0, 0, loc) // 使用中国的时区
+	// found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+	// 	Db: db,
+	// 	Query: repo.Map{
+	// 		"uid": apictx.User.ID,
+	// 		"cid": learnLog.Cid,
+	// 		"createTime": repo.Map{
+	// 			"$gte": startTime,
+	// 			"$lt":  endTime,
+	// 		},
+	// 	},
+	// }, searchLearnLog)
+	// if err != nil {
+	// 	return nil, err
+	// }
+	// if found {
+	// 	update := bson.M{"$inc": bson.M{"learnTime": 1}, "$set": bson.M{"updateTime": time.Now()}}
+	// 	res, err := repo.RepoUpdateSetDbDocProps(apictx.CreateRepoCtx(), db, repo.CollectionLearnLog, searchLearnLog.Id.Hex(), update)
+	// 	if err != nil {
+	// 		return nil, err
+	// 	}
+	// 	if res.ModifiedCount > 0 {
+	// 		return true, nil
+	// 	}
+	// 	return false, nil
+	// }
 
 	// 没找到数据,创建
 	learnLog.Uid = apictx.User.ID
-	zero := 1
+	zero := 0
 	learnLog.LearnTime = &zero
 	learnLog.CreateTime = time.Now()
 	learnLog.UpdateTime = time.Now()
-	_, err = repo.RepoAddDbDoc(apictx.CreateRepoCtx(), db, repo.CollectionLearnLog, learnLog)
-	if err != nil {
-		log.Error(err)
-		return false, nil
-	}
-	return true, nil
+	return repo.RepoAddDbDoc(apictx.CreateRepoCtx(), db, repo.CollectionLearnLog, learnLog)
+	// if err != nil {
+	// 	log.Error(err)
+	// 	return false, nil
+	// }
+	// return true, nil
 }
 
 // 每分钟记录下学习时长
 // sync/time/:id/:scope
-// func SyncLearnTime(c *gin.Context, apictx *ApiSession) (interface{}, error) {
-// 	id := c.Param("id")
-// 	db := c.Param("scope")
-// 	objId, _ := primitive.ObjectIDFromHex(id)
-// 	if objId.IsZero() {
-// 		return nil, errors.New("id错误")
-// 	}
-// 	update := bson.M{"$inc": bson.M{"learnTime": 1}, "$set": bson.M{"updateTime": time.Now()}}
-// 	return repo.RepoUpdateSetDbDocProps(apictx.CreateRepoCtx(), db, repo.CollectionLearnLog, id, update)
-// }
+func SyncLearnTime(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	id := c.Param("id")
+	db := c.Param("scope")
+	objId, _ := primitive.ObjectIDFromHex(id)
+	if objId.IsZero() {
+		return nil, errors.New("id错误")
+	}
+	update := bson.M{"$inc": bson.M{"learnTime": 1}, "$set": bson.M{"updateTime": time.Now()}}
+	return repo.RepoUpdateSetDbDocProps(apictx.CreateRepoCtx(), db, repo.CollectionLearnLog, id, update)
+}

+ 14 - 9
src/api/router.go

@@ -31,6 +31,7 @@ func RegRouters(svc *Service) {
 	root.POSTJWT("/category/update", UpdateCategory)
 
 	// 学习记录
+	root.POSTJWT("/learnLog/create/:scope", CreateLearnLog)
 	root.POSTJWT("/learnLog/sync/time/:scope", SyncLearnTime)
 
 	// 考核试题管理
@@ -48,16 +49,20 @@ func RegRouters(svc *Service) {
 	// 考核记录列表
 	root.GETJWT("/exeamLog/list/:scope", ExeamLogList)
 
-	// todo
+	// 本年每月学习时长 type/year/uid
 	root.GETJWT("/statistics/learn/process/:scope", StatisticsLearnProcess)
-
-	// 学习记录统计 / 考核记录统计
-
-	// 管理员:选择学员查看,理论和实操分开
-	// 学习记录统计: x月份 y时长/ 本年
-	// 考核记录统计: 历史考核记录[v] | 考核列表
-
-	// 考虑创建一张统计表,学习完成把当前学习时长写入统计表
+	// 个人:上次学习时长 uid
+	root.GETJWT("/statistics/last/learnTime/:scope", StatisticsLastLearnTime)
+	// 个人:用户总学习时长 uid
+	root.GETJWT("/statistics/total/learnTime/:scope", StatisticsTotalLearnTime)
+	// 个人: 获取最后一场考试的成绩 uid
+	root.GETJWT("/statistics/last/exeamScore/:scope", StatisticsLastExeamScore)
+	// 个人: 获取历史最好成绩 uid
+	root.GETJWT("/statistics/best/exeamScore/:scope", StatisticsBestExeamScore)
+	// 个人: 已学习模块数 uid
+	root.GETJWT("/statistics/learned/modules/:scope", StatisticsLearnedModules)
+	// 个人: 统计用户本年每月考核正确和错误数 year
+	root.GETJWT("/statistics/exeanTF/Rate/:scope", StatisticsExeamTFRate)
 
 }
 

+ 262 - 3
src/api/statistics.go

@@ -2,26 +2,40 @@ package api
 
 import (
 	"context"
+	"copter-train/db/model"
 	"copter-train/db/repo"
 	"errors"
 	"strconv"
+	"time"
 
 	"github.com/gin-gonic/gin"
 	"go.mongodb.org/mongo-driver/bson"
 	"go.mongodb.org/mongo-driver/mongo"
 )
 
+// 本年每月学习时长
 func StatisticsLearnProcess(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 	db := c.Param("scope")
 	if len(db) == 0 {
 		return nil, errors.New("scope不能为空")
 	}
+	// 如果没有uid参数,则查询当前用户的
 	uid := c.Query("uid")
+	if len(uid) == 0 {
+		uid = apictx.User.ID
+	}
 	_type := c.Query("type")
+	// 如果没有年份参数,则查询当前年份的
 	_year := c.Query("year")
-	year, err := strconv.Atoi(_year)
-	if err != nil {
-		return nil, errors.New("year参数错误")
+	year := 2024
+	if len(_year) == 0 {
+		year = time.Now().Year()
+	} else {
+		year_, err := strconv.Atoi(_year)
+		if err != nil {
+			return nil, errors.New("year参数错误")
+		}
+		year = year_
 	}
 
 	colls := apictx.CreateRepoCtx().Client.GetDbCollection(db, repo.CollectionLearnLog)
@@ -29,6 +43,7 @@ func StatisticsLearnProcess(c *gin.Context, apictx *ApiSession) (interface{}, er
 
 }
 
+// 整个模块的学习时长 不需要cid约束
 func AggregateMonthlyLearnTime(colls *mongo.Collection, uid string, learnType string, year int) ([]bson.M, error) {
 	pipeline := mongo.Pipeline{
 		{
@@ -85,3 +100,247 @@ func AggregateMonthlyLearnTime(colls *mongo.Collection, uid string, learnType st
 
 	return results, nil
 }
+
+// 个人:上次学习时长
+// 获取最新一条数据
+func StatisticsLastLearnTime(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	db := c.Param("scope")
+	if len(db) == 0 {
+		return nil, errors.New("scope不能为空")
+	}
+	// 如果没有uid参数,则查询当前用户的
+	uid := c.Query("uid")
+	if len(uid) == 0 {
+		uid = apictx.User.ID
+	}
+
+	learnLog := &model.LearnLog{}
+	found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+		Db:          db,
+		CollectName: repo.CollectionLearnLog,
+		Query:       repo.Map{"uid": uid},
+		Sort:        bson.M{"createTime": -1},
+	}, learnLog)
+	if err != nil {
+		return nil, err
+	}
+	if !found {
+		return 0, nil
+	}
+	return learnLog.LearnTime, nil
+}
+
+// 个人:用户总学习时长
+func StatisticsTotalLearnTime(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	db := c.Param("scope")
+	if len(db) == 0 {
+		return nil, errors.New("scope不能为空")
+	}
+	// 如果没有uid参数,则查询当前用户的
+	uid := c.Query("uid")
+	if len(uid) == 0 {
+		uid = apictx.User.ID
+	}
+
+	colls := apictx.CreateRepoCtx().Client.GetDbCollection(db, repo.CollectionLearnLog)
+	return AggregateTotalLearnTime(colls, uid)
+
+}
+
+// 查询用户学习总时长
+func AggregateTotalLearnTime(colls *mongo.Collection, uid string) (int, error) {
+	pipeline := mongo.Pipeline{
+		{
+			{"$match", bson.D{
+				{"uid", uid},
+			}},
+		},
+		{
+			{"$group", bson.D{
+				{"_id", "$uid"},
+				{"totalLearnTime", bson.D{
+					{"$sum", "$learnTime"},
+				}},
+			}},
+		},
+	}
+
+	cursor, err := colls.Aggregate(context.TODO(), pipeline)
+	if err != nil {
+		return 0, err
+	}
+
+	var results []bson.M
+	if err = cursor.All(context.TODO(), &results); err != nil {
+		return 0, err
+	}
+
+	if len(results) > 0 {
+		return results[0]["totalLearnTime"].(int), nil
+	}
+	return 0, nil
+}
+
+// 个人: 获取最后一场考试的成绩
+func StatisticsLastExeamScore(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	db := c.Param("scope")
+	if len(db) == 0 {
+		return nil, errors.New("scope不能为空")
+	}
+	// 如果没有uid参数,则查询当前用户的
+	uid := c.Query("uid")
+	if len(uid) == 0 {
+		uid = apictx.User.ID
+	}
+
+	exeamLog := &model.ExeamLog{}
+	found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+		Db:          db,
+		CollectName: repo.CollectionExeamLog,
+		Query:       repo.Map{"uid": uid},
+		Sort:        bson.M{"createTime": -1},
+	}, exeamLog)
+	if err != nil {
+		return nil, err
+	}
+	if !found {
+		return 0, nil
+	}
+	return exeamLog.Score, nil
+}
+
+// 个人: 获取历史最好成绩
+func StatisticsBestExeamScore(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	db := c.Param("scope")
+	if len(db) == 0 {
+		return nil, errors.New("scope不能为空")
+	}
+	// 如果没有uid参数,则查询当前用户的
+	uid := c.Query("uid")
+	if len(uid) == 0 {
+		uid = apictx.User.ID
+	}
+
+	exeamLog := &model.ExeamLog{}
+	found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
+		Db:          db,
+		CollectName: repo.CollectionExeamLog,
+		Query:       repo.Map{"uid": uid},
+		Sort:        bson.M{"score": -1},
+	}, exeamLog)
+	if err != nil {
+		return nil, err
+	}
+	if !found {
+		return 0, nil
+	}
+	return exeamLog.Score, nil
+}
+
+// 个人: 已学习模块数
+// 统计各个模块的学习总时长,根据总时长时间判断是否已学习
+// 只需要返回统计各个模块的时长汇总,cid相同的为同一个模块
+func StatisticsLearnedModules(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	db := c.Param("scope")
+	if len(db) == 0 {
+		return nil, errors.New("scope不能为空")
+	}
+	// 如果没有uid参数,则查询当前用户的
+	uid := c.Query("uid")
+	if len(uid) == 0 {
+		uid = apictx.User.ID
+	}
+
+	colls := apictx.CreateRepoCtx().Client.GetDbCollection(db, repo.CollectionLearnLog)
+	pipeline := mongo.Pipeline{
+		{
+			{"$match", bson.D{
+				{"uid", uid},
+			}},
+		},
+		{
+			{"$group", bson.D{
+				{"_id", "$cid"},
+				{"totalLearnTime", bson.D{{"$sum", "$learnTime"}}},
+			}},
+		},
+	}
+
+	cursor, err := colls.Aggregate(apictx.CreateRepoCtx().Ctx, pipeline)
+	if err != nil {
+		return nil, err
+	}
+
+	var results []bson.M
+	if err = cursor.All(apictx.CreateRepoCtx().Ctx, &results); err != nil {
+		return nil, err
+	}
+
+	return results, nil
+}
+
+// 个人: 统计用户本年每月考核正确和错误数
+func StatisticsExeamTFRate(c *gin.Context, apictx *ApiSession) (interface{}, error) {
+	db := c.Param("scope")
+	if len(db) == 0 {
+		return nil, errors.New("scope不能为空")
+	}
+	// 如果没有uid参数,则查询当前用户的
+	uid := c.Query("uid")
+	if len(uid) == 0 {
+		uid = apictx.User.ID
+	}
+	_year := c.Query("year")
+	year := 2024
+	if len(_year) == 0 {
+		year = time.Now().Year()
+	} else {
+		year_, err := strconv.Atoi(_year)
+		if err != nil {
+			return nil, errors.New("year参数错误")
+		}
+		year = year_
+	}
+
+	colls := apictx.CreateRepoCtx().Client.GetDbCollection(db, repo.CollectionLearnLog)
+	pipeline := mongo.Pipeline{
+		{
+			{"$match", bson.D{
+				{"uid", uid},
+				{"createTime", bson.D{
+					{"$gte", bson.M{
+						"$dateFromParts": bson.M{
+							"year": year, "month": 1, "day": 1,
+							"timezone": "Asia/Shanghai",
+						},
+					}},
+					{"$lt", bson.M{
+						"$dateFromParts": bson.M{
+							"year": year + 1, "month": 1, "day": 1,
+							"timezone": "Asia/Shanghai",
+						},
+					}},
+				}},
+			}},
+		},
+		{{"$group", bson.D{
+			{"_id", bson.D{{"month", bson.D{{"$month", "$createTime"}}}}},
+			{"totalCorrect", bson.D{{"$sum", "$correct"}}},
+			{"totalError", bson.D{{"$sum", "$error"}}},
+		}}},
+		{{"$sort", bson.D{{"_id.month", 1}}}},
+	}
+
+	cursor, err := colls.Aggregate(apictx.CreateRepoCtx().Ctx, pipeline)
+	if err != nil {
+		return nil, err
+	}
+
+	var results []bson.M
+	if err = cursor.All(apictx.CreateRepoCtx().Ctx, &results); err != nil {
+		return nil, err
+	}
+
+	return results, nil
+
+}

+ 1 - 0
src/api/user.go

@@ -92,6 +92,7 @@ func CreateUser(c *gin.Context, apictx *ApiSession) (interface{}, error) {
 	if len(user.Roles) < 1 {
 		user.Roles = []string{"student"}
 	}
+	user.Password = UtilMd5(user.Password)
 	user.CreateTime = time.Now()
 	user.UpdateTime = time.Now()
 	return repo.RepoAddDoc(apictx.CreateRepoCtx(), repo.CollectionUser, &user)

+ 1 - 1
src/db/model/exeamLog.go

@@ -15,7 +15,7 @@ type ExeamLog struct {
 	Error      *int               `bson:"error,omitempty" json:"error"`           // 错误题目数
 	TotalScore *int               `bson:"totalScore,omitempty" json:"totalScore"` // 总分
 	// 成绩
-	Socre        *int      `bson:"socre,omitempty" json:"socre"`               // 分数
+	Score        *int      `bson:"socre,omitempty" json:"socre"`               // 分数
 	CompleteRate *int      `bson:"completeRate,omitempty" json:"completeRate"` // 完成进度
 	CreateTime   time.Time `bson:"createTime,omitempty" json:"createTime"`
 	UpdateTime   time.Time `bson:"updateTime,omitempty" json:"updateTime"`