micro-bundle/internal/dao/taskDao.go

826 lines
36 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 dao
import (
"micro-bundle/internal/model"
"micro-bundle/pkg/app"
commonErr "micro-bundle/pkg/err"
"time"
"github.com/google/uuid"
"gorm.io/gorm"
)
// TaskQueryRequest 查询待指派任务记录请求参数
type TaskQueryRequest struct {
Keyword string `json:"keyword"` // 艺人姓名、编号、手机号搜索关键词
Page int `json:"page"` // 页码
PageSize int `json:"pageSize"` // 每页数量
SortBy string `json:"sortBy"` // 排序字段
SortType string `json:"sortType"` // 排序类型 asc/desc
}
// TaskAssignRequest 指派任务请求参数
type TaskAssignRequest struct {
SubNum string `json:"subNum"` // 艺人编号
TelNum string `json:"telNum"` // 艺人手机号
ArtistName string `json:"artistName"` // 艺人姓名
TaskAssignee string `json:"taskAssignee"` // 任务指派人
TaskAssigneeNum string `json:"taskAssigneeNum"` // 任务指派人账号
Operator string `json:"operator"` // 操作人
OperatorNum string `json:"operatorNum"` // 操作人账号
AssignVideoCount int `json:"assignVideoCount"` // 指派视频数
AssignPostCount int `json:"assignPostCount"` // 指派图文数
AssignDataCount int `json:"assignDataCount"` // 指派数据数
}
// UpdatePendingCountRequest 修改待发数量请求参数
type UpdatePendingCountRequest struct {
SubNum string `json:"subNum"` // 艺人编号
TelNum string `json:"telNum"` // 艺人手机号
ArtistName string `json:"artistName"` // 艺人姓名
PendingVideoCount int `json:"pendingVideoCount"` // 待发视频数量
PendingPostCount int `json:"pendingPostCount"` // 待发图文数量
PendingDataCount int `json:"pendingDataCount"` // 待发数据数量
Operator string `json:"operator"` // 操作人
OperatorNum string `json:"operatorNum"` // 操作人账号
}
// EmployeeTaskQueryRequest 员工任务查询请求参数
type EmployeeTaskQueryRequest struct {
TaskAssigneeNum string `json:"taskAssigneeNum"` // 被指派人账号
Keyword string `json:"keyword"` // 艺人姓名、编号、手机号搜索关键词
Operator string `json:"operator"` // 操作人
SortBy string `json:"sortBy"` // 排序字段
StartTime string `json:"startTime"` // 指派开始时间
EndTime string `json:"endTime"` // 指派结束时间
StartCompleteTime string `json:"startCompleteTime"` // 开始完成时间
EndCompleteTime string `json:"endCompleteTime"` // 结束完成时间
Status int `json:"status"` // 反馈完成状态
Page int `json:"page"` // 页码
PageSize int `json:"pageSize"` // 每页数量
}
// CompleteTaskRequest 完成任务请求参数
type CompleteTaskRequest struct {
AssignRecordsUUID string `json:"assignRecordsUUID,omitempty"` // 指派记录UUID可选
EmployeeName string `json:"employeeName"` // 员工姓名(必要)
EmployeeNum string `json:"employeeNum"` // 员工工号(必要)
TaskType string `json:"taskType"` // 任务类型: video/post/data
CompleteCount int `json:"completeCount"` // 完成数量
}
// TaskAssignRecordsQueryRequest 多条件查询操作记录表请求参数
type TaskAssignRecordsQueryRequest struct {
Keyword string `json:"keyword"` // 艺人姓名、编号、手机号搜索关键词
TaskAssignee string `json:"taskAssignee"` // 指派人姓名
Operator string `json:"operator"` // 操作人姓名
OperatorNum string `json:"operatorNum"` // 操作人手机号
StartTime string `json:"startTime"` // 操作开始时间
EndTime string `json:"endTime"` // 操作结束时间
Status int `json:"status"` // 反馈完成状态 0:全部 1:未完成 2:完成
ActualStatus int `json:"actualStatus"` // 实际完成状态 0:全部 1:未完成 2:完成
Page int `json:"page"` // 页码
PageSize int `json:"pageSize"` // 每页数量
}
// 待指派任务 response
type TaskQueryResponse struct {
SubNum string `json:"subNum"`
TelNum string `json:"telNum"`
ArtistName string `json:"artistName"`
PendingVideoCount int `gorm:"column:pending_video_count;comment:待发视频数量" json:"pendingVideoCount"`
PendingPostCount int `gorm:"column:pending_post_count;comment:待发图文数量" json:"pendingPostCount"`
PendingDataCount int `gorm:"column:pending_data_count;comment:待发数据数量" json:"pendingDataCount"`
ProgressTaskCount int `gorm:"column:progress_task_count;comment:进行中的任务数量" json:"progressTaskCount"`
CompleteTaskCount int `gorm:"column:complete_task_count;comment:已完成任务数量" json:"completeTaskCount"`
LastTaskAssignee string `gorm:"column:last_task_assignee;comment:最后一次的任务指派人" json:"lastTaskAssignee"`
TaskAssigneeNum string `gorm:"column:task_assignee_num;comment:最后一次指派人账号" json:"taskAssigneeNum"`
}
// 任务记录表返回结构体
type TaskAssignRecordsResponse struct {
AssignRecordsUUID string `gorm:"column:assign_records_uuid;comment:指派记录UUID" json:"assignRecordsUUID"`
SubNum string `gorm:"column:sub_num;comment:艺人编号" json:"subNum"`
TelNum string `gorm:"column:tel_num;comment:艺人手机号" json:"telNum"`
ArtistName string `gorm:"column:artist_name;comment:艺人名称" json:"artistName"`
Status int `gorm:"column:status;comment:反馈完成状态 1:未完成 2:完成" json:"status"`
ActualStatus int `gorm:"column:actual_status;comment:实际完成状态 1:未完成 2:完成" json:"actualStatus"`
CompleteTime *time.Time `gorm:"column:complete_time;comment:反馈完成时间" json:"completeTime"`
OperatorType int `gorm:"column:operator_type;comment:操作类型 1:修改待发数量 2:指派" json:"operatorType"`
Operator string `gorm:"column:operator;comment:操作人" json:"operator"`
OperatorNum string `gorm:"column:operator_num;comment:操作人账号" json:"operatorNum"`
OperatorTime time.Time `gorm:"column:operator_time;comment:操作时间" json:"operatorTime"`
TaskAssignee string `gorm:"column:task_assignee;comment:任务指派人" json:"taskAssignee"`
TaskAssigneeNum string `gorm:"column:task_assignee_num;comment:任务指派人账号" json:"taskAssigneeNum"`
PendingVideoCount int `gorm:"column:pending_video_count;comment:待发视频数量" json:"pendingVideoCount"`
PendingPostCount int `gorm:"column:pending_post_count;comment:待发图文数量" json:"pendingPostCount"`
PendingDataCount int `gorm:"column:pending_data_count;comment:待发数据数量" json:"pendingDataCount"`
UpdatedAt time.Time `gorm:"column:updated_at;comment:更新时间" json:"updatedAt"`
}
// 任务指派记录数量结构体
type TaskAssignRecords struct {
TaskAssigneeNum string `json:"taskAssigneeNum"` // 任务指派人工号
ProgressTaskCount int `json:"progressTaskCount"` // 进行中任务数量
CompleteTaskCount int `json:"completeTaskCount"` // 已完成任务数量
}
// ValidArtistInfo 有效艺人信息结构体
type ValidArtistInfo struct {
UserID int `json:"userId"` // 用户ID
CustomerNum string `json:"customerNum"` // 艺人编号
UserName string `json:"userName"` // 艺人姓名
UserPhoneNumber string `json:"userPhoneNumber"` // 艺人手机号
BundleName string `json:"bundleName"` // 套餐名称
ExpirationTime string `json:"expirationTime"` // 过期时间
Status int `json:"status"` // 套餐状态
OrderUUID string `json:"orderUUID"` // 订单UUID
AccountNumber int `json:"accountNumber"` // 账号数量
AccountConsumptionNumber int `json:"accountConsumptionNumber"` // 账号消耗数量
VideoNumber int `json:"videoNumber"` // 视频数量
VideoConsumptionNumber int `json:"videoConsumptionNumber"` // 视频消耗数量
ImageNumber int `json:"imageNumber"` // 图片数量
ImageConsumptionNumber int `json:"imageConsumptionNumber"` // 图片消耗数量
DataAnalysisNumber int `json:"dataAnalysisNumber"` // 数据分析数量
DataAnalysisConsumptionNumber int `json:"dataAnalysisConsumptionNumber"` // 数据分析消耗数量
ExpansionPacksNumber int `json:"expansionPacksNumber"` // 扩展套餐数量
}
// ArtistBundleBalanceRequest 查询艺人套餐剩余数量请求参数
type ArtistBundleBalanceRequest struct {
CustomerNum string `json:"customerNum"` // 艺人编号(推荐使用)
TelNum string `json:"telNum"` // 艺人手机号(备选)
}
// ArtistBundleBalanceResponse 艺人套餐剩余数量响应结构体
type ArtistBundleBalanceResponse struct {
RemainingVideoCount int `json:"remainingVideoCount"` // 剩余视频数量 (video_number - video_consumption_number)
RemainingImageCount int `json:"remainingImageCount"` // 剩余图片数量 (image_number - image_consumption_number)
RemainingDataAnalysisCount int `json:"remainingDataAnalysisCount"` // 剩余数据分析数量 (data_analysis_number - data_analysis_consumption_number)
}
// PendingAndBalanceRequest 查询待发与任务余额请求参数
// 优先使用艺人编号查询,如果为空则使用手机号查询
type PendingAndBalanceRequest struct {
SubNum string `json:"subNum"` // 艺人编号(推荐使用)
TelNum string `json:"telNum"` // 艺人手机号(备选)
}
// PendingAndBalanceResponse 查询待发与任务余额响应结构
// 待发数量为细分待发字段求和;余额为对应总数减已使用数后再按类别(套餐/增值)聚合
type PendingAndBalanceResponse struct {
// 待发(按类别聚合)
PendingBundleVideoCount int `json:"pendingBundleVideoCount"` // 待发套餐视频数量(非限制+限制非过期+限制会过期)
PendingBundleImageCount int `json:"pendingBundleImageCount"` // 待发套餐图文数量(非限制+限制非过期+限制会过期)
PendingBundleDataAnalysisCount int `json:"pendingBundleDataAnalysisCount"` // 待发套餐数据分析数量(非限制+限制非过期+限制会过期)
PendingIncreaseVideoCount int `json:"pendingIncreaseVideoCount"` // 待发增值视频数量(非限制+限制非过期+限制会过期)
PendingIncreaseImageCount int `json:"pendingIncreaseImageCount"` // 待发增值图文数量(非限制+限制非过期+限制会过期)
PendingIncreaseDataAnalysisCount int `json:"pendingIncreaseDataAnalysisCount"` // 待发增值数据分析数量(非限制+限制非过期+限制会过期)
// 任务余额(套餐/增值按类别聚合)
BundleVideoBalance int `json:"bundleVideoBalance"` // 套餐视频余额
BundleImageBalance int `json:"bundleImageBalance"` // 套餐图文余额
BundleDataAnalysisBalance int `json:"bundleDataAnalysisBalance"` // 套餐数据分析余额
IncreaseVideoBalance int `json:"increaseVideoBalance"` // 增值视频余额
IncreaseImageBalance int `json:"increaseImageBalance"` // 增值图文余额
IncreaseDataAnalysisBalance int `json:"increaseDataAnalysisBalance"` // 增值数据分析余额
}
// 新增:仅返回“剩余待发数量”的精简响应(区分套餐/增值共6个字段
type ArtistRemainingPendingResponse struct {
PendingBundleVideoCount int `json:"pendingBundleVideoCount"` // 待发套餐视频数量(非限制+限制非过期+限制会过期)
PendingBundleImageCount int `json:"pendingBundleImageCount"` // 待发套餐图文数量(非限制+限制非过期+限制会过期)
PendingBundleDataAnalysisCount int `json:"pendingBundleDataAnalysisCount"` // 待发套餐数据分析数量(非限制+限制非过期+限制会过期)
PendingIncreaseVideoCount int `json:"pendingIncreaseVideoCount"` // 待发增值视频数量(非限制+限制非过期+限制会过期)
PendingIncreaseImageCount int `json:"pendingIncreaseImageCount"` // 待发增值图文数量(非限制+限制非过期+限制会过期)
PendingIncreaseDataAnalysisCount int `json:"pendingIncreaseDataAnalysisCount"` // 待发增值数据分析数量(非限制+限制非过期+限制会过期)
}
// calculateBundleBalances 计算套餐类别下的三类余额(视频/图文/数据分析)
// 余额=对应类别总数-对应类别已使用数;再将非限制、限制非过期、限制会过期三类相加
func calculateBundleBalances(tb *model.TaskBalance) (video int, image int, data int) {
// 视频套餐余额
video = (tb.TaskBundleVideoNumber - tb.TaskBundleVideoConsumptionNumber) +
(tb.TaskBundleLimitVideoNumber - tb.TaskBundleLimitVideoConsumptionNumber) +
(tb.TaskBundleLimitVideoExpiredNumber - tb.TaskBundleLimitVideoExpiredConsumptionNumber)
// 图文套餐余额
image = (tb.TaskBundleImageNumber - tb.TaskBundleImageConsumptionNumber) +
(tb.TaskBundleLimitImageNumber - tb.TaskBundleLimitImageConsumptionNumber) +
(tb.TaskBundleLimitImageExpiredNumber - tb.TaskBundleLimitImageExpiredConsumptionNumber)
// 数据分析套餐余额
data = (tb.TaskBundleDataAnalysisNumber - tb.TaskBundleDataAnalysisConsumptionNumber) +
(tb.TaskBundleLimitDataAnalysisNumber - tb.TaskBundleLimitDataAnalysisConsumptionNumber) +
(tb.TaskBundleLimitDataAnalysisExpiredNumber - tb.TaskBundleLimitDataAnalysisExpiredConsumptionNumber)
return
}
// calculateIncreaseBalances 计算增值类别下的三类余额(视频/图文/数据分析)
// 余额=对应类别总数-对应类别已使用数;再将非限制、限制非过期、限制会过期三类相加
func calculateIncreaseBalances(tb *model.TaskBalance) (video int, image int, data int) {
// 视频增值余额
video = (tb.TaskIncreaseVideoNumber - tb.TaskIncreaseVideoConsumptionNumber) +
(tb.TaskIncreaseLimitVideoNumber - tb.TaskIncreaseLimitVideoConsumptionNumber) +
(tb.TaskIncreaseLimitVideoExpiredNumber - tb.TaskIncreaseLimitVideoExpiredConsumptionNumber)
// 图文增值余额
image = (tb.TaskIncreaseImageNumber - tb.TaskIncreaseImageConsumptionNumber) +
(tb.TaskIncreaseLimitImageNumber - tb.TaskIncreaseLimitImageConsumptionNumber) +
(tb.TaskIncreaseLimitImageExpiredNumber - tb.TaskIncreaseLimitImageExpiredConsumptionNumber)
// 数据分析增值余额
data = (tb.TaskIncreaseDataAnalysisNumber - tb.TaskIncreaseDataAnalysisConsumptionNumber) +
(tb.TaskIncreaseLimitDataAnalysisNumber - tb.TaskIncreaseLimitDataAnalysisConsumptionNumber) +
(tb.TaskIncreaseLimitDataAnalysisExpiredNumber - tb.TaskIncreaseLimitDataAnalysisExpiredConsumptionNumber)
return
}
// AssignTask 指派某位员工完成某个艺人的任务
func AssignTask(req *TaskAssignRequest, progressTaskCount int, completeTaskCount int) error {
// 开启事务
tx := app.ModuleClients.TaskBenchDB.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// 1. 查询当前艺人的任务记录
var taskManagement model.TaskManagement
err := tx.Where("sub_num = ? AND tel_num = ?", req.SubNum, req.TelNum).First(&taskManagement).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
// 如果不存在,创建新记录
taskManagement = model.TaskManagement{
SubNum: req.SubNum,
TelNum: req.TelNum,
ArtistName: req.ArtistName,
// 其他字段
ProgressCount: 0,
CompleteCount: 0,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
if err = tx.Create(&taskManagement).Error; err != nil {
tx.Rollback()
return commonErr.ReturnError(err, "创建任务记录失败", "创建任务记录失败: ")
}
} else {
tx.Rollback()
return commonErr.ReturnError(err, "查询任务记录失败", "查询任务记录失败: ")
}
}
// 2. 更新任务记录中的待办任务数量
updateData := map[string]interface{}{
"ProgressCount": progressTaskCount,
"CompleteCount": completeTaskCount,
"UpdatedAt": time.Now(),
}
if err = tx.Model(&taskManagement).Updates(updateData).Error; err != nil {
tx.Rollback()
return commonErr.ReturnError(err, "更新任务记录失败", "更新任务记录失败: ")
}
// 5. 创建指派记录
assignRecord := &model.TaskAssignRecords{
AssignRecordsUUID: uuid.New().String(), // 使用Google UUID
SubNum: req.SubNum,
TelNum: req.TelNum,
ArtistName: req.ArtistName,
Status: 1, // 1:未完成
ActualStatus: 1, // 1:未完成
OperatorType: 2, // 2:指派
Operator: req.Operator, // 当前操作人名字
OperatorNum: req.OperatorNum, // 当前操作人账号
OperatorTime: time.Now(),
TaskAssignee: req.TaskAssignee, // 指派员工姓名
TaskAssigneeNum: req.TaskAssigneeNum, // 指派员工账号
PendingVideoCount: req.AssignVideoCount,
PendingPostCount: req.AssignPostCount,
PendingDataCount: req.AssignDataCount,
AssignVideoCount: req.AssignVideoCount,
AssignPostCount: req.AssignPostCount,
AssignDataCount: req.AssignDataCount,
CompleteVideoCount: 0,
CompletePostCount: 0,
CompleteDataCount: 0,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
if err = tx.Create(assignRecord).Error; err != nil {
tx.Rollback()
return commonErr.ReturnError(err, "创建指派记录失败", "创建指派记录失败: ")
}
// 提交事务
if err = tx.Commit().Error; err != nil {
return commonErr.ReturnError(err, "提交事务失败", "提交事务失败: ")
}
return nil
}
// GetRecentAssignRecords 查询最近被指派记录
// 查询操作类型为"指派"的最近n条不同员工的记录
func GetRecentAssignRecords(limit int) ([]*model.TaskAssignRecords, error) {
var records []*model.TaskAssignRecords
// 查询操作类型为指派(2)的记录,按操作时间倒序,去重员工
err := app.ModuleClients.TaskBenchDB.Model(&model.TaskAssignRecords{}).
Where("operator_type = ?", 2). // 2:指派
Group("task_assignee_num"). // 按指派人账号分组去重
Order("operator_time DESC"). // 按操作时间倒序
Limit(limit).
Find(&records).Error
if err != nil {
return nil, commonErr.ReturnError(err, "查询最近指派记录失败", "查询最近指派记录失败: ")
}
return records, nil
}
// GetEmployeeAssignedTasks 根据登录人信息查询被指派给该员工的艺人任务
func GetEmployeeAssignedTasks(req *EmployeeTaskQueryRequest) ([]*model.TaskAssignRecords, int64, error) {
var records []*model.TaskAssignRecords
var total int64
// 构建查询条件
query := app.ModuleClients.TaskBenchDB.Model(&model.TaskAssignRecords{}).
Where("task_assignee_num = ?", req.TaskAssigneeNum)
// 关键词搜索(艺人姓名、编号、手机号)
if req.Keyword != "" {
query = query.Where("sub_num LIKE ? OR tel_num LIKE ?",
"%"+req.Keyword+"%", "%"+req.Keyword+"%")
}
// 被指派人姓名
if req.Operator != "" {
query = query.Where("task_assignee LIKE ?", "%"+req.Operator+"%")
}
// 指派时间区间
if req.StartTime != "" && req.EndTime != "" {
query = query.Where("operator_time BETWEEN ? AND ?", req.StartTime, req.EndTime)
} else if req.StartTime != "" {
query = query.Where("operator_time >= ?", req.StartTime)
} else if req.EndTime != "" {
query = query.Where("operator_time <= ?", req.EndTime)
}
// 完成时间区间
if req.StartCompleteTime != "" && req.EndCompleteTime != "" {
query = query.Where("complete_time BETWEEN ? AND ?", req.StartCompleteTime, req.EndCompleteTime)
} else if req.StartCompleteTime != "" {
query = query.Where("complete_time >= ?", req.StartCompleteTime)
} else if req.EndCompleteTime != "" {
query = query.Where("complete_time <= ?", req.EndCompleteTime)
}
// 反馈完成状态
if req.Status != 0 {
query = query.Where("status = ?", req.Status)
}
// 根据排序字段倒序
if req.SortBy != "" {
query = query.Order(req.SortBy + " DESC")
}
// 计算总数
query.Count(&total)
// 分页
if req.PageSize > 0 && req.Page > 0 {
offset := (req.Page - 1) * req.PageSize
query = query.Limit(req.PageSize).Offset(offset)
}
// 按操作时间倒序
err := query.Order("operator_time DESC").Find(&records).Error
if err != nil {
return nil, 0, commonErr.ReturnError(err, "查询员工指派任务失败", "查询员工指派任务失败: ")
}
return records, total, nil
}
// CompleteTaskManually 员工手动点击完成任务
func CompleteTaskManually(assignRecordsUUID string) error {
now := time.Now()
updateData := map[string]interface{}{
"status": 2, // 2:完成
"complete_time": &now,
"updated_at": now,
}
err := app.ModuleClients.TaskBenchDB.Model(&model.TaskAssignRecords{}).
Where("assign_records_uuid = ?", assignRecordsUUID).
Updates(updateData).Error
if err != nil {
return commonErr.ReturnError(err, "更新任务完成状态失败", "更新任务完成状态失败: ")
}
return nil
}
// UpdateTaskProgress 员工实际完成任务状态更新
// 员工调用视频、图文、数据时,对应的待完成数据减一,已完成数据加一
func UpdateTaskProgress(req *CompleteTaskRequest) error {
// 开启事务
tx := app.ModuleClients.TaskBenchDB.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
// 1. 查询指派记录
var assignRecord model.TaskAssignRecords
var err error
if req.AssignRecordsUUID != "" {
// 如果提供了UUID直接根据UUID查询
err = tx.Where("assign_records_uuid = ?", req.AssignRecordsUUID).First(&assignRecord).Error
if err != nil {
tx.Rollback()
return commonErr.ReturnError(err, "查询指派记录失败", "查询指派记录失败: ")
}
} else {
// 如果没有提供UUID根据员工信息查询最早的未完成任务
if req.EmployeeName == "" || req.EmployeeNum == "" {
tx.Rollback()
return commonErr.ReturnError(nil, "参数错误", "员工姓名和手机号不能为空")
}
err = tx.Where("task_assignee = ? AND task_assignee_num = ? AND actual_status = 1",
req.EmployeeName, req.EmployeeNum).
Order("operator_time ASC").
First(&assignRecord).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
tx.Rollback()
return commonErr.ReturnError(nil, "未找到任务记录", "该员工没有未完成的任务记录")
}
tx.Rollback()
return commonErr.ReturnError(err, "查询指派记录失败", "查询指派记录失败: ")
}
}
// 2. 根据任务类型更新完成数量
updateData := map[string]interface{}{
"updated_at": time.Now(),
}
switch req.TaskType {
case "video":
newCompleteCount := assignRecord.CompleteVideoCount + req.CompleteCount
if newCompleteCount > assignRecord.AssignVideoCount {
tx.Rollback()
return commonErr.ReturnError(nil, "完成数量超出限制", "视频完成数量不能超过指派数量")
}
updateData["complete_video_count"] = newCompleteCount
case "post":
newCompleteCount := assignRecord.CompletePostCount + req.CompleteCount
if newCompleteCount > assignRecord.AssignPostCount {
tx.Rollback()
return commonErr.ReturnError(nil, "完成数量超出限制", "图文完成数量不能超过指派数量")
}
updateData["complete_post_count"] = newCompleteCount
case "data":
newCompleteCount := assignRecord.CompleteDataCount + req.CompleteCount
if newCompleteCount > assignRecord.AssignDataCount {
tx.Rollback()
return commonErr.ReturnError(nil, "完成数量超出限制", "数据完成数量不能超过指派数量")
}
updateData["complete_data_count"] = newCompleteCount
default:
tx.Rollback()
return commonErr.ReturnError(nil, "无效的任务类型", "任务类型必须是video、post或data")
}
// 3. 更新指派记录
if err = tx.Model(&assignRecord).Updates(updateData).Error; err != nil {
tx.Rollback()
return commonErr.ReturnError(err, "更新完成数量失败", "更新完成数量失败: ")
}
// 4. 重新查询更新后的记录,检查是否全部完成
if err = tx.Where("assign_records_uuid = ?", assignRecord.AssignRecordsUUID).First(&assignRecord).Error; err != nil {
tx.Rollback()
return commonErr.ReturnError(err, "查询更新后记录失败", "查询更新后记录失败: ")
}
// 5. 检查是否所有任务都已完成
if assignRecord.CompleteVideoCount == assignRecord.AssignVideoCount &&
assignRecord.CompletePostCount == assignRecord.AssignPostCount &&
assignRecord.CompleteDataCount == assignRecord.AssignDataCount {
// 更新实际完成状态
if err = tx.Model(&assignRecord).Update("actual_status", 2).Error; err != nil {
tx.Rollback()
return commonErr.ReturnError(err, "更新实际完成状态失败", "更新实际完成状态失败: ")
}
}
// 提交事务
if err = tx.Commit().Error; err != nil {
return commonErr.ReturnError(err, "提交事务失败", "提交事务失败: ")
}
return nil
}
// GetTaskManagementBySubNum 根据艺人编号查询任务管理记录
func GetTaskManagementBySubNum(subNum string) (*model.TaskManagement, error) {
var task model.TaskManagement
err := app.ModuleClients.TaskBenchDB.Where("sub_num = ?", subNum).First(&task).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil // 记录不存在
}
return nil, commonErr.ReturnError(err, "查询任务管理记录失败", "查询任务管理记录失败: ")
}
return &task, nil
}
// GetTaskBalanceBySubNum 根据艺人编号查询任务余额记录
func GetTaskBalanceBySubNum(subNum string) (*model.TaskBalance, error) {
var taskBalance model.TaskBalance
now := time.Now()
// 优先查询当前有效期内的记录
err := app.ModuleClients.TaskBenchDB.Where("sub_num = ? AND start_at <= ? AND expired_at >= ?",
subNum, now, now).Order("start_at DESC").First(&taskBalance).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
// 如果没有当前有效的记录,查询最新的记录
err = app.ModuleClients.TaskBenchDB.Where("sub_num = ?", subNum).
Order("start_at DESC").First(&taskBalance).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil // 记录不存在
}
return nil, commonErr.ReturnError(err, "查询任务余额记录失败", "查询任务余额记录失败: ")
}
} else {
return nil, commonErr.ReturnError(err, "查询任务余额记录失败", "查询任务余额记录失败: ")
}
}
return &taskBalance, nil
}
// GetRemainingPendingBySubNum 根据艺人编号查询剩余待发数量(区分套餐/增值共6个字段
func GetRemainingPendingBySubNum(subNum string) (*ArtistRemainingPendingResponse, error) {
if subNum == "" {
return nil, commonErr.ReturnError(nil, "查询参数错误", "艺人编号不能为空")
}
tb, err := GetTaskBalanceBySubNum(subNum)
if err != nil {
return nil, err
}
if tb == nil {
// 没有余额记录,视为 0
return &ArtistRemainingPendingResponse{}, nil
}
// 计算套餐与增值剩余待发数量(非限制 + 限制非过期 + 限制会过期)
bundleVideo, bundleImage, bundleData := calculateBundleBalances(tb)
increaseVideo, increaseImage, increaseData := calculateIncreaseBalances(tb)
return &ArtistRemainingPendingResponse{
PendingBundleVideoCount: bundleVideo,
PendingBundleImageCount: bundleImage,
PendingBundleDataAnalysisCount: bundleData,
PendingIncreaseVideoCount: increaseVideo,
PendingIncreaseImageCount: increaseImage,
PendingIncreaseDataAnalysisCount: increaseData,
}, nil
}
// GetAssignRecordByUUID 根据UUID查询指派记录
func GetAssignRecordByUUID(uuid string) (*model.TaskAssignRecords, error) {
var record model.TaskAssignRecords
err := app.ModuleClients.TaskBenchDB.Where("assign_records_uuid = ?", uuid).First(&record).Error
if err != nil {
if err == gorm.ErrRecordNotFound {
return nil, nil // 记录不存在
}
return nil, commonErr.ReturnError(err, "查询指派记录失败", "查询指派记录失败: ")
}
return &record, nil
}
// GetTaskAssignRecordsList 多条件查询操作记录表
// 支持通过艺人信息、指派人、操作人、操作时间、完成状态等多条件查询TaskAssignRecords表
func GetTaskAssignRecordsList(req *TaskAssignRecordsQueryRequest) ([]*model.TaskAssignRecords, int64, error) {
var records []*model.TaskAssignRecords
var total int64
// 构建查询条件
query := app.ModuleClients.TaskBenchDB.Model(&model.TaskAssignRecords{})
// 关键词搜索(艺人姓名、编号、手机号)
if req.Keyword != "" {
query = query.Where("sub_num LIKE ? OR tel_num LIKE ? OR artist_name LIKE ?",
"%"+req.Keyword+"%", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
}
// 指派人姓名
if req.TaskAssignee != "" {
query = query.Where("task_assignee LIKE ?", "%"+req.TaskAssignee+"%")
}
// 操作人姓名
if req.Operator != "" {
query = query.Where("operator LIKE ?", "%"+req.Operator+"%")
}
// 操作人手机号
if req.OperatorNum != "" {
query = query.Where("operator_num LIKE ?", "%"+req.OperatorNum+"%")
}
// 操作时间区间
if req.StartTime != "" && req.EndTime != "" {
query = query.Where("operator_time BETWEEN ? AND ?", req.StartTime, req.EndTime)
} else if req.StartTime != "" {
query = query.Where("operator_time >= ?", req.StartTime)
} else if req.EndTime != "" {
query = query.Where("operator_time <= ?", req.EndTime)
}
// 反馈完成状态
if req.Status != 0 {
query = query.Where("status = ?", req.Status)
}
// 实际完成状态
if req.ActualStatus != 0 {
query = query.Where("actual_status = ?", req.ActualStatus)
}
// 计算总数
query.Count(&total)
// 分页
if req.PageSize > 0 && req.Page > 0 {
offset := (req.Page - 1) * req.PageSize
query = query.Limit(req.PageSize).Offset(offset)
}
// 按更新时间倒序排序
err := query.Order("updated_at DESC").Find(&records).Error
if err != nil {
return nil, 0, commonErr.ReturnError(err, "查询操作记录失败", "查询操作记录失败: ")
}
return records, total, nil
}
// GetValidArtistList 查询套餐状态为有效中的艺人数据列表
// 根据BundleOrderRecords表查询过期时间大于当前时间且状态为已支付的艺人详细信息
func GetValidArtistList() ([]ValidArtistInfo, error) {
// 构建子查询,获取每个用户的最新订单记录
subQuery := app.ModuleClients.BundleDB.Table("bundle_order_records as bor1").
Select("bor1.*").
Joins(`INNER JOIN (
SELECT customer_id, MAX(created_at) AS max_created_time
FROM bundle_order_records
GROUP BY customer_id
) bor2 ON bor1.customer_id = bor2.customer_id AND bor1.created_at = bor2.max_created_time`)
// 主查询,关联用户表和实名信息表
session := app.ModuleClients.BundleDB.Table("`micro-account`.`user` AS u").
Select(`u.id as user_id, bor.customer_num, rn.name as user_name,
u.tel_num as user_phone_number, bor.bundle_name, bor.expiration_time,
bor.status, bor.uuid as order_uuid,
(bb.bundle_account_number + bb.increase_account_number + bb.manual_account_number) as account_number,
(bb.bundle_account_consumption_number + bb.increase_account_consumption_number + bb.manual_account_consumption_number) as account_consumption_number,
(bb.bundle_video_number + bb.increase_video_number) as video_number,
(bb.bundle_video_consumption_number + bb.increase_video_consumption_number) as video_consumption_number,
(bb.bundle_image_number + bb.increase_image_number + bb.manual_image_number) as image_number,
(bb.bundle_image_consumption_number + bb.increase_image_consumption_number + bb.manual_image_consumption_number) as image_consumption_number,
(bb.bundle_data_analysis_number + bb.increase_data_analysis_number + bb.manual_data_analysis_number) as data_analysis_number,
(bb.bundle_data_analysis_consumption_number + bb.increase_data_analysis_consumption_number + bb.manual_data_analysis_consumption_number) as data_analysis_consumption_number,
bb.expansion_packs_number`).
Joins("LEFT JOIN `micro-account`.real_name rn ON u.real_name_id = rn.id").
Joins("LEFT JOIN (?) as bor ON bor.customer_id = u.id", subQuery).
Joins("LEFT JOIN bundle_balance bb ON u.id = bb.user_id AND bb.order_uuid = bor.uuid").
Where("rn.name IS NOT NULL").
Where("u.deleted_at = 0").
Where("bor.expiration_time > ?", time.Now().Format("2006-01-02 15:04:05")).
Where("bor.status = ?", 2). // 2:已签已支付
Order("bor.expiration_time desc")
var data []ValidArtistInfo
err := session.Find(&data).Error
if err != nil {
return nil, commonErr.ReturnError(err, "查询有效艺人失败", "查询有效艺人失败: ")
}
return data, nil
}
// 根据员工的工号从指派任务记录表中查询这名员工,进行中任务数量和已经完成的任务数量
func GetTaskAssigneeInfo(taskAssigneeNum string) (int, int, error) {
// 如果taskAssigneeNum为空直接返回默认值
if taskAssigneeNum == "" {
return 0, 0, nil
}
var taskAssignRecords TaskAssignRecords
err := app.ModuleClients.TaskBenchDB.Table("task_assign_records").
Select("task_assignee_num, count(*) as progress_task_count, sum(status = 2) as complete_task_count").
Where("task_assignee_num = ?", taskAssigneeNum).
Group("task_assignee_num").
First(&taskAssignRecords).Error
// 如果查询不到记录,返回默认值而不是错误
if err != nil {
if err == gorm.ErrRecordNotFound {
return 0, 0, nil
}
return 0, 0, commonErr.ReturnError(err, "查询任务指派记录失败", "查询任务指派记录失败: ")
}
return taskAssignRecords.ProgressTaskCount, taskAssignRecords.CompleteTaskCount, nil
}
// 更新被指派员工为 taskAssigneeNum 的记录中的ProgressCount + 1 和CompleteCount - 1
func UpdateTaskRecordsByAssigneeNum(taskAssigneeNum string) error {
err := app.ModuleClients.TaskBenchDB.Table("task_management").
Where("task_assignee_num = ?", taskAssigneeNum).
Update("progress_count", gorm.Expr("progress_count + ?", 1)).
Update("complete_count", gorm.Expr("complete_count - ?", 1)).Error
if err != nil {
return commonErr.ReturnError(err, "更新员工任务进度失败", "更新员工任务进度失败: ")
}
return nil
}
// GetPendingTaskList 查询待指派任务记录(按有效艺人过滤)
// 说明:由于待发视频/图文/数据以及进行中/已完成数量来自不同表,这里仅返回基础字段,后续由逻辑层计算并排序
func GetPendingTaskList(req *TaskQueryRequest, validArtist []ValidArtistInfo) ([]*TaskQueryResponse, int64, error) {
// 提取有效艺人的艺人编号sub_num
subNums := make([]string, 0, len(validArtist))
for _, a := range validArtist {
if a.CustomerNum != "" {
subNums = append(subNums, a.CustomerNum)
}
}
if len(subNums) == 0 {
// 没有有效艺人,直接返回空
return []*TaskQueryResponse{}, 0, nil
}
// 构建 TaskManagement 的基础查询
query := app.ModuleClients.TaskBenchDB.Model(&model.TaskManagement{}).
Select("sub_num, tel_num, artist_name, last_task_assignee, task_assignee_num").
Where("sub_num IN ?", subNums)
// 关键词搜索(支持 sub_num / tel_num / artist_name
if req.Keyword != "" {
like := "%" + req.Keyword + "%"
query = query.Where("(sub_num LIKE ? OR tel_num LIKE ? OR artist_name LIKE ?)", like, like, like)
}
// 执行查询(不做排序与分页,交由逻辑层处理)
var tmList []model.TaskManagement
if err := query.Find(&tmList).Error; err != nil {
return nil, 0, commonErr.ReturnError(err, "查询待指派任务记录失败", "查询待指派任务记录失败: ")
}
// 统计总数(分页前)
total := int64(len(tmList))
// 映射为响应结构(基础字段)
resp := make([]*TaskQueryResponse, 0, len(tmList))
for _, t := range tmList {
resp = append(resp, &TaskQueryResponse{
SubNum: t.SubNum,
TelNum: t.TelNum,
ArtistName: t.ArtistName,
LastTaskAssignee: t.LastTaskAssignee,
TaskAssigneeNum: t.TaskAssigneeNum,
})
}
return resp, total, nil
}