Files
pj0235-eai_agentplatform/eai_ap_app/backend-go/internal/api/learning.go
T
eaiadmin 4e8817d768 init: 数字员工平台初始代码
包含前端(Vue3 + VueFlow 画布)、后端(Go)、文档体系。
- 工作台画布:节点拖放、连线模式、右键菜单、AI 助手
- 后端:连接器 API、专员种子数据
- 导航:左侧导航、工坊、市场、控制台
2026-08-18 20:19:58 +08:00

115 lines
3.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package api
import (
"time"
"github.com/gin-gonic/gin"
"eaisalestrain/backend/internal/middleware"
"eaisalestrain/backend/internal/model"
"eaisalestrain/backend/internal/store"
"eaisalestrain/backend/internal/web"
)
var validItemTypes = map[string]bool{"company": true, "product": true, "course": true}
// RecordLearningProgress POST /api/learning/progress —— 员工浏览内容时上报进度
func RecordLearningProgress(c *gin.Context) {
u := middleware.CurrentUser(c)
if u == nil {
web.Fail(c, web.NewAuthError("未登录"))
return
}
var req struct {
ItemType string `json:"item_type"`
ItemID uint `json:"item_id"`
}
if err := c.ShouldBindJSON(&req); err != nil || !validItemTypes[req.ItemType] {
web.Fail(c, web.NewBadRequest("item_type 必填且合法(company/product/course)"))
return
}
if req.ItemType == "company" {
req.ItemID = 0
}
lp := model.LearningProgress{UserID: u.ID, ItemType: req.ItemType, ItemID: req.ItemID}
// 幂等:已存在则仅刷新 updated_at;首次记录才加分(避免重复刷分)
var existing model.LearningProgress
if err := store.DB.Where("user_id = ? AND item_type = ? AND item_id = ?", u.ID, req.ItemType, req.ItemID).
First(&existing).Error; err != nil {
if createErr := store.DB.Create(&lp).Error; createErr != nil {
web.Fail(c, web.NewBadRequest("记录学习进度失败"))
return
}
awardFirstView(u.ID, req.ItemType, req.ItemID)
} else {
store.DB.Model(&existing).Update("updated_at", time.Now())
}
web.OK(c, gin.H{"recorded": true, "item_type": req.ItemType, "item_id": req.ItemID})
}
// MyLearningProgress GET /api/learning/me —— 我的学习进度
func MyLearningProgress(c *gin.Context) {
u := middleware.CurrentUser(c)
if u == nil {
web.Fail(c, web.NewAuthError("未登录"))
return
}
var items []model.LearningProgress
store.DB.Where("user_id = ?", u.ID).Order("updated_at DESC").Find(&items)
web.OK(c, items)
}
// AdminLearningProgress GET /api/system/learning-progress —— 全员学习进度(管理员)
func AdminLearningProgress(c *gin.Context) {
var employees []model.User
store.DB.Where("role = ?", "employee").Order("id ASC").Find(&employees)
var items []model.LearningProgress
store.DB.Order("updated_at DESC").Find(&items)
type agg struct {
CompanyViewed bool `json:"company_viewed"`
ProductCount int `json:"product_count"`
CourseCount int `json:"course_count"`
TotalItems int `json:"total_items"`
LastViewedAt *time.Time `json:"last_viewed_at"`
}
perUser := map[uint]*agg{}
for _, lp := range items {
a := perUser[lp.UserID]
if a == nil {
a = &agg{}
perUser[lp.UserID] = a
}
switch lp.ItemType {
case "company":
a.CompanyViewed = true
case "product":
a.ProductCount++
case "course":
a.CourseCount++
}
a.TotalItems++
if a.LastViewedAt == nil || lp.UpdatedAt.After(*a.LastViewedAt) {
t := lp.UpdatedAt
a.LastViewedAt = &t
}
}
out := make([]gin.H, 0, len(employees))
for _, e := range employees {
a := perUser[e.ID]
if a == nil {
a = &agg{}
}
out = append(out, gin.H{
"user_id": e.ID, "username": e.Username, "full_name": e.FullName,
"department": e.Department, "position": e.Position,
"company_viewed": a.CompanyViewed, "product_count": a.ProductCount,
"course_count": a.CourseCount, "total_items": a.TotalItems,
"last_viewed_at": a.LastViewedAt,
})
}
web.OK(c, gin.H{"users": out})
}