245 lines
6.5 KiB
Go
245 lines
6.5 KiB
Go
package dao
|
||
|
||
import (
|
||
"database/sql"
|
||
"fmt"
|
||
|
||
"dd_fiber_api/internal/camp"
|
||
"dd_fiber_api/pkg/database"
|
||
"dd_fiber_api/pkg/utils"
|
||
|
||
"github.com/didi/gendry/builder"
|
||
)
|
||
|
||
// UserCampDAO 用户加入打卡营 DAO
|
||
type UserCampDAO struct {
|
||
client *database.MySQLClient
|
||
}
|
||
|
||
// NewUserCampDAO 创建用户打卡营DAO实例
|
||
func NewUserCampDAO(client *database.MySQLClient) *UserCampDAO {
|
||
return &UserCampDAO{client: client}
|
||
}
|
||
|
||
// CreateIfNotExists 幂等加入(存在则忽略)
|
||
func (d *UserCampDAO) CreateIfNotExists(id, userID, campID string) error {
|
||
table := "camp_user_camps"
|
||
|
||
// 尝试插入;依赖 uk_user_camp 唯一约束保障幂等
|
||
data := []map[string]any{{
|
||
"id": id,
|
||
"user_id": userID,
|
||
"camp_id": campID,
|
||
"status": "ACTIVE",
|
||
// joined_at 由表默认值 CURRENT_TIMESTAMP 自动填充
|
||
}}
|
||
|
||
cond, vals, err := builder.BuildInsert(table, data)
|
||
if err != nil {
|
||
return fmt.Errorf("构建插入语句失败: %v", err)
|
||
}
|
||
|
||
// ON DUPLICATE KEY UPDATE 保持幂等
|
||
cond += " ON DUPLICATE KEY UPDATE status=VALUES(status)"
|
||
|
||
if _, err := d.client.DB.Exec(cond, vals...); err != nil {
|
||
return fmt.Errorf("加入打卡营失败: %v", err)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// CheckUserCampStatus 检查用户是否加入了指定打卡营
|
||
func (d *UserCampDAO) CheckUserCampStatus(userID, campID string) (bool, sql.NullTime, sql.NullString, error) {
|
||
table := "camp_user_camps"
|
||
|
||
where := map[string]any{
|
||
"user_id": userID,
|
||
"camp_id": campID,
|
||
"status": "ACTIVE",
|
||
}
|
||
|
||
cond, vals, err := builder.BuildSelect(table, where, []string{"status", "joined_at", "current_section_id"})
|
||
if err != nil {
|
||
return false, sql.NullTime{}, sql.NullString{}, fmt.Errorf("构建查询语句失败: %v", err)
|
||
}
|
||
|
||
var status string
|
||
var joinedAt sql.NullTime
|
||
var currentSectionID sql.NullString
|
||
err = d.client.DB.QueryRow(cond, vals...).Scan(&status, &joinedAt, ¤tSectionID)
|
||
if err != nil {
|
||
if err == sql.ErrNoRows {
|
||
return false, sql.NullTime{}, sql.NullString{}, nil // 用户未加入
|
||
}
|
||
return false, sql.NullTime{}, sql.NullString{}, fmt.Errorf("查询用户打卡营状态失败: %v", err)
|
||
}
|
||
|
||
return true, joinedAt, currentSectionID, nil
|
||
}
|
||
|
||
// UpdateCurrentSection 更新用户在当前打卡营中的当前小节
|
||
func (d *UserCampDAO) UpdateCurrentSection(userID, campID, sectionID string) error {
|
||
table := "camp_user_camps"
|
||
|
||
where := map[string]any{
|
||
"user_id": userID,
|
||
"camp_id": campID,
|
||
}
|
||
|
||
data := map[string]any{
|
||
"current_section_id": sectionID,
|
||
}
|
||
|
||
cond, vals, err := builder.BuildUpdate(table, where, data)
|
||
if err != nil {
|
||
return fmt.Errorf("构建更新语句失败: %v", err)
|
||
}
|
||
|
||
_, err = d.client.DB.Exec(cond, vals...)
|
||
if err != nil {
|
||
return fmt.Errorf("更新当前小节失败: %v", err)
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// GetCurrentSection 获取用户在当前打卡营中的当前小节ID
|
||
func (d *UserCampDAO) GetCurrentSection(userID, campID string) (string, error) {
|
||
table := "camp_user_camps"
|
||
|
||
where := map[string]any{
|
||
"user_id": userID,
|
||
"camp_id": campID,
|
||
"status": "ACTIVE",
|
||
}
|
||
|
||
cond, vals, err := builder.BuildSelect(table, where, []string{"current_section_id"})
|
||
if err != nil {
|
||
return "", fmt.Errorf("构建查询语句失败: %v", err)
|
||
}
|
||
|
||
var currentSectionID sql.NullString
|
||
err = d.client.DB.QueryRow(cond, vals...).Scan(¤tSectionID)
|
||
if err != nil {
|
||
if err == sql.ErrNoRows {
|
||
return "", nil // 用户未加入
|
||
}
|
||
return "", fmt.Errorf("查询当前小节失败: %v", err)
|
||
}
|
||
|
||
if !currentSectionID.Valid {
|
||
return "", nil
|
||
}
|
||
|
||
return currentSectionID.String, nil
|
||
}
|
||
|
||
// ListUserJoinedCamps 获取用户已加入的打卡营列表
|
||
func (d *UserCampDAO) ListUserJoinedCamps(userID string, page, pageSize int) ([]*camp.UserJoinedCamp, int, error) {
|
||
table := "camp_user_camps"
|
||
|
||
// 查询总数
|
||
countCond, countVals, err := builder.BuildSelect(table, map[string]any{
|
||
"user_id": userID,
|
||
"status": "ACTIVE",
|
||
}, []string{"COUNT(*) as total"})
|
||
if err != nil {
|
||
return nil, 0, fmt.Errorf("构建计数查询失败: %v", err)
|
||
}
|
||
|
||
var total int
|
||
err = d.client.DB.QueryRow(countCond, countVals...).Scan(&total)
|
||
if err != nil {
|
||
return nil, 0, fmt.Errorf("查询总数失败: %v", err)
|
||
}
|
||
|
||
// 查询列表
|
||
where := map[string]any{
|
||
"user_id": userID,
|
||
"status": "ACTIVE",
|
||
}
|
||
|
||
cond, vals, err := builder.BuildSelect(table, where, []string{"camp_id", "joined_at", "status"})
|
||
if err != nil {
|
||
return nil, 0, fmt.Errorf("构建查询语句失败: %v", err)
|
||
}
|
||
|
||
// 添加分页
|
||
offset := (page - 1) * pageSize
|
||
cond += " ORDER BY joined_at DESC LIMIT ? OFFSET ?"
|
||
vals = append(vals, pageSize, offset)
|
||
|
||
rows, err := d.client.DB.Query(cond, vals...)
|
||
if err != nil {
|
||
return nil, 0, fmt.Errorf("查询用户打卡营列表失败: %v", err)
|
||
}
|
||
defer rows.Close()
|
||
|
||
var camps []*camp.UserJoinedCamp
|
||
for rows.Next() {
|
||
var camp camp.UserJoinedCamp
|
||
var joinedAt sql.NullTime
|
||
err := rows.Scan(&camp.CampID, &joinedAt, &camp.Status)
|
||
if err != nil {
|
||
return nil, 0, fmt.Errorf("扫描行数据失败: %v", err)
|
||
}
|
||
|
||
// 格式化时间
|
||
camp.JoinedAt = utils.FormatNullTimeToStd(joinedAt)
|
||
|
||
camps = append(camps, &camp)
|
||
}
|
||
|
||
return camps, total, nil
|
||
}
|
||
|
||
// ListUserIDsByCamp 分页列出已加入指定打卡营的用户 ID(用于管理端进度矩阵:展示所有开启过该营的用户)
|
||
// userKeyword 可选,对 user_id 做 LIKE 模糊匹配
|
||
func (d *UserCampDAO) ListUserIDsByCamp(campID, userKeyword string, page, pageSize int) ([]string, int, error) {
|
||
table := "camp_user_camps"
|
||
|
||
where := map[string]any{
|
||
"camp_id": campID,
|
||
"status": "ACTIVE",
|
||
}
|
||
if userKeyword != "" {
|
||
where["user_id like"] = "%" + userKeyword + "%"
|
||
}
|
||
|
||
// 总数
|
||
countCond, countVals, err := builder.BuildSelect(table, where, []string{"COUNT(*) as total"})
|
||
if err != nil {
|
||
return nil, 0, fmt.Errorf("构建计数查询失败: %v", err)
|
||
}
|
||
var total int
|
||
err = d.client.DB.QueryRow(countCond, countVals...).Scan(&total)
|
||
if err != nil {
|
||
return nil, 0, fmt.Errorf("查询总数失败: %v", err)
|
||
}
|
||
|
||
cond, vals, err := builder.BuildSelect(table, where, []string{"user_id"})
|
||
if err != nil {
|
||
return nil, 0, fmt.Errorf("构建查询失败: %v", err)
|
||
}
|
||
offset := (page - 1) * pageSize
|
||
cond += " ORDER BY joined_at DESC LIMIT ? OFFSET ?"
|
||
vals = append(vals, pageSize, offset)
|
||
|
||
rows, err := d.client.DB.Query(cond, vals...)
|
||
if err != nil {
|
||
return nil, 0, fmt.Errorf("查询用户列表失败: %v", err)
|
||
}
|
||
defer rows.Close()
|
||
|
||
var userIDs []string
|
||
for rows.Next() {
|
||
var uid string
|
||
if err := rows.Scan(&uid); err != nil {
|
||
continue
|
||
}
|
||
userIDs = append(userIDs, uid)
|
||
}
|
||
return userIDs, total, nil
|
||
}
|
||
|