137 lines
4.6 KiB
Go
137 lines
4.6 KiB
Go
package dao
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/gogf/gf/v2/database/gdb"
|
|
"github.com/gogf/gf/v2/frame/g"
|
|
"github.com/gogf/gf/v2/os/gtime"
|
|
|
|
"36wisdom/biz/consts"
|
|
"36wisdom/biz/model/entity"
|
|
"36wisdom/common"
|
|
)
|
|
|
|
type userProgressDao struct{ common.BaseDao }
|
|
|
|
var UserProgress = &userProgressDao{BaseDao: common.BaseDao{Table: consts.TableUserProgress}}
|
|
|
|
func (d *userProgressDao) Init(ctx context.Context) error {
|
|
_, err := g.DB().Exec(ctx, `
|
|
CREATE TABLE IF NOT EXISTS user_progress (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
child_id INTEGER NOT NULL,
|
|
level_id INTEGER NOT NULL,
|
|
stars INTEGER NOT NULL DEFAULT 0,
|
|
score INTEGER NOT NULL DEFAULT 0,
|
|
perfect INTEGER NOT NULL DEFAULT 0,
|
|
content_version INTEGER NOT NULL DEFAULT 1,
|
|
completed_at DATETIME,
|
|
UNIQUE(child_id, level_id)
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_progress_level ON user_progress(level_id);`)
|
|
return err
|
|
}
|
|
|
|
// GetByChildLevel 孩子某关进度(不缓存);不存在返回 nil, nil。
|
|
func (d *userProgressDao) GetByChildLevel(ctx context.Context, childId, levelId int64) (*entity.UserProgress, error) {
|
|
return common.GetOne[entity.UserProgress](d.Model().Ctx(ctx).
|
|
Where("child_id", childId).Where("level_id", levelId))
|
|
}
|
|
|
|
// ListByChildLevelIds 孩子多关进度(不缓存)。
|
|
func (d *userProgressDao) ListByChildLevelIds(ctx context.Context, childId int64, levelIds []int64) ([]*entity.UserProgress, error) {
|
|
if len(levelIds) == 0 {
|
|
return []*entity.UserProgress{}, nil
|
|
}
|
|
return common.GetList[entity.UserProgress](d.Model().Ctx(ctx).
|
|
Where("child_id", childId).WhereIn("level_id", levelIds))
|
|
}
|
|
|
|
// ListByChild 孩子全部闯关进度(不缓存),按 level_id 升序。
|
|
func (d *userProgressDao) ListByChild(ctx context.Context, childId int64) ([]*entity.UserProgress, error) {
|
|
return common.GetList[entity.UserProgress](d.Model().Ctx(ctx).
|
|
Where("child_id", childId).Order("level_id ASC"))
|
|
}
|
|
|
|
// CountPerfectByChild 孩子完美通关关卡数。
|
|
func (d *userProgressDao) CountPerfectByChild(ctx context.Context, childId int64) (int, error) {
|
|
return d.Model().Ctx(ctx).Where("child_id", childId).Where("perfect", 1).Count()
|
|
}
|
|
|
|
// CountPerfectByChildIds 批量孩子的完美通关数(按 child_id 分组聚合)。
|
|
func (d *userProgressDao) CountPerfectByChildIds(ctx context.Context, childIds []int64) (map[int64]int, error) {
|
|
m := make(map[int64]int, len(childIds))
|
|
if len(childIds) == 0 {
|
|
return m, nil
|
|
}
|
|
recs, err := d.Model().Ctx(ctx).Fields("child_id", "COUNT(*) AS cnt").
|
|
Where("perfect", 1).WhereIn("child_id", childIds).Group("child_id").All()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
for _, r := range recs {
|
|
m[r["child_id"].Int64()] = r["cnt"].Int()
|
|
}
|
|
return m, nil
|
|
}
|
|
|
|
// ProgressStatsByLevel 关卡参与人数与完美通关数(单表聚合)。
|
|
func (d *userProgressDao) ProgressStatsByLevel(ctx context.Context, levelId int64) (players, perfectCount int, err error) {
|
|
rec, err := d.Model().Ctx(ctx).
|
|
Fields("COUNT(*) AS players, SUM(CASE WHEN perfect = 1 THEN 1 ELSE 0 END) AS perfect_cnt").
|
|
Where("level_id", levelId).One()
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
if rec.IsEmpty() {
|
|
return 0, 0, nil
|
|
}
|
|
return rec["players"].Int(), rec["perfect_cnt"].Int(), nil
|
|
}
|
|
|
|
// GetInTx 事务内读孩子某关进度;不存在返回 nil, nil。
|
|
func (d *userProgressDao) GetInTx(ctx context.Context, tx gdb.TX, childId, levelId int64) (*entity.UserProgress, error) {
|
|
rec, err := tx.Model(consts.TableUserProgress).Ctx(ctx).
|
|
Where("child_id", childId).Where("level_id", levelId).One()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if rec.IsEmpty() {
|
|
return nil, nil
|
|
}
|
|
dst := &entity.UserProgress{}
|
|
if err = rec.Struct(dst); err != nil {
|
|
return nil, err
|
|
}
|
|
return dst, nil
|
|
}
|
|
|
|
// UpsertInTx 事务内合并写入进度:星星取历史最高、完美取并集;不存在则插入。
|
|
func (d *userProgressDao) UpsertInTx(ctx context.Context, tx gdb.TX, childId, levelId int64, stars, perfect, contentVersion int) error {
|
|
rec, err := tx.Model(consts.TableUserProgress).Ctx(ctx).
|
|
Where("child_id", childId).Where("level_id", levelId).One()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if stars < rec["stars"].Int() {
|
|
stars = rec["stars"].Int()
|
|
}
|
|
perfect = rec["perfect"].Int() | perfect
|
|
data := g.Map{
|
|
"stars": stars,
|
|
"perfect": perfect,
|
|
"content_version": contentVersion,
|
|
"completed_at": gtime.Now(),
|
|
}
|
|
if rec.IsEmpty() {
|
|
data["child_id"] = childId
|
|
data["level_id"] = levelId
|
|
_, err = tx.Model(consts.TableUserProgress).Ctx(ctx).Data(data).Insert()
|
|
} else {
|
|
_, err = tx.Model(consts.TableUserProgress).Ctx(ctx).Data(data).
|
|
Where("child_id", childId).Where("level_id", levelId).Update()
|
|
}
|
|
return err
|
|
}
|