Files
36Wisdom/biz/service/child.go
T
2026-08-14 16:11:10 +08:00

252 lines
6.8 KiB
Go

package service
import (
"context"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gtime"
"36wisdom/biz/consts"
"36wisdom/biz/dao"
"36wisdom/biz/model/dto"
"36wisdom/biz/model/entity"
"36wisdom/common/auth"
)
type child struct{}
var Child = &child{}
// getChildOf 校验孩子归属当前家长并返回档案。
func getChildOf(ctx context.Context, parentUid, childId int64) (*entity.Child, error) {
rec, err := dao.Child.GetByPk(ctx, childId)
if err != nil {
return nil, err
}
if rec == nil || rec.ParentId != parentUid {
return nil, gerror.New("孩子档案不存在")
}
return rec, nil
}
// Create 新建孩子档案。
func (s *child) Create(ctx context.Context, req *dto.ChildCreateReq) (*dto.ChildCreateRes, error) {
id, err := dao.Child.InsertAndReturnId(ctx, g.Map{
"parent_id": auth.GetUid(ctx),
"nickname": req.Nickname,
"age_group": req.AgeGroup,
"status": consts.StatusEnabled,
})
if err != nil {
return nil, err
}
return &dto.ChildCreateRes{ChildId: id}, nil
}
// Update 更新孩子档案:仅本人名下档案可改,非空字段覆盖。
func (s *child) Update(ctx context.Context, req *dto.ChildUpdateReq) (*dto.ChildUpdateRes, error) {
rec, err := dao.Child.GetByPk(ctx, req.ChildId)
if err != nil {
return nil, err
}
if rec == nil || rec.ParentId != auth.GetUid(ctx) {
return nil, gerror.New("孩子档案不存在")
}
data := g.Map{}
if req.Nickname != "" {
data["nickname"] = req.Nickname
}
if req.Avatar != "" {
data["avatar"] = req.Avatar
}
if req.AgeGroup != "" {
data["age_group"] = req.AgeGroup
}
if req.DailyLimitMinutes > 0 {
data["daily_limit_minutes"] = req.DailyLimitMinutes
}
if len(data) == 0 {
return &dto.ChildUpdateRes{}, nil
}
if err = dao.Child.UpdateByPk(ctx, req.ChildId, data); err != nil {
return nil, err
}
return &dto.ChildUpdateRes{}, nil
}
// List 家长名下的孩子列表,perfect 数经 user_progress 批量汇总。
func (s *child) List(ctx context.Context, req *dto.ChildListReq) (*dto.ChildListRes, error) {
parentId := auth.GetUid(ctx)
recs, err := dao.Child.ListByParent(ctx, parentId)
if err != nil {
return nil, err
}
items := make([]dto.ChildListItem, 0, len(recs))
if len(recs) == 0 {
return &dto.ChildListRes{List: items}, nil
}
childIds := make([]int64, 0, len(recs))
for _, r := range recs {
childIds = append(childIds, r.Id)
}
perfectMap, err := dao.UserProgress.CountPerfectByChildIds(ctx, childIds)
if err != nil {
return nil, err
}
for _, r := range recs {
perfect := perfectMap[r.Id]
level, title := LevelOf(perfect)
items = append(items, dto.ChildListItem{
ChildId: r.Id,
Nickname: r.Nickname,
Avatar: r.Avatar,
AgeGroup: r.AgeGroup,
Points: r.Points,
Level: level,
LevelTitle: title,
PerfectCount: perfect,
DailyLimitMinutes: r.DailyLimitMinutes,
})
}
return &dto.ChildListRes{List: items}, nil
}
// ---------- 后台管理 ----------
type adminChild struct{}
var AdminChild = &adminChild{}
// List 孩子列表(parent_id 可选筛),含完美数与成长等级。
func (s *adminChild) List(ctx context.Context, req *dto.AdminChildListReq) (*dto.AdminChildListRes, error) {
recs, err := dao.Child.ListAll(ctx, req.ParentId)
if err != nil {
return nil, err
}
childIds := make([]int64, 0, len(recs))
for _, r := range recs {
childIds = append(childIds, r.Id)
}
perfects, err := perfectCounts(ctx, childIds)
if err != nil {
return nil, err
}
items := make([]*dto.AdminChildItem, 0, len(recs))
for _, r := range recs {
item := childItemOf(r)
item.PerfectCount = perfects[r.Id]
item.Level, item.LevelTitle = LevelOf(item.PerfectCount)
items = append(items, item)
}
return &dto.AdminChildListRes{List: items}, nil
}
// childItemOf 孩子档案 → 管理项(完美数与成长等级由调用方补填)。
func childItemOf(r *entity.Child) *dto.AdminChildItem {
return &dto.AdminChildItem{
Id: r.Id, ParentId: r.ParentId,
Nickname: r.Nickname, Avatar: r.Avatar,
AgeGroup: r.AgeGroup, Points: r.Points,
DailyLimitMinutes: r.DailyLimitMinutes,
Status: r.Status, CreatedAt: timeString(r.CreatedAt),
}
}
// perfectCounts 各孩子完美通关数(复用前台统计)。
func perfectCounts(ctx context.Context, childIds []int64) (map[int64]int, error) {
return dao.UserProgress.CountPerfectByChildIds(ctx, childIds)
}
// childNicknames 批量孩子昵称,返回 id → 昵称。
func childNicknames(ctx context.Context, childIds []int64) (map[int64]string, error) {
m := make(map[int64]string, len(childIds))
childIds = uniqueInt64(childIds)
if len(childIds) == 0 {
return m, nil
}
recs, err := dao.Child.ListByIds(ctx, childIds)
if err != nil {
return nil, err
}
for _, r := range recs {
m[r.Id] = r.Nickname
}
return m, nil
}
// timeString 时间格式化(空值回退空串)。
func timeString(t *gtime.Time) string {
if t == nil {
return ""
}
return t.String()
}
// Detail 孩子详情:档案 + 全部闯关进度 + 汇总统计。
func (s *adminChild) Detail(ctx context.Context, req *dto.AdminChildDetailReq) (*dto.AdminChildDetailRes, error) {
child, err := dao.Child.GetByPk(ctx, req.ChildId)
if err != nil {
return nil, err
}
if child == nil {
return nil, gerror.New("孩子不存在")
}
recs, err := dao.UserProgress.ListByChild(ctx, req.ChildId)
if err != nil {
return nil, err
}
levelIds := make([]int64, 0, len(recs))
for _, p := range recs {
levelIds = append(levelIds, p.LevelId)
}
levels, err := dao.Level.ListByIds(ctx, levelIds)
if err != nil {
return nil, err
}
levelById := make(map[int64]*entity.Level, len(levels))
strategyIds := make([]int64, 0, len(levels))
for _, lv := range levels {
levelById[lv.Id] = lv
strategyIds = append(strategyIds, lv.StrategyId)
}
strategies, err := dao.Strategy.ListByIds(ctx, strategyIds)
if err != nil {
return nil, err
}
strategyNames := make(map[int64]string, len(strategies))
for _, st := range strategies {
strategyNames[st.Id] = st.Name
}
item := childItemOf(child)
perfect, stars := 0, 0
for _, p := range recs {
if p.Perfect == 1 {
perfect++
}
stars += p.Stars
}
item.PerfectCount = perfect
detail := &dto.AdminChildDetailRes{
Child: item, Passed: len(recs), Perfect: perfect, Stars: stars,
Progress: make([]*dto.AdminProgressItem, 0, len(recs)),
}
for _, p := range recs {
title, sid := "", int64(0)
if lv := levelById[p.LevelId]; lv != nil {
title, sid = lv.Title, lv.StrategyId
}
detail.Progress = append(detail.Progress, &dto.AdminProgressItem{
LevelId: p.LevelId, LevelTitle: title,
StrategyId: sid, StrategyName: strategyNames[sid],
Stars: p.Stars, Perfect: p.Perfect == 1,
ContentVersion: p.ContentVersion, CompletedAt: timeString(p.CompletedAt),
})
}
return detail, nil
}