2001 lines
83 KiB
Go
2001 lines
83 KiB
Go
package dao
|
||
|
||
import (
|
||
"fmt"
|
||
bundleConfig "micro-bundle/config"
|
||
"micro-bundle/internal/dto"
|
||
"micro-bundle/internal/model"
|
||
"micro-bundle/pkg/app"
|
||
commonErr "micro-bundle/pkg/err"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/google/uuid"
|
||
"go.uber.org/zap"
|
||
"gorm.io/gorm"
|
||
"gorm.io/gorm/clause"
|
||
)
|
||
|
||
func GetPendingTaskLayout() (string, error) {
|
||
var rec model.TaskPendingLayout
|
||
if err := app.ModuleClients.TaskBenchDB.Model(&model.TaskPendingLayout{}).Where("id = ?", 1).First(&rec).Error; err != nil {
|
||
return "", commonErr.ReturnError(err, "查询待指派布局失败", "查询待指派布局失败: ")
|
||
}
|
||
return rec.Data, nil
|
||
}
|
||
|
||
func SetPendingTaskLayout(data string) error {
|
||
err := app.ModuleClients.TaskBenchDB.Clauses(clause.OnConflict{
|
||
Columns: []clause.Column{{Name: "id"}},
|
||
DoUpdates: clause.AssignmentColumns([]string{"data"}),
|
||
}).Create(&model.TaskPendingLayout{ID: 1, Data: data}).Error
|
||
if err != nil {
|
||
return commonErr.ReturnError(err, "保存待指派布局失败", "保存待指派布局失败: ")
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func AddHiddenTaskAssignee(taskAssignee string, taskAssigneeNum string) error {
|
||
rec := &model.TaskAssigneeHidden{
|
||
TaskAssignee: taskAssignee,
|
||
TaskAssigneeNum: taskAssigneeNum,
|
||
HiddenAt: time.Now(),
|
||
CreatedAt: time.Now(),
|
||
UpdatedAt: time.Now(),
|
||
}
|
||
if err := app.ModuleClients.TaskBenchDB.Clauses(clause.OnConflict{
|
||
Columns: []clause.Column{{Name: "task_assignee_num"}},
|
||
DoUpdates: clause.Assignments(map[string]interface{}{
|
||
"task_assignee": taskAssignee,
|
||
"hidden_at": time.Now(),
|
||
"updated_at": time.Now(),
|
||
"deleted_at": 0,
|
||
}),
|
||
}).Create(rec).Error; err != nil {
|
||
return commonErr.ReturnError(err, "新增隐藏指派人失败", "新增隐藏指派人失败: ")
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// GetArtistUploadStatsList 查询所有艺人的上传统计与额度信息(通过 BundleBalance 关联 CastWork/CastWorkAnalysis,及 subNum 关联 TaskManagement)
|
||
func GetArtistUploadStatsList(req *dto.TaskQueryRequest) ([]*dto.ArtistUploadStatsItem, int64, error) {
|
||
taskSchema := bundleConfig.Data.TaskBenchDB.DbName
|
||
nowMonth := time.Now().Format("2006-01")
|
||
|
||
cte := `WITH
|
||
-- 1. 获取每个用户最新的订单记录(使用NOT EXISTS避免窗口函数重复物化)
|
||
latest_bor AS (
|
||
SELECT bor.id, bor.uuid, bor.customer_id, bor.customer_num, bor.created_at
|
||
FROM bundle_order_records bor
|
||
WHERE bor.deleted_at IS NULL
|
||
AND bor.customer_id IS NOT NULL
|
||
AND NOT EXISTS (
|
||
SELECT 1 FROM bundle_order_records bor2
|
||
WHERE bor2.customer_id = bor.customer_id
|
||
AND bor2.deleted_at IS NULL
|
||
AND (bor2.created_at > bor.created_at OR (bor2.created_at = bor.created_at AND bor2.id > bor.id))
|
||
)
|
||
),
|
||
|
||
-- 2. 获取每个用户的最新月份
|
||
newest_month AS (
|
||
SELECT user_id, MAX(month) AS month
|
||
FROM bundle_balance
|
||
WHERE deleted_at IS NULL
|
||
GROUP BY user_id
|
||
),
|
||
|
||
-- 3. 活跃窗口:包含所有必要字段,作为核心CTE
|
||
active_windows AS (
|
||
SELECT
|
||
u.id AS user_id,
|
||
u.tel_num AS phone,
|
||
bor.uuid AS order_uuid,
|
||
bor.customer_num,
|
||
bb.start_at,
|
||
bb.expired_at,
|
||
bb.bundle_video_number, bb.bundle_limit_video_number, bb.bundle_limit_video_expired_number,
|
||
bb.increase_video_number, bb.increase_limit_video_number, bb.increase_limit_video_expired_number,
|
||
bb.manual_video_number, bb.manual_video_consumption_number,
|
||
bb.bundle_image_number, bb.bundle_limit_image_number, bb.bundle_limit_image_expired_number,
|
||
bb.increase_image_number, bb.increase_limit_image_number, bb.increase_limit_image_expired_number,
|
||
bb.manual_image_number, bb.manual_image_consumption_number,
|
||
bb.bundle_data_analysis_number, bb.bundle_limit_data_analysis_number, bb.bundle_limit_data_analysis_expired_number,
|
||
bb.increase_data_analysis_number, bb.increase_limit_data_analysis_number, bb.increase_limit_data_analysis_expired_number,
|
||
bb.manual_data_analysis_number, bb.manual_data_analysis_consumption_number,
|
||
rn.name AS user_name
|
||
FROM ` + "`micro-account`.`user`" + ` u
|
||
INNER JOIN ` + "`micro-account`.real_name" + ` rn ON rn.id = u.real_name_id AND rn.name IS NOT NULL AND rn.deleted_at = 0
|
||
INNER JOIN bundle_activate bc ON bc.user_id = u.id AND bc.activate = 2
|
||
INNER JOIN latest_bor bor ON bor.customer_id = u.id AND u.deleted_at = 0
|
||
INNER JOIN newest_month nm ON nm.user_id = u.id
|
||
INNER JOIN bundle_balance bb ON bb.user_id = u.id AND bb.order_uuid = bor.uuid AND bb.month = nm.month AND bb.deleted_at IS NULL
|
||
WHERE u.deleted_at = 0
|
||
AND DATE_ADD(UTC_TIMESTAMP(), INTERVAL 8 HOUR) BETWEEN bb.start_at AND bb.expired_at
|
||
),
|
||
|
||
-- 4. 每个订单的最新月份余额
|
||
latest_per_order AS (
|
||
SELECT bb_inner.user_id, bb_inner.order_uuid, MAX(bb_inner.month) AS max_month
|
||
FROM bundle_balance bb_inner
|
||
INNER JOIN bundle_order_records bor_inner ON bor_inner.uuid = bb_inner.order_uuid
|
||
AND bor_inner.deleted_at IS NULL
|
||
AND bor_inner.status = 2
|
||
WHERE bb_inner.deleted_at IS NULL
|
||
AND bb_inner.month <= ?
|
||
GROUP BY bb_inner.user_id, bb_inner.order_uuid
|
||
),
|
||
|
||
-- 5. 余额汇总
|
||
balance_sum AS (
|
||
SELECT
|
||
bb2.user_id,
|
||
SUM(bb2.bundle_video_number + bb2.monthly_bundle_limit_video_number + bb2.monthly_bundle_limit_expired_video_number
|
||
+ bb2.invalid_bundle_video_number + bb2.bundle_limit_video_consumption_number
|
||
+ bb2.bundle_limit_video_expired_consumption_number - bb2.monthly_bundle_limit_expired_video_consumption_number
|
||
- bb2.monthly_bundle_limit_video_consumption_number) AS video_sum,
|
||
SUM(bb2.increase_video_number + bb2.monthly_increase_limit_video_number + bb2.monthly_increase_limit_expired_video_number
|
||
+ bb2.invalid_increase_video_number + bb2.increase_limit_video_consumption_number
|
||
+ bb2.increase_limit_video_expired_consumption_number - bb2.monthly_increase_limit_expired_video_consumption_number
|
||
- bb2.monthly_increase_limit_video_consumption_number) AS increase_video_sum,
|
||
SUM(bb2.bundle_image_number + bb2.monthly_bundle_limit_image_number + bb2.monthly_bundle_limit_expired_image_number
|
||
+ bb2.invalid_bundle_image_number + bb2.bundle_limit_image_consumption_number
|
||
+ bb2.bundle_limit_image_expired_consumption_number - bb2.monthly_bundle_limit_expired_image_consumption_number
|
||
- bb2.monthly_bundle_limit_image_consumption_number) AS post_sum,
|
||
SUM(bb2.increase_image_number + bb2.monthly_increase_limit_image_number + bb2.monthly_increase_limit_expired_image_number
|
||
+ bb2.invalid_increase_image_number + bb2.increase_limit_image_consumption_number
|
||
+ bb2.increase_limit_image_expired_consumption_number - bb2.monthly_increase_limit_expired_image_consumption_number
|
||
- bb2.monthly_increase_limit_image_consumption_number) AS increase_post_sum,
|
||
SUM(bb2.bundle_data_analysis_number + bb2.monthly_bundle_limit_data_analysis_number
|
||
+ bb2.monthly_bundle_limit_expired_data_analysis_number + bb2.invalid_bundle_data_analysis_number
|
||
+ bb2.bundle_limit_data_analysis_consumption_number + bb2.bundle_limit_data_analysis_expired_consumption_number
|
||
- bb2.monthly_bundle_limit_expired_data_analysis_consumption_number
|
||
- bb2.monthly_bundle_limit_data_analysis_consumption_number) AS data_sum,
|
||
SUM(bb2.increase_data_analysis_number + bb2.monthly_increase_limit_data_analysis_number
|
||
+ bb2.monthly_increase_limit_expired_data_analysis_number + bb2.invalid_increase_data_analysis_number
|
||
+ bb2.increase_limit_data_analysis_consumption_number + bb2.increase_limit_data_analysis_expired_consumption_number
|
||
- bb2.monthly_increase_limit_expired_data_analysis_consumption_number
|
||
- bb2.monthly_increase_limit_data_analysis_consumption_number) AS increase_data_sum
|
||
FROM bundle_balance bb2
|
||
INNER JOIN latest_per_order lpo ON bb2.user_id = lpo.user_id
|
||
AND bb2.order_uuid = lpo.order_uuid
|
||
AND bb2.month = lpo.max_month
|
||
INNER JOIN bundle_order_records bor2 ON bor2.uuid = bb2.order_uuid
|
||
AND bor2.deleted_at IS NULL
|
||
AND bor2.status = 2
|
||
WHERE bb2.deleted_at IS NULL
|
||
AND DATE_ADD(UTC_TIMESTAMP(), INTERVAL 8 HOUR) BETWEEN bb2.start_at AND bb2.expired_at
|
||
GROUP BY bb2.user_id
|
||
),
|
||
|
||
-- 6. 作品统计(只统计 origin_uuid 为空的作品)
|
||
cw_agg AS (
|
||
SELECT
|
||
aw.user_id,
|
||
COUNT(CASE WHEN cw.work_category = 2 AND cw.deleted_at = 0 AND (cw.origin_uuid = '' OR cw.origin_uuid IS NULL) AND cw.submit_time BETWEEN aw.start_at AND aw.expired_at THEN 1 END) AS uploaded_video_count,
|
||
COUNT(CASE WHEN cw.work_category = 1 AND cw.deleted_at = 0 AND (cw.origin_uuid = '' OR cw.origin_uuid IS NULL) AND cw.submit_time BETWEEN aw.start_at AND aw.expired_at THEN 1 END) AS uploaded_post_count,
|
||
COUNT(CASE WHEN cw.work_category = 2 AND cw.cost = 1 AND cw.deleted_at = 0 AND (cw.origin_uuid = '' OR cw.origin_uuid IS NULL) AND cw.submit_time BETWEEN aw.start_at AND aw.expired_at THEN 1 END) AS released_video_consumed,
|
||
COUNT(CASE WHEN cw.work_category = 1 AND cw.cost = 1 AND cw.deleted_at = 0 AND (cw.origin_uuid = '' OR cw.origin_uuid IS NULL) AND cw.submit_time BETWEEN aw.start_at AND aw.expired_at THEN 1 END) AS released_post_consumed
|
||
FROM active_windows aw
|
||
LEFT JOIN cast_work cw ON cw.artist_phone COLLATE utf8mb4_general_ci = aw.phone COLLATE utf8mb4_general_ci
|
||
GROUP BY aw.user_id
|
||
),
|
||
|
||
-- 7. 数据分析作品统计(排除 work_analysis_status = 1 的数据分析)
|
||
cwa_agg AS (
|
||
SELECT
|
||
aw.user_id,
|
||
COUNT(CASE WHEN cwa.deleted_at = 0 AND (cwa.work_analysis_status IS NULL OR cwa.work_analysis_status != 1) AND cwa.submit_time BETWEEN UNIX_TIMESTAMP(aw.start_at) AND UNIX_TIMESTAMP(aw.expired_at) THEN 1 END) AS uploaded_data_count,
|
||
COUNT(CASE WHEN cwa.cost = 1 AND cwa.deleted_at = 0 AND (cwa.work_analysis_status IS NULL OR cwa.work_analysis_status != 1) AND cwa.submit_time BETWEEN UNIX_TIMESTAMP(aw.start_at) AND UNIX_TIMESTAMP(aw.expired_at) THEN 1 END) AS released_data_consumed
|
||
FROM active_windows aw
|
||
LEFT JOIN cast_work_analysis cwa ON cwa.artist_phone COLLATE utf8mb4_general_ci = aw.phone COLLATE utf8mb4_general_ci
|
||
GROUP BY aw.user_id
|
||
),
|
||
|
||
-- 8. 视频脚本统计
|
||
cvs_agg AS (
|
||
SELECT
|
||
aw.user_id,
|
||
COUNT(cvs.artist_uuid) AS uploaded_video_script_count
|
||
FROM active_windows aw
|
||
LEFT JOIN cast_video_script cvs ON CAST(aw.user_id AS CHAR) COLLATE utf8mb4_general_ci = cvs.artist_uuid COLLATE utf8mb4_general_ci
|
||
AND cvs.deleted_at = 0
|
||
AND cvs.created_at BETWEEN UNIX_TIMESTAMP(CONVERT_TZ(aw.start_at, '+00:00', '+00:00'))
|
||
AND UNIX_TIMESTAMP(CONVERT_TZ(aw.expired_at, '+00:00', '+00:00'))
|
||
GROUP BY aw.user_id
|
||
),
|
||
|
||
-- 9. 任务统计(直接使用active_windows的customer_num,避免重复JOIN latest_bor)
|
||
tar_agg AS (
|
||
SELECT
|
||
aw.user_id,
|
||
COUNT(CASE WHEN tar.status = 1 AND tar.deleted_at = 0 AND tar.created_at BETWEEN aw.start_at AND aw.expired_at THEN 1 END) AS progress_task_count,
|
||
COUNT(CASE WHEN tar.status = 2 AND tar.deleted_at = 0 AND tar.created_at BETWEEN aw.start_at AND aw.expired_at THEN 1 END) AS complete_task_count
|
||
FROM active_windows aw
|
||
LEFT JOIN ` + "`" + taskSchema + "`" + `.task_assign_records tar ON tar.sub_num COLLATE utf8mb4_general_ci = aw.customer_num COLLATE utf8mb4_general_ci
|
||
GROUP BY aw.user_id
|
||
),
|
||
|
||
-- 10. 已指派且未完成的数量统计(actual_status=1未完成或actual_status=3已中止,排除actual_status=2实际已完成的)
|
||
assigned_pending_agg AS (
|
||
SELECT
|
||
aw.user_id,
|
||
COALESCE(SUM(
|
||
CASE
|
||
WHEN tar.actual_status = 1 THEN tar.pending_video_script_count
|
||
ELSE 0
|
||
END
|
||
), 0) AS assigned_video_script_count,
|
||
COALESCE(SUM(
|
||
CASE
|
||
WHEN tar.actual_status = 1 THEN tar.pending_video_count
|
||
ELSE 0
|
||
END
|
||
), 0) AS assigned_video_count,
|
||
COALESCE(SUM(
|
||
CASE
|
||
WHEN tar.actual_status = 1 THEN tar.pending_post_count
|
||
ELSE 0
|
||
END
|
||
), 0) AS assigned_post_count,
|
||
COALESCE(SUM(
|
||
CASE
|
||
WHEN tar.actual_status = 1 THEN tar.pending_data_count
|
||
ELSE 0
|
||
END
|
||
), 0) AS assigned_data_count
|
||
FROM active_windows aw
|
||
LEFT JOIN ` + "`" + taskSchema + "`" + `.task_assign_records tar ON tar.sub_num COLLATE utf8mb4_general_ci = aw.customer_num COLLATE utf8mb4_general_ci
|
||
AND tar.actual_status IN (1, 3)
|
||
AND tar.deleted_at = 0
|
||
AND tar.created_at BETWEEN aw.start_at AND aw.expired_at
|
||
GROUP BY aw.user_id
|
||
),
|
||
|
||
-- 11. 任务管理信息
|
||
task_mgmt AS (
|
||
SELECT t.user_id, t.last_task_assignee, t.task_assignee_num
|
||
FROM ` + "`" + taskSchema + "`" + `.task_management t
|
||
INNER JOIN (
|
||
SELECT user_id, MAX(updated_at) AS max_updated_at
|
||
FROM ` + "`" + taskSchema + "`" + `.task_management
|
||
WHERE deleted_at = 0
|
||
GROUP BY user_id
|
||
) x ON x.user_id = t.user_id AND x.max_updated_at = t.updated_at
|
||
WHERE t.deleted_at = 0
|
||
)`
|
||
|
||
fromClause := `FROM active_windows aw
|
||
LEFT JOIN balance_sum bs ON bs.user_id = aw.user_id
|
||
LEFT JOIN task_mgmt tm ON tm.user_id = aw.user_id
|
||
LEFT JOIN cw_agg cw ON cw.user_id = aw.user_id
|
||
LEFT JOIN cwa_agg cwa ON cwa.user_id = aw.user_id
|
||
LEFT JOIN cvs_agg cvs ON cvs.user_id = aw.user_id
|
||
LEFT JOIN tar_agg ta ON ta.user_id = aw.user_id
|
||
LEFT JOIN assigned_pending_agg apa ON apa.user_id = aw.user_id`
|
||
|
||
whereParts := make([]string, 0, 4)
|
||
args := make([]interface{}, 0, 8)
|
||
args = append(args, nowMonth)
|
||
if req != nil && req.Keyword != "" {
|
||
like := "%" + req.Keyword + "%"
|
||
whereParts = append(whereParts, "(aw.customer_num LIKE ? OR aw.phone LIKE ? OR aw.user_name LIKE ?)")
|
||
args = append(args, like, like, like)
|
||
}
|
||
if req != nil && req.LastTaskAssignee != "" {
|
||
like := "%" + req.LastTaskAssignee + "%"
|
||
whereParts = append(whereParts, "tm.last_task_assignee LIKE ?")
|
||
args = append(args, like)
|
||
}
|
||
if req != nil && len(req.SubNums) > 0 {
|
||
whereParts = append(whereParts, "aw.customer_num IN ?")
|
||
args = append(args, req.SubNums)
|
||
}
|
||
whereClause := ""
|
||
if len(whereParts) > 0 {
|
||
whereClause = " WHERE " + strings.Join(whereParts, " AND ")
|
||
}
|
||
|
||
countSQL := cte + " SELECT COUNT(DISTINCT aw.customer_num) " + fromClause + whereClause
|
||
var total int64
|
||
if err := app.ModuleClients.BundleDB.Raw(countSQL, args...).Scan(&total).Error; err != nil {
|
||
return nil, 0, commonErr.ReturnError(err, "查询总数失败", "查询艺人上传统计总数失败: ")
|
||
}
|
||
|
||
orderClause := "aw.start_at DESC"
|
||
if req != nil && req.SortBy != "" {
|
||
sortType := req.SortType
|
||
if sortType != "asc" && sortType != "desc" && sortType != "ASC" && sortType != "DESC" {
|
||
sortType = "DESC"
|
||
}
|
||
allowed := map[string]bool{
|
||
"user_name": true,
|
||
"user_phone_number": true,
|
||
"customer_num": true,
|
||
"uploaded_video_count": true,
|
||
"uploaded_video_script_count": true,
|
||
"uploaded_post_count": true,
|
||
"uploaded_data_count": true,
|
||
"bundle_video_total": true,
|
||
"increase_video_total": true,
|
||
"released_video_total": true,
|
||
"pending_video_count": true,
|
||
"pending_video_script_count": true,
|
||
"bundle_post_total": true,
|
||
"increase_post_total": true,
|
||
"released_post_total": true,
|
||
"pending_post_count": true,
|
||
"bundle_data_total": true,
|
||
"increase_data_total": true,
|
||
"released_data_total": true,
|
||
"pending_data_count": true,
|
||
"last_task_assignee": true,
|
||
"task_assignee_num": true,
|
||
"progress_task_count": true,
|
||
"complete_task_count": true,
|
||
"allow_video_script_count": true,
|
||
"allow_video_count": true,
|
||
"allow_post_count": true,
|
||
"allow_data_count": true,
|
||
}
|
||
if allowed[req.SortBy] {
|
||
orderClause = fmt.Sprintf("%s %s", req.SortBy, sortType)
|
||
}
|
||
}
|
||
|
||
selectSQL := cte + ` SELECT
|
||
aw.user_name,
|
||
aw.phone AS user_phone_number,
|
||
aw.customer_num,
|
||
aw.start_at,
|
||
aw.expired_at,
|
||
COALESCE(cw.uploaded_video_count, 0) AS uploaded_video_count,
|
||
COALESCE(cvs.uploaded_video_script_count, 0) AS uploaded_video_script_count,
|
||
COALESCE(cw.uploaded_post_count, 0) AS uploaded_post_count,
|
||
COALESCE(cwa.uploaded_data_count, 0) AS uploaded_data_count,
|
||
(aw.bundle_video_number + aw.bundle_limit_video_number + aw.bundle_limit_video_expired_number) AS bundle_video_total,
|
||
(aw.increase_video_number + aw.increase_limit_video_number + aw.increase_limit_video_expired_number + aw.manual_video_number) AS increase_video_total,
|
||
GREATEST(COALESCE(bs.video_sum, 0) + COALESCE(bs.increase_video_sum, 0) + aw.manual_video_number - aw.manual_video_consumption_number, 0) AS released_video_total,
|
||
GREATEST(COALESCE(bs.video_sum, 0) + COALESCE(bs.increase_video_sum, 0) + aw.manual_video_number - aw.manual_video_consumption_number, 0) - COALESCE(cw.uploaded_video_count, 0) AS pending_video_count,
|
||
GREATEST(COALESCE(bs.video_sum, 0) + COALESCE(bs.increase_video_sum, 0) + aw.manual_video_number - aw.manual_video_consumption_number, 0) - COALESCE(cvs.uploaded_video_script_count, 0) AS pending_video_script_count,
|
||
(aw.bundle_image_number + aw.bundle_limit_image_number + aw.bundle_limit_image_expired_number) AS bundle_post_total,
|
||
(aw.increase_image_number + aw.increase_limit_image_number + aw.increase_limit_image_expired_number + aw.manual_image_number) AS increase_post_total,
|
||
GREATEST(COALESCE(bs.post_sum, 0) + COALESCE(bs.increase_post_sum, 0) + aw.manual_image_number - aw.manual_image_consumption_number, 0) AS released_post_total,
|
||
GREATEST(COALESCE(bs.post_sum, 0) + COALESCE(bs.increase_post_sum, 0) + aw.manual_image_number - aw.manual_image_consumption_number, 0) - COALESCE(cw.uploaded_post_count, 0) AS pending_post_count,
|
||
(aw.bundle_data_analysis_number + aw.bundle_limit_data_analysis_number + aw.bundle_limit_data_analysis_expired_number) AS bundle_data_total,
|
||
(aw.increase_data_analysis_number + aw.increase_limit_data_analysis_number + aw.increase_limit_data_analysis_expired_number + aw.manual_data_analysis_number) AS increase_data_total,
|
||
GREATEST(COALESCE(bs.data_sum, 0) + COALESCE(bs.increase_data_sum, 0) + aw.manual_data_analysis_number - aw.manual_data_analysis_consumption_number, 0) AS released_data_total,
|
||
GREATEST(COALESCE(bs.data_sum, 0) + COALESCE(bs.increase_data_sum, 0) + aw.manual_data_analysis_number - aw.manual_data_analysis_consumption_number, 0) - COALESCE(cwa.uploaded_data_count, 0) AS pending_data_count,
|
||
tm.last_task_assignee,
|
||
tm.task_assignee_num,
|
||
COALESCE(ta.progress_task_count, 0) AS progress_task_count,
|
||
COALESCE(ta.complete_task_count, 0) AS complete_task_count,
|
||
(GREATEST(COALESCE(bs.video_sum, 0) + COALESCE(bs.increase_video_sum, 0) + aw.manual_video_number - aw.manual_video_consumption_number, 0) - COALESCE(cvs.uploaded_video_script_count, 0)) - COALESCE(apa.assigned_video_script_count, 0) AS allow_video_script_count,
|
||
(GREATEST(COALESCE(bs.video_sum, 0) + COALESCE(bs.increase_video_sum, 0) + aw.manual_video_number - aw.manual_video_consumption_number, 0) - COALESCE(cw.uploaded_video_count, 0)) - COALESCE(apa.assigned_video_count, 0) AS allow_video_count,
|
||
(GREATEST(COALESCE(bs.post_sum, 0) + COALESCE(bs.increase_post_sum, 0) + aw.manual_image_number - aw.manual_image_consumption_number, 0) - COALESCE(cw.uploaded_post_count, 0)) - COALESCE(apa.assigned_post_count, 0) AS allow_post_count,
|
||
(GREATEST(COALESCE(bs.data_sum, 0) + COALESCE(bs.increase_data_sum, 0) + aw.manual_data_analysis_number - aw.manual_data_analysis_consumption_number, 0) - COALESCE(cwa.uploaded_data_count, 0)) - COALESCE(apa.assigned_data_count, 0) AS allow_data_count
|
||
` + fromClause + whereClause + " ORDER BY " + orderClause
|
||
|
||
listArgs := make([]interface{}, 0, len(args)+2)
|
||
listArgs = append(listArgs, args...)
|
||
if req != nil && req.Page > 0 && req.PageSize > 0 {
|
||
offset := (req.Page - 1) * req.PageSize
|
||
selectSQL = selectSQL + " LIMIT ? OFFSET ?"
|
||
listArgs = append(listArgs, req.PageSize, offset)
|
||
}
|
||
|
||
items := make([]dto.ArtistUploadStatsItem, 0)
|
||
if err := app.ModuleClients.BundleDB.Raw(selectSQL, listArgs...).Scan(&items).Error; err != nil {
|
||
return nil, 0, commonErr.ReturnError(err, "查询艺人余额与上传数据失败", "查询艺人余额与上传数据失败: ")
|
||
}
|
||
|
||
resp := make([]*dto.ArtistUploadStatsItem, 0, len(items))
|
||
for i := range items {
|
||
resp = append(resp, &items[i])
|
||
}
|
||
return resp, total, nil
|
||
}
|
||
|
||
// GetPendingAssignBySubNums 查询指定艺人的可指派数量(可上传数 - 已指派且未完成的数量)
|
||
func GetPendingAssignBySubNums(subNums []string, page int, pageSize int) ([]*dto.ArtistPendingAssignItem, int64, error) {
|
||
if len(subNums) == 0 {
|
||
return []*dto.ArtistPendingAssignItem{}, 0, nil
|
||
}
|
||
|
||
taskSchema := bundleConfig.Data.TaskBenchDB.DbName
|
||
nowMonth := time.Now().Format("2006-01")
|
||
|
||
cte := `WITH
|
||
-- 1. 获取每个用户最新的订单记录(使用NOT EXISTS避免窗口函数重复物化)
|
||
latest_bor AS (
|
||
SELECT bor.id, bor.uuid, bor.customer_id, bor.customer_num, bor.created_at
|
||
FROM bundle_order_records bor
|
||
WHERE bor.deleted_at IS NULL
|
||
AND bor.customer_id IS NOT NULL
|
||
AND bor.customer_num IN ?
|
||
AND NOT EXISTS (
|
||
SELECT 1 FROM bundle_order_records bor2
|
||
WHERE bor2.customer_id = bor.customer_id
|
||
AND bor2.deleted_at IS NULL
|
||
AND (bor2.created_at > bor.created_at OR (bor2.created_at = bor.created_at AND bor2.id > bor.id))
|
||
)
|
||
),
|
||
|
||
-- 2. 获取每个用户的最新月份
|
||
newest_month AS (
|
||
SELECT user_id, MAX(month) AS month
|
||
FROM bundle_balance
|
||
WHERE deleted_at IS NULL
|
||
GROUP BY user_id
|
||
),
|
||
|
||
-- 3. 活跃窗口:包含所有必要字段,作为核心CTE
|
||
active_windows AS (
|
||
SELECT
|
||
u.id AS user_id,
|
||
u.tel_num AS phone,
|
||
bor.uuid AS order_uuid,
|
||
bor.customer_num,
|
||
bb.start_at,
|
||
bb.expired_at,
|
||
bb.manual_video_number, bb.manual_video_consumption_number,
|
||
bb.manual_image_number, bb.manual_image_consumption_number,
|
||
bb.manual_data_analysis_number, bb.manual_data_analysis_consumption_number,
|
||
rn.name AS user_name
|
||
FROM ` + "`micro-account`.`user`" + ` u
|
||
INNER JOIN ` + "`micro-account`.real_name" + ` rn ON rn.id = u.real_name_id AND rn.name IS NOT NULL
|
||
INNER JOIN bundle_activate bc ON bc.user_id = u.id AND bc.activate = 2
|
||
INNER JOIN latest_bor bor ON bor.customer_id = u.id
|
||
INNER JOIN newest_month nm ON nm.user_id = u.id
|
||
INNER JOIN bundle_balance bb ON bb.user_id = u.id AND bb.order_uuid = bor.uuid AND bb.month = nm.month AND bb.deleted_at IS NULL
|
||
WHERE u.deleted_at = 0
|
||
AND DATE_ADD(UTC_TIMESTAMP(), INTERVAL 8 HOUR) BETWEEN bb.start_at AND bb.expired_at
|
||
),
|
||
|
||
-- 4. 每个订单的最新月份余额
|
||
latest_per_order AS (
|
||
SELECT bb_inner.user_id, bb_inner.order_uuid, MAX(bb_inner.month) AS max_month
|
||
FROM bundle_balance bb_inner
|
||
INNER JOIN bundle_order_records bor_inner ON bor_inner.uuid = bb_inner.order_uuid
|
||
AND bor_inner.deleted_at IS NULL
|
||
AND bor_inner.status = 2
|
||
WHERE bb_inner.deleted_at IS NULL
|
||
AND bb_inner.month <= ?
|
||
GROUP BY bb_inner.user_id, bb_inner.order_uuid
|
||
),
|
||
|
||
-- 5. 余额汇总
|
||
balance_sum AS (
|
||
SELECT
|
||
bb2.user_id,
|
||
SUM(bb2.bundle_video_number + bb2.monthly_bundle_limit_video_number + bb2.monthly_bundle_limit_expired_video_number
|
||
+ bb2.invalid_bundle_video_number + bb2.bundle_limit_video_consumption_number
|
||
+ bb2.bundle_limit_video_expired_consumption_number - bb2.monthly_bundle_limit_expired_video_consumption_number
|
||
- bb2.monthly_bundle_limit_video_consumption_number) AS video_sum,
|
||
SUM(bb2.increase_video_number + bb2.monthly_increase_limit_video_number + bb2.monthly_increase_limit_expired_video_number
|
||
+ bb2.invalid_increase_video_number + bb2.increase_limit_video_consumption_number
|
||
+ bb2.increase_limit_video_expired_consumption_number - bb2.monthly_increase_limit_expired_video_consumption_number
|
||
- bb2.monthly_increase_limit_video_consumption_number) AS increase_video_sum,
|
||
SUM(bb2.bundle_image_number + bb2.monthly_bundle_limit_image_number + bb2.monthly_bundle_limit_expired_image_number
|
||
+ bb2.invalid_bundle_image_number + bb2.bundle_limit_image_consumption_number
|
||
+ bb2.bundle_limit_image_expired_consumption_number - bb2.monthly_bundle_limit_expired_image_consumption_number
|
||
- bb2.monthly_bundle_limit_image_consumption_number) AS post_sum,
|
||
SUM(bb2.increase_image_number + bb2.monthly_increase_limit_image_number + bb2.monthly_increase_limit_expired_image_number
|
||
+ bb2.invalid_increase_image_number + bb2.increase_limit_image_consumption_number
|
||
+ bb2.increase_limit_image_expired_consumption_number - bb2.monthly_increase_limit_expired_image_consumption_number
|
||
- bb2.monthly_increase_limit_image_consumption_number) AS increase_post_sum,
|
||
SUM(bb2.bundle_data_analysis_number + bb2.monthly_bundle_limit_data_analysis_number
|
||
+ bb2.monthly_bundle_limit_expired_data_analysis_number + bb2.invalid_bundle_data_analysis_number
|
||
+ bb2.bundle_limit_data_analysis_consumption_number + bb2.bundle_limit_data_analysis_expired_consumption_number
|
||
- bb2.monthly_bundle_limit_expired_data_analysis_consumption_number
|
||
- bb2.monthly_bundle_limit_data_analysis_consumption_number) AS data_sum,
|
||
SUM(bb2.increase_data_analysis_number + bb2.monthly_increase_limit_data_analysis_number
|
||
+ bb2.monthly_increase_limit_expired_data_analysis_number + bb2.invalid_increase_data_analysis_number
|
||
+ bb2.increase_limit_data_analysis_consumption_number + bb2.increase_limit_data_analysis_expired_consumption_number
|
||
- bb2.monthly_increase_limit_expired_data_analysis_consumption_number
|
||
- bb2.monthly_increase_limit_data_analysis_consumption_number) AS increase_data_sum
|
||
FROM bundle_balance bb2
|
||
INNER JOIN latest_per_order lpo ON bb2.user_id = lpo.user_id
|
||
AND bb2.order_uuid = lpo.order_uuid
|
||
AND bb2.month = lpo.max_month
|
||
INNER JOIN bundle_order_records bor2 ON bor2.uuid = bb2.order_uuid
|
||
AND bor2.deleted_at IS NULL
|
||
AND bor2.status = 2
|
||
WHERE bb2.deleted_at IS NULL
|
||
AND DATE_ADD(UTC_TIMESTAMP(), INTERVAL 8 HOUR) BETWEEN bb2.start_at AND bb2.expired_at
|
||
GROUP BY bb2.user_id
|
||
),
|
||
|
||
-- 6. 作品统计
|
||
cw_agg AS (
|
||
SELECT
|
||
aw.user_id,
|
||
COUNT(CASE WHEN cw.work_category = 2 AND cw.deleted_at = 0 AND cw.submit_time BETWEEN aw.start_at AND aw.expired_at THEN 1 END) AS uploaded_video_count,
|
||
COUNT(CASE WHEN cw.work_category = 1 AND cw.deleted_at = 0 AND cw.submit_time BETWEEN aw.start_at AND aw.expired_at THEN 1 END) AS uploaded_post_count
|
||
FROM active_windows aw
|
||
LEFT JOIN cast_work cw ON cw.artist_phone COLLATE utf8mb4_general_ci = aw.phone COLLATE utf8mb4_general_ci
|
||
GROUP BY aw.user_id
|
||
),
|
||
|
||
-- 7. 数据分析作品统计
|
||
cwa_agg AS (
|
||
SELECT
|
||
aw.user_id,
|
||
COUNT(CASE WHEN cwa.deleted_at = 0 AND cwa.submit_time BETWEEN UNIX_TIMESTAMP(aw.start_at) AND UNIX_TIMESTAMP(aw.expired_at) THEN 1 END) AS uploaded_data_count
|
||
FROM active_windows aw
|
||
LEFT JOIN cast_work_analysis cwa ON cwa.artist_phone COLLATE utf8mb4_general_ci = aw.phone COLLATE utf8mb4_general_ci
|
||
GROUP BY aw.user_id
|
||
),
|
||
|
||
-- 8. 视频脚本统计
|
||
cvs_agg AS (
|
||
SELECT
|
||
aw.user_id,
|
||
COUNT(cvs.artist_uuid) AS uploaded_video_script_count
|
||
FROM active_windows aw
|
||
LEFT JOIN cast_video_script cvs ON CAST(aw.user_id AS CHAR) COLLATE utf8mb4_general_ci = cvs.artist_uuid COLLATE utf8mb4_general_ci
|
||
AND cvs.deleted_at = 0
|
||
AND cvs.created_at BETWEEN UNIX_TIMESTAMP(CONVERT_TZ(aw.start_at, '+00:00', '+00:00'))
|
||
AND UNIX_TIMESTAMP(CONVERT_TZ(aw.expired_at, '+00:00', '+00:00'))
|
||
GROUP BY aw.user_id
|
||
),
|
||
|
||
-- 9. 已指派且未完成的数量统计(actual_status=1未完成或actual_status=3已中止,排除actual_status=2实际已完成的)
|
||
assigned_pending_agg AS (
|
||
SELECT
|
||
aw.user_id,
|
||
COALESCE(SUM(
|
||
CASE
|
||
WHEN tar.actual_status = 1 THEN tar.pending_video_script_count
|
||
ELSE 0
|
||
END
|
||
), 0) AS assigned_video_script_count,
|
||
COALESCE(SUM(
|
||
CASE
|
||
WHEN tar.actual_status = 1 THEN tar.pending_video_count
|
||
ELSE 0
|
||
END
|
||
), 0) AS assigned_video_count,
|
||
COALESCE(SUM(
|
||
CASE
|
||
WHEN tar.actual_status = 1 THEN tar.pending_post_count
|
||
ELSE 0
|
||
END
|
||
), 0) AS assigned_post_count,
|
||
COALESCE(SUM(
|
||
CASE
|
||
WHEN tar.actual_status = 1 THEN tar.pending_data_count
|
||
ELSE 0
|
||
END
|
||
), 0) AS assigned_data_count
|
||
FROM active_windows aw
|
||
LEFT JOIN ` + "`" + taskSchema + "`" + `.task_assign_records tar ON tar.sub_num COLLATE utf8mb4_general_ci = aw.customer_num COLLATE utf8mb4_general_ci
|
||
AND tar.actual_status IN (1, 3)
|
||
AND tar.deleted_at = 0
|
||
AND tar.created_at BETWEEN aw.start_at AND aw.expired_at
|
||
GROUP BY aw.user_id
|
||
)`
|
||
|
||
fromClause := `FROM active_windows aw
|
||
LEFT JOIN balance_sum bs ON bs.user_id = aw.user_id
|
||
LEFT JOIN cw_agg cw ON cw.user_id = aw.user_id
|
||
LEFT JOIN cwa_agg cwa ON cwa.user_id = aw.user_id
|
||
LEFT JOIN cvs_agg cvs ON cvs.user_id = aw.user_id
|
||
LEFT JOIN assigned_pending_agg apa ON apa.user_id = aw.user_id`
|
||
|
||
args := make([]interface{}, 0, 4)
|
||
args = append(args, subNums)
|
||
args = append(args, nowMonth)
|
||
|
||
countSQL := cte + " SELECT COUNT(DISTINCT aw.customer_num) " + fromClause
|
||
var total int64
|
||
if err := app.ModuleClients.BundleDB.Raw(countSQL, args...).Scan(&total).Error; err != nil {
|
||
return nil, 0, commonErr.ReturnError(err, "查询可指派数量总数失败", "查询可指派数量总数失败: ")
|
||
}
|
||
|
||
selectSQL := cte + ` SELECT
|
||
aw.customer_num,
|
||
aw.phone AS tel_num,
|
||
aw.user_name,
|
||
(GREATEST(COALESCE(bs.video_sum, 0) + COALESCE(bs.increase_video_sum, 0) + aw.manual_video_number - aw.manual_video_consumption_number, 0) - COALESCE(cvs.uploaded_video_script_count, 0)) - COALESCE(apa.assigned_video_script_count, 0) AS allow_video_script_count,
|
||
(GREATEST(COALESCE(bs.video_sum, 0) + COALESCE(bs.increase_video_sum, 0) + aw.manual_video_number - aw.manual_video_consumption_number, 0) - COALESCE(cw.uploaded_video_count, 0)) - COALESCE(apa.assigned_video_count, 0) AS allow_video_count,
|
||
(GREATEST(COALESCE(bs.post_sum, 0) + COALESCE(bs.increase_post_sum, 0) + aw.manual_image_number - aw.manual_image_consumption_number, 0) - COALESCE(cw.uploaded_post_count, 0)) - COALESCE(apa.assigned_post_count, 0) AS allow_post_count,
|
||
(GREATEST(COALESCE(bs.data_sum, 0) + COALESCE(bs.increase_data_sum, 0) + aw.manual_data_analysis_number - aw.manual_data_analysis_consumption_number, 0) - COALESCE(cwa.uploaded_data_count, 0)) - COALESCE(apa.assigned_data_count, 0) AS allow_data_count
|
||
` + fromClause + " ORDER BY aw.user_id"
|
||
|
||
listArgs := make([]interface{}, 0, len(args)+2)
|
||
listArgs = append(listArgs, args...)
|
||
if page > 0 && pageSize > 0 {
|
||
offset := (page - 1) * pageSize
|
||
selectSQL = selectSQL + " LIMIT ? OFFSET ?"
|
||
listArgs = append(listArgs, pageSize, offset)
|
||
}
|
||
|
||
items := make([]dto.ArtistPendingAssignItem, 0)
|
||
if err := app.ModuleClients.BundleDB.Raw(selectSQL, listArgs...).Scan(&items).Error; err != nil {
|
||
return nil, 0, commonErr.ReturnError(err, "查询可指派数量失败", "查询可指派数量失败: ")
|
||
}
|
||
|
||
resp := make([]*dto.ArtistPendingAssignItem, 0, len(items))
|
||
for i := range items {
|
||
resp = append(resp, &items[i])
|
||
}
|
||
return resp, total, nil
|
||
}
|
||
|
||
// AssignTask 指派某位员工完成某个艺人的任务
|
||
func AssignTask(req *dto.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 {
|
||
var userID int
|
||
if req.SubNum != "" {
|
||
var row struct{ ID int }
|
||
_ = tx.Table("`micro-account`.`user`").Unscoped().Select("id").Where("sub_num COLLATE utf8mb4_general_ci = ? COLLATE utf8mb4_general_ci AND deleted_at = 0", req.SubNum).Limit(1).Scan(&row).Error
|
||
if row.ID > 0 {
|
||
userID = row.ID
|
||
}
|
||
}
|
||
if userID == 0 && req.SubNum != "" {
|
||
// 使用子查询获取每个 customer_id 最新订单记录的 customer_id
|
||
// 逻辑:找到 customer_num 匹配且 created_at 为该 customer_id 最大值的记录
|
||
var o struct{ CustomerID string }
|
||
subQuery := app.ModuleClients.BundleDB.Table("bundle_order_records").
|
||
Select("customer_id, MAX(created_at) AS max_created_time").
|
||
Where("deleted_at IS NULL AND status = 2").
|
||
Group("customer_id")
|
||
|
||
_ = app.ModuleClients.BundleDB.Table("bundle_order_records bor1").
|
||
Select("bor1.customer_id").
|
||
Joins("INNER JOIN (?) bor2 ON bor1.customer_id = bor2.customer_id AND bor1.created_at = bor2.max_created_time", subQuery).
|
||
Where("bor1.deleted_at IS NULL AND bor1.status = 2").
|
||
Where("bor1.customer_num COLLATE utf8mb4_general_ci = ? COLLATE utf8mb4_general_ci", req.SubNum).
|
||
Limit(1).
|
||
Scan(&o).Error
|
||
if o.CustomerID != "" {
|
||
if id64, _ := strconv.ParseInt(o.CustomerID, 10, 64); id64 > 0 {
|
||
userID = int(id64)
|
||
}
|
||
}
|
||
}
|
||
taskManagement = model.TaskManagement{
|
||
UserId: userID,
|
||
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,
|
||
"LastTaskAssignee": req.TaskAssignee,
|
||
"TaskAssigneeNum": req.TaskAssigneeNum,
|
||
"UpdatedAt": time.Now(),
|
||
}
|
||
|
||
if err = tx.Model(&taskManagement).Updates(updateData).Error; err != nil {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(err, "更新任务记录失败", "更新任务记录失败: ")
|
||
}
|
||
|
||
if err = tx.Where("task_assignee_num = ? AND deleted_at = 0", req.TaskAssigneeNum).Delete(&model.TaskAssigneeHidden{}).Error; err != nil {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(err, "移除隐藏指派人失败", "移除隐藏指派人失败: ")
|
||
}
|
||
|
||
// 5. 创建指派记录
|
||
if req.TaskBatch == "" {
|
||
req.TaskBatch = "未填写"
|
||
}
|
||
|
||
assignRecord := &model.TaskAssignRecords{
|
||
AssignRecordsUUID: uuid.New().String(), // 使用Google UUID
|
||
SubNum: req.SubNum,
|
||
TelNum: req.TelNum,
|
||
ArtistName: req.ArtistName,
|
||
TaskBatch: req.TaskBatch,
|
||
Status: 1, // 1:未完成
|
||
ActualStatus: 1, // 1:未完成
|
||
OperatorType: 2, // 2:指派
|
||
Operator: req.Operator, // 当前操作人名字
|
||
OperatorNum: req.OperatorNum, // 当前操作人账号
|
||
OperatorTime: time.Now(),
|
||
TaskAssignee: req.TaskAssignee, // 指派员工姓名
|
||
TaskAssigneeNum: req.TaskAssigneeNum, // 指派员工账号
|
||
PendingVideoScriptCount: req.AssignVideoScriptCount,
|
||
PendingVideoCount: req.AssignVideoCount,
|
||
PendingPostCount: req.AssignPostCount,
|
||
PendingDataCount: req.AssignDataCount,
|
||
AssignVideoScriptCount: req.AssignVideoScriptCount,
|
||
AssignVideoCount: req.AssignVideoCount,
|
||
AssignPostCount: req.AssignPostCount,
|
||
AssignDataCount: req.AssignDataCount,
|
||
CompleteVideoScriptCount: 0,
|
||
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
|
||
}
|
||
|
||
func RevertTaskCompletionByUUIDItem(uuid string) error {
|
||
tx := app.ModuleClients.TaskBenchDB.Begin()
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
tx.Rollback()
|
||
}
|
||
}()
|
||
var item model.TaskAssignUUIDItems
|
||
if err := tx.Where("uuid = ?", uuid).First(&item).Error; err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
tx.Rollback()
|
||
return nil
|
||
}
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(err, "该UUID没有对应的任务uuid记录", "查询任务UUID记录失败: ")
|
||
}
|
||
if err := tx.Where("uuid = ?", uuid).Delete(&model.TaskAssignUUIDItems{}).Error; err != nil {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(err, "软删除任务UUID记录失败", "软删除任务UUID记录失败: ")
|
||
}
|
||
var assignRecord model.TaskAssignRecords
|
||
if err := tx.Where("assign_records_uuid = ?", item.AssignRecordsUUID).First(&assignRecord).Error; err != nil {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(err, "查询指派记录失败", "查询指派记录失败: ")
|
||
}
|
||
// now := time.Now()
|
||
updateData := map[string]interface{}{
|
||
"updated_at": assignRecord.UpdatedAt,
|
||
}
|
||
// 如果记录是 3的话就不改变,直接返回
|
||
if assignRecord.ActualStatus == 3 {
|
||
return nil
|
||
}
|
||
if assignRecord.ActualStatus == 2 {
|
||
updateData["actual_status"] = 1
|
||
}
|
||
switch item.Type {
|
||
case 1:
|
||
updateData["pending_video_count"] = assignRecord.PendingVideoCount + 1
|
||
cv := assignRecord.CompleteVideoCount - 1
|
||
if cv < 0 {
|
||
cv = 0
|
||
}
|
||
updateData["complete_video_count"] = cv
|
||
case 2:
|
||
updateData["pending_post_count"] = assignRecord.PendingPostCount + 1
|
||
cp := assignRecord.CompletePostCount - 1
|
||
if cp < 0 {
|
||
cp = 0
|
||
}
|
||
updateData["complete_post_count"] = cp
|
||
case 3:
|
||
updateData["pending_data_count"] = assignRecord.PendingDataCount + 1
|
||
cd := assignRecord.CompleteDataCount - 1
|
||
if cd < 0 {
|
||
cd = 0
|
||
}
|
||
updateData["complete_data_count"] = cd
|
||
case 4:
|
||
updateData["pending_video_script_count"] = assignRecord.PendingVideoScriptCount + 1
|
||
cs := assignRecord.CompleteVideoScriptCount - 1
|
||
if cs < 0 {
|
||
cs = 0
|
||
}
|
||
updateData["complete_video_script_count"] = cs
|
||
}
|
||
if err := tx.Model(&assignRecord).Updates(updateData).Error; err != nil {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(err, "更新指派记录失败", "更新指派记录失败: ")
|
||
}
|
||
if err := tx.Commit().Error; err != nil {
|
||
return commonErr.ReturnError(err, "提交事务失败", "提交事务失败: ")
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// BatchAssignTasks 批量指派
|
||
func BatchAssignTasks(items []*dto.BatchAssignItem) error {
|
||
if len(items) == 0 {
|
||
return commonErr.ReturnError(nil, "参数错误", "批量指派项不能为空")
|
||
}
|
||
|
||
now := time.Now()
|
||
|
||
subNumSet := make(map[string]struct{})
|
||
subTelKeys := make(map[string]*dto.BatchAssignItem) // key: sub_num|tel_num
|
||
for _, it := range items {
|
||
if it == nil {
|
||
return commonErr.ReturnError(nil, "参数错误", "存在空的指派项")
|
||
}
|
||
// 校验:数量不可为负,且不可全部为0
|
||
if it.AssignVideoCount < 0 || it.AssignPostCount < 0 || it.AssignDataCount < 0 || it.AssignVideoScriptCount < 0 ||
|
||
(it.AssignVideoCount == 0 && it.AssignPostCount == 0 && it.AssignDataCount == 0 && it.AssignVideoScriptCount == 0) {
|
||
errMsg := fmt.Sprintf("\"%s\"老师任务不可全部为0", it.ArtistName)
|
||
return commonErr.ReturnError(nil, errMsg, "批量指派校验失败: ")
|
||
}
|
||
if it.TaskBatch == "" {
|
||
it.TaskBatch = "未填写"
|
||
}
|
||
if it.SubNum != "" {
|
||
subNumSet[it.SubNum] = struct{}{}
|
||
}
|
||
key := it.SubNum + "|" + it.TelNum
|
||
subTelKeys[key] = it
|
||
}
|
||
|
||
// 提取 subNums 列表
|
||
subNums := make([]string, 0, len(subNumSet))
|
||
for sn := range subNumSet {
|
||
subNums = append(subNums, sn)
|
||
}
|
||
// 2.1 批量查询已存在的 task_management 记录
|
||
existingTMMap := make(map[string]*model.TaskManagement) // key: sub_num|tel_num
|
||
if len(subTelKeys) > 0 {
|
||
var existingTMs []model.TaskManagement
|
||
// 构建 OR 条件查询
|
||
var conditions []string
|
||
var args []interface{}
|
||
for key := range subTelKeys {
|
||
parts := strings.SplitN(key, "|", 2)
|
||
conditions = append(conditions, "(sub_num = ? AND tel_num = ?)")
|
||
args = append(args, parts[0], parts[1])
|
||
}
|
||
query := strings.Join(conditions, " OR ")
|
||
if err := app.ModuleClients.TaskBenchDB.Where(query, args...).Find(&existingTMs).Error; err != nil {
|
||
return commonErr.ReturnError(err, "批量查询任务记录失败", "批量查询任务记录失败: ")
|
||
}
|
||
for i := range existingTMs {
|
||
tm := &existingTMs[i]
|
||
key := tm.SubNum + "|" + tm.TelNum
|
||
existingTMMap[key] = tm
|
||
}
|
||
}
|
||
|
||
// 2.2 批量查询 user_id(从 micro-account.user 表)
|
||
userIDMap := make(map[string]int) // key: sub_num
|
||
if len(subNums) > 0 {
|
||
var userRows []struct {
|
||
SubNum string `gorm:"column:sub_num"`
|
||
ID int `gorm:"column:id"`
|
||
}
|
||
_ = app.ModuleClients.TaskBenchDB.Table("`micro-account`.`user`").Unscoped().
|
||
Select("sub_num, id").
|
||
Where("sub_num COLLATE utf8mb4_general_ci IN ? AND deleted_at = 0", subNums).
|
||
Scan(&userRows).Error
|
||
for _, row := range userRows {
|
||
if row.ID > 0 {
|
||
userIDMap[row.SubNum] = row.ID
|
||
}
|
||
}
|
||
}
|
||
|
||
// 2.3 批量查询 customer_id(从 bundle_order_records 表,针对未找到 user_id 的 sub_num)
|
||
missingSubNums := make([]string, 0)
|
||
for _, sn := range subNums {
|
||
if _, found := userIDMap[sn]; !found {
|
||
missingSubNums = append(missingSubNums, sn)
|
||
}
|
||
}
|
||
if len(missingSubNums) > 0 {
|
||
var orderRows []struct {
|
||
CustomerNum string `gorm:"column:customer_num"`
|
||
CustomerID string `gorm:"column:customer_id"`
|
||
}
|
||
// 使用子查询获取每个 customer_num 对应的最新订单的 customer_id
|
||
subQuery := app.ModuleClients.BundleDB.Table("bundle_order_records").
|
||
Select("customer_id, customer_num, MAX(created_at) AS max_created_time").
|
||
Where("deleted_at IS NULL AND status = 2").
|
||
Where("customer_num COLLATE utf8mb4_general_ci IN ?", missingSubNums).
|
||
Group("customer_id, customer_num")
|
||
|
||
_ = app.ModuleClients.BundleDB.Table("bundle_order_records bor1").
|
||
Select("bor1.customer_num, bor1.customer_id").
|
||
Joins("INNER JOIN (?) bor2 ON bor1.customer_id = bor2.customer_id AND bor1.created_at = bor2.max_created_time", subQuery).
|
||
Where("bor1.deleted_at IS NULL AND bor1.status = 2").
|
||
Where("bor1.customer_num COLLATE utf8mb4_general_ci IN ?", missingSubNums).
|
||
Scan(&orderRows).Error
|
||
|
||
for _, row := range orderRows {
|
||
if row.CustomerID != "" {
|
||
if id64, _ := strconv.ParseInt(row.CustomerID, 10, 64); id64 > 0 {
|
||
// 只有当 userIDMap 中没有时才设置
|
||
if _, found := userIDMap[row.CustomerNum]; !found {
|
||
userIDMap[row.CustomerNum] = int(id64)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 2.4 批量统计每个 sub_num 的进行中与已完成任务数量
|
||
statsMap := make(map[string]struct {
|
||
ProgressTaskCount int
|
||
CompleteTaskCount int
|
||
})
|
||
if len(subNums) > 0 {
|
||
var statRows []struct {
|
||
SubNum string `gorm:"column:sub_num"`
|
||
ProgressTaskCount int `gorm:"column:progress_task_count"`
|
||
CompleteTaskCount int `gorm:"column:complete_task_count"`
|
||
}
|
||
if err := app.ModuleClients.TaskBenchDB.Table("task_assign_records").
|
||
Select("sub_num, COUNT(CASE WHEN status = 1 THEN 1 END) as progress_task_count, COUNT(CASE WHEN status = 2 THEN 1 END) as complete_task_count").
|
||
Where("sub_num IN ? AND deleted_at = 0", subNums).
|
||
Group("sub_num").
|
||
Scan(&statRows).Error; err != nil {
|
||
return commonErr.ReturnError(err, "批量查询艺人任务指派记录失败", "批量查询艺人任务指派记录失败: ")
|
||
}
|
||
for _, row := range statRows {
|
||
statsMap[row.SubNum] = struct {
|
||
ProgressTaskCount int
|
||
CompleteTaskCount int
|
||
}{
|
||
ProgressTaskCount: row.ProgressTaskCount,
|
||
CompleteTaskCount: row.CompleteTaskCount,
|
||
}
|
||
}
|
||
}
|
||
|
||
tx := app.ModuleClients.TaskBenchDB.Begin()
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
tx.Rollback()
|
||
}
|
||
}()
|
||
|
||
// 3.1 准备需要创建的 task_management 记录
|
||
var newTMs []model.TaskManagement
|
||
newTMMap := make(map[string]*model.TaskManagement) // key: sub_num|tel_num,用于后续更新时引用
|
||
for _, it := range items {
|
||
key := it.SubNum + "|" + it.TelNum
|
||
if _, exists := existingTMMap[key]; !exists {
|
||
// 检查是否已经在 newTMs 中(同一批次可能有重复的 sub_num|tel_num)
|
||
if _, inNew := newTMMap[key]; !inNew {
|
||
userID := userIDMap[it.SubNum]
|
||
tm := model.TaskManagement{
|
||
UserId: userID,
|
||
SubNum: it.SubNum,
|
||
TelNum: it.TelNum,
|
||
ArtistName: it.ArtistName,
|
||
ProgressCount: 0,
|
||
CompleteCount: 0,
|
||
CreatedAt: now,
|
||
UpdatedAt: now,
|
||
}
|
||
newTMs = append(newTMs, tm)
|
||
newTMMap[key] = &newTMs[len(newTMs)-1]
|
||
}
|
||
}
|
||
}
|
||
|
||
// 3.2 批量创建新的 task_management 记录
|
||
if len(newTMs) > 0 {
|
||
if err := tx.CreateInBatches(&newTMs, 100).Error; err != nil {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(err, "批量创建任务记录失败", "批量创建任务记录失败: ")
|
||
}
|
||
// 更新 newTMMap 中的 ID(CreateInBatches 会填充 ID)
|
||
for i := range newTMs {
|
||
key := newTMs[i].SubNum + "|" + newTMs[i].TelNum
|
||
newTMMap[key] = &newTMs[i]
|
||
}
|
||
}
|
||
|
||
// 3.3 准备指派记录和更新数据
|
||
// 统计每个 sub_num 在本批次中新增的指派数量(用于计算 progress_count)
|
||
subNumAssignCount := make(map[string]int)
|
||
for _, it := range items {
|
||
subNumAssignCount[it.SubNum]++
|
||
}
|
||
|
||
// 准备批量插入的指派记录
|
||
assignRecords := make([]model.TaskAssignRecords, 0, len(items))
|
||
for _, it := range items {
|
||
rec := model.TaskAssignRecords{
|
||
AssignRecordsUUID: uuid.New().String(),
|
||
SubNum: it.SubNum,
|
||
TelNum: it.TelNum,
|
||
ArtistName: it.ArtistName,
|
||
TaskBatch: it.TaskBatch,
|
||
Status: 1, // 未完成
|
||
ActualStatus: 1, // 未完成
|
||
OperatorType: 2, // 指派
|
||
Operator: it.Operator,
|
||
OperatorNum: it.OperatorNum,
|
||
OperatorTime: now,
|
||
TaskAssignee: it.TaskAssignee,
|
||
TaskAssigneeNum: it.TaskAssigneeNum,
|
||
PendingVideoScriptCount: it.AssignVideoScriptCount,
|
||
PendingVideoCount: it.AssignVideoCount,
|
||
PendingPostCount: it.AssignPostCount,
|
||
PendingDataCount: it.AssignDataCount,
|
||
AssignVideoScriptCount: it.AssignVideoScriptCount,
|
||
AssignVideoCount: it.AssignVideoCount,
|
||
AssignPostCount: it.AssignPostCount,
|
||
AssignDataCount: it.AssignDataCount,
|
||
CompleteVideoScriptCount: 0,
|
||
CompleteVideoCount: 0,
|
||
CompletePostCount: 0,
|
||
CompleteDataCount: 0,
|
||
CreatedAt: now,
|
||
UpdatedAt: now,
|
||
}
|
||
assignRecords = append(assignRecords, rec)
|
||
}
|
||
|
||
// 3.4 批量插入指派记录
|
||
assigneeNumsSet := make(map[string]struct{})
|
||
for _, it := range items {
|
||
if it.TaskAssigneeNum != "" {
|
||
assigneeNumsSet[it.TaskAssigneeNum] = struct{}{}
|
||
}
|
||
}
|
||
assigneeNums := make([]string, 0, len(assigneeNumsSet))
|
||
for num := range assigneeNumsSet {
|
||
assigneeNums = append(assigneeNums, num)
|
||
}
|
||
if len(assigneeNums) > 0 {
|
||
if err := tx.Where("task_assignee_num IN ? AND deleted_at = 0", assigneeNums).Delete(&model.TaskAssigneeHidden{}).Error; err != nil {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(err, "移除隐藏指派人失败", "移除隐藏指派人失败: ")
|
||
}
|
||
}
|
||
if len(assignRecords) > 0 {
|
||
if err := tx.CreateInBatches(&assignRecords, 100).Error; err != nil {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(err, "批量创建指派记录失败", "批量创建指派记录失败: ")
|
||
}
|
||
}
|
||
|
||
// 3.5 批量更新 task_management 表
|
||
updateGroups := make(map[string]*dto.BatchAssignItem)
|
||
for _, it := range items {
|
||
key := it.SubNum + "|" + it.TelNum
|
||
updateGroups[key] = it
|
||
}
|
||
|
||
for key, it := range updateGroups {
|
||
stats := statsMap[it.SubNum]
|
||
newProgressCount := stats.ProgressTaskCount + subNumAssignCount[it.SubNum]
|
||
|
||
// 获取对应的 task_management 记录 ID
|
||
var tmID int64
|
||
if tm, exists := existingTMMap[key]; exists {
|
||
tmID = tm.ID
|
||
} else if tm, exists := newTMMap[key]; exists {
|
||
tmID = tm.ID
|
||
}
|
||
|
||
if tmID > 0 {
|
||
updateData := map[string]interface{}{
|
||
"progress_count": newProgressCount,
|
||
"complete_count": stats.CompleteTaskCount,
|
||
"last_task_assignee": it.TaskAssignee,
|
||
"task_assignee_num": it.TaskAssigneeNum,
|
||
"updated_at": now,
|
||
}
|
||
if err := tx.Model(&model.TaskManagement{}).Where("id = ?", tmID).Updates(updateData).Error; err != nil {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(err, "更新任务记录失败", "更新任务记录失败: ")
|
||
}
|
||
}
|
||
}
|
||
|
||
if err := tx.Commit().Error; err != nil {
|
||
return commonErr.ReturnError(err, "提交事务失败", "提交批量指派事务失败: ")
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// GetRecentAssignRecords 查询最近被指派记录
|
||
func GetRecentAssignRecords(limit int) ([]*model.TaskAssignRecords, error) {
|
||
var records []*model.TaskAssignRecords
|
||
|
||
err := app.ModuleClients.TaskBenchDB.Raw(`
|
||
SELECT t1.*
|
||
FROM task_assign_records t1
|
||
WHERE t1.operator_type = ? AND t1.deleted_at = 0
|
||
AND NOT EXISTS (
|
||
SELECT 1 FROM task_assign_records t2
|
||
WHERE t2.task_assignee_num = t1.task_assignee_num
|
||
AND t2.operator_type = ? AND t2.deleted_at = 0
|
||
AND (t2.operator_time > t1.operator_time
|
||
OR (t2.operator_time = t1.operator_time AND t2.id > t1.id))
|
||
)
|
||
AND NOT EXISTS (
|
||
SELECT 1 FROM task_assignee_hidden h
|
||
WHERE h.task_assignee_num = t1.task_assignee_num
|
||
AND h.deleted_at = 0
|
||
)
|
||
ORDER BY t1.operator_time DESC, t1.id DESC
|
||
LIMIT ?
|
||
`, 2, 2, limit).Scan(&records).Error
|
||
|
||
if err != nil {
|
||
return nil, commonErr.ReturnError(err, "查询最近指派记录失败", "查询最近指派记录失败: ")
|
||
}
|
||
|
||
return records, nil
|
||
}
|
||
|
||
// GetEmployeeAssignedTasks 根据登录人信息查询被指派给该员工的艺人任务
|
||
func GetEmployeeAssignedTasks(req *dto.EmployeeTaskQueryRequest) ([]*model.TaskAssignRecords, int64, error) {
|
||
var records []*model.TaskAssignRecords
|
||
var total int64
|
||
|
||
// 构建查询条件
|
||
query := app.ModuleClients.TaskBenchDB.Model(&model.TaskAssignRecords{}).
|
||
Select("task_assign_records.id, task_assign_records.assign_records_uuid, task_assign_records.sub_num, task_assign_records.tel_num, task_assign_records.task_batch, task_assign_records.status, task_assign_records.actual_status, task_assign_records.complete_time, task_assign_records.operator_type, task_assign_records.operator, task_assign_records.operator_id, task_assign_records.operator_num, task_assign_records.operator_time, task_assign_records.task_assignee, task_assign_records.task_assignee_num, task_assign_records.pending_video_script_count, task_assign_records.pending_video_count, task_assign_records.pending_post_count, task_assign_records.pending_data_count, task_assign_records.assign_video_script_count, task_assign_records.assign_video_count, task_assign_records.assign_post_count, task_assign_records.assign_data_count, task_assign_records.complete_video_script_count, task_assign_records.complete_video_count, task_assign_records.complete_post_count, task_assign_records.complete_data_count, task_assign_records.created_at, task_assign_records.updated_at, task_assign_records.deleted_at, rn.name AS artist_name").
|
||
Joins("LEFT JOIN `micro-account`.`user` u ON u.sub_num COLLATE utf8mb4_general_ci = task_assign_records.sub_num COLLATE utf8mb4_general_ci").
|
||
Joins("LEFT JOIN `micro-account`.real_name rn ON u.real_name_id = rn.id").
|
||
Where("u.deleted_at = 0").
|
||
Where("task_assign_records.task_assignee_num = ?", req.TaskAssigneeNum)
|
||
|
||
// 关键词搜索(艺人姓名、编号、手机号)
|
||
if req.Keyword != "" {
|
||
query = query.Where("task_assign_records.sub_num LIKE ? OR task_assign_records.tel_num LIKE ? OR rn.name LIKE ?",
|
||
"%"+req.Keyword+"%", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
|
||
}
|
||
|
||
// 指派人操作人姓名
|
||
if req.Operator != "" {
|
||
query = query.Where("task_assign_records.operator LIKE ?", "%"+req.Operator+"%")
|
||
}
|
||
|
||
// 任务批次
|
||
if req.TaskBatch != "" {
|
||
query = query.Where("task_assign_records.task_batch LIKE ?", "%"+req.TaskBatch+"%")
|
||
}
|
||
|
||
// 指派时间区间
|
||
if req.StartTime != "" && req.EndTime != "" {
|
||
query = query.Where("task_assign_records.operator_time BETWEEN ? AND ?", req.StartTime, req.EndTime)
|
||
} else if req.StartTime != "" {
|
||
query = query.Where("task_assign_records.operator_time >= ?", req.StartTime)
|
||
} else if req.EndTime != "" {
|
||
query = query.Where("task_assign_records.operator_time <= ?", req.EndTime)
|
||
}
|
||
|
||
// 完成时间区间
|
||
if req.StartCompleteTime != "" && req.EndCompleteTime != "" {
|
||
query = query.Where("task_assign_records.complete_time BETWEEN ? AND ?", req.StartCompleteTime, req.EndCompleteTime)
|
||
} else if req.StartCompleteTime != "" {
|
||
query = query.Where("task_assign_records.complete_time >= ?", req.StartCompleteTime)
|
||
} else if req.EndCompleteTime != "" {
|
||
query = query.Where("task_assign_records.complete_time <= ?", req.EndCompleteTime)
|
||
}
|
||
|
||
// 反馈完成状态
|
||
if req.Status != 0 {
|
||
query = query.Where("task_assign_records.status = ?", req.Status)
|
||
}
|
||
|
||
// 根据排序字段倒序
|
||
if req.SortBy != "" {
|
||
if strings.EqualFold(req.SortBy, "artist_name") {
|
||
query = query.Order("rn.name DESC")
|
||
} else {
|
||
query = query.Order("task_assign_records." + 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("task_assign_records.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 {
|
||
// 开启事务,确保指派记录更新与任务管理同步原子性
|
||
tx := app.ModuleClients.TaskBenchDB.Begin()
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
tx.Rollback()
|
||
}
|
||
}()
|
||
|
||
// 1) 查询该指派记录,获取艺人信息
|
||
var assignRecord model.TaskAssignRecords
|
||
if err := tx.Where("assign_records_uuid = ?", assignRecordsUUID).First(&assignRecord).Error; err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(nil, "未找到任务记录", "未找到指派记录: ")
|
||
}
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(err, "查询指派记录失败", "查询指派记录失败: ")
|
||
}
|
||
|
||
// 2) 更新该记录为完成状态
|
||
now := time.Now()
|
||
updateData := map[string]interface{}{
|
||
"status": 2, // 2:完成
|
||
"complete_time": &now,
|
||
"updated_at": now,
|
||
}
|
||
if err := tx.Model(&assignRecord).Updates(updateData).Error; err != nil {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(err, "更新任务完成状态失败", "更新任务完成状态失败: ")
|
||
}
|
||
|
||
// 3) 统计该艺人按照艺人sub_num 当前进行中与已完成数量
|
||
var stats struct {
|
||
ProgressTaskCount int
|
||
CompleteTaskCount int
|
||
}
|
||
if err := tx.Table("task_assign_records").
|
||
Select("COUNT(CASE WHEN status = 1 THEN 1 END) as progress_task_count, COUNT(CASE WHEN status = 2 THEN 1 END) as complete_task_count").
|
||
Where("sub_num = ? AND deleted_at = 0", assignRecord.SubNum).
|
||
Scan(&stats).Error; err != nil {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(err, "查询艺人任务指派记录失败", "查询艺人任务指派记录失败: ")
|
||
}
|
||
|
||
// 4) 同步任务管理表的进行中与已完成数量(若不存在则创建)
|
||
var taskManagement model.TaskManagement
|
||
if err := tx.Where("sub_num = ? AND tel_num = ?", assignRecord.SubNum, assignRecord.TelNum).First(&taskManagement).Error; err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
// 创建新记录
|
||
taskManagement = model.TaskManagement{
|
||
SubNum: assignRecord.SubNum,
|
||
TelNum: assignRecord.TelNum,
|
||
ArtistName: assignRecord.ArtistName,
|
||
ProgressCount: stats.ProgressTaskCount,
|
||
CompleteCount: stats.CompleteTaskCount,
|
||
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, "查询任务记录失败", "查询任务记录失败: ")
|
||
}
|
||
} else {
|
||
updateTM := map[string]interface{}{
|
||
"ProgressCount": stats.ProgressTaskCount,
|
||
"CompleteCount": stats.CompleteTaskCount,
|
||
"UpdatedAt": time.Now(),
|
||
}
|
||
if err := tx.Model(&taskManagement).Updates(updateTM).Error; err != nil {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(err, "更新任务记录失败", "更新任务记录失败: ")
|
||
}
|
||
}
|
||
|
||
// 5) 提交事务
|
||
if err := tx.Commit().Error; err != nil {
|
||
return commonErr.ReturnError(err, "提交事务失败", "提交事务失败: ")
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// UpdateTaskActualStatusByUUID 根据指派记录UUID更新实际完成状态(支持设置为未完成/完成/已中止)
|
||
func UpdateTaskActualStatusByUUID(assignRecordsUUID string, actualStatus int) error {
|
||
// 开启事务,确保更新原子性
|
||
tx := app.ModuleClients.TaskBenchDB.Begin()
|
||
defer func() {
|
||
if r := recover(); r != nil {
|
||
tx.Rollback()
|
||
}
|
||
}()
|
||
|
||
// 1) 查询该指派记录
|
||
var assignRecord model.TaskAssignRecords
|
||
if err := tx.Where("assign_records_uuid = ?", assignRecordsUUID).First(&assignRecord).Error; err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(nil, "未找到任务记录", "未找到指派记录: ")
|
||
}
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(err, "查询指派记录失败", "查询指派记录失败: ")
|
||
}
|
||
|
||
// 如果目标状态为中止(3),且该任务已实际完成(2),则不允许中止
|
||
if actualStatus == 3 && assignRecord.ActualStatus == 2 {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(nil, "该任务已实际完成", "该任务已实际完成,不能中止: ")
|
||
}
|
||
|
||
// 2) 更新实际完成状态
|
||
now := time.Now()
|
||
updateData := map[string]interface{}{
|
||
"actual_status": actualStatus,
|
||
"updated_at": now,
|
||
}
|
||
if err := tx.Model(&assignRecord).Updates(updateData).Error; err != nil {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(err, "更新实际完成状态失败", "更新实际完成状态失败: ")
|
||
}
|
||
|
||
// 3) 提交事务
|
||
if err := tx.Commit().Error; err != nil {
|
||
return commonErr.ReturnError(err, "提交事务失败", "提交事务失败: ")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
// UpdateTaskProgress 员工实际完成任务状态更新
|
||
// 员工调用视频、图文、数据时,对应的待完成数据减一,已完成数据加一
|
||
func UpdateTaskProgress(req *dto.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, "参数错误", "员工姓名和手机号不能为空")
|
||
}
|
||
|
||
// 仅选择“未手动完成”的记录
|
||
query := tx.Where("task_assignee = ? AND task_assignee_num = ? AND status = 1",
|
||
req.EmployeeName, req.EmployeeNum)
|
||
|
||
// 根据任务类型,要求该类型仍有可完成的余量
|
||
switch req.TaskType {
|
||
case "video":
|
||
query = query.Where("pending_video_count > 0 AND complete_video_count < assign_video_count")
|
||
case "post":
|
||
query = query.Where("pending_post_count > 0 AND complete_post_count < assign_post_count")
|
||
case "data":
|
||
query = query.Where("pending_data_count > 0 AND complete_data_count < assign_data_count")
|
||
case "script":
|
||
query = query.Where("pending_video_script_count > 0 AND complete_video_script_count < assign_video_script_count")
|
||
default:
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(nil, "无效的任务类型", "任务类型必须是视频、图文、数据或脚本")
|
||
}
|
||
|
||
err = query.
|
||
Order("operator_time ASC").
|
||
First(&assignRecord).Error
|
||
|
||
if err != nil {
|
||
if err == gorm.ErrRecordNotFound {
|
||
tx.Rollback()
|
||
return nil
|
||
}
|
||
tx.Rollback()
|
||
fmt.Println("查找指派记录失败")
|
||
return nil
|
||
}
|
||
}
|
||
|
||
// 2. 根据任务类型更新完成数量
|
||
updateData := map[string]interface{}{
|
||
"updated_at": time.Now(),
|
||
}
|
||
|
||
switch req.TaskType {
|
||
case "video":
|
||
newCompleteCount := assignRecord.CompleteVideoCount + req.CompleteCount
|
||
if newCompleteCount > assignRecord.AssignVideoCount {
|
||
// 暂时都只记录错误,不报错;并且不更新
|
||
if req.AssignRecordsUUID != "false" {
|
||
app.ModuleClients.Lg.Info("视频完成数量超出限制,跳过更新",
|
||
zap.String("assignRecordsUUID", assignRecord.AssignRecordsUUID),
|
||
zap.String("employeeName", req.EmployeeName),
|
||
zap.String("employeeNum", req.EmployeeNum),
|
||
zap.Int("currentCompleteCount", assignRecord.CompleteVideoCount),
|
||
zap.Int("requestCompleteCount", req.CompleteCount),
|
||
zap.Int("newCompleteCount", newCompleteCount),
|
||
zap.Int("assignVideoCount", assignRecord.AssignVideoCount),
|
||
)
|
||
tx.Rollback()
|
||
return nil
|
||
} else {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(nil, "完成数量超出指派数量", "视频完成数量不能超过已指派数量")
|
||
}
|
||
}
|
||
newPending := assignRecord.PendingVideoCount - req.CompleteCount
|
||
if newPending < 0 {
|
||
// 暂时都只记录错误,不报错;并且不更新
|
||
if req.AssignRecordsUUID != "false" {
|
||
app.ModuleClients.Lg.Info("待发视频数不足,跳过更新",
|
||
zap.String("assignRecordsUUID", assignRecord.AssignRecordsUUID),
|
||
zap.String("employeeName", req.EmployeeName),
|
||
zap.String("employeeNum", req.EmployeeNum),
|
||
zap.Int("currentPendingCount", assignRecord.PendingVideoCount),
|
||
zap.Int("requestCompleteCount", req.CompleteCount),
|
||
zap.Int("newPendingCount", newPending),
|
||
)
|
||
tx.Rollback()
|
||
return nil
|
||
} else {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(nil, "待发视频数不足", "待发视频数不能小于0")
|
||
}
|
||
}
|
||
updateData["complete_video_count"] = newCompleteCount
|
||
updateData["pending_video_count"] = newPending
|
||
case "post":
|
||
newCompleteCount := assignRecord.CompletePostCount + req.CompleteCount
|
||
if newCompleteCount > assignRecord.AssignPostCount {
|
||
// 暂时先只记录,不报错;并且不更新
|
||
if req.AssignRecordsUUID != "false" {
|
||
app.ModuleClients.Lg.Info("图文完成数量超出限制,跳过更新",
|
||
zap.String("assignRecordsUUID", assignRecord.AssignRecordsUUID),
|
||
zap.String("employeeName", req.EmployeeName),
|
||
zap.String("employeeNum", req.EmployeeNum),
|
||
zap.Int("currentCompleteCount", assignRecord.CompletePostCount),
|
||
zap.Int("requestCompleteCount", req.CompleteCount),
|
||
zap.Int("newCompleteCount", newCompleteCount),
|
||
zap.Int("assignPostCount", assignRecord.AssignPostCount),
|
||
)
|
||
tx.Rollback()
|
||
return nil
|
||
} else {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(nil, "完成数量超出指派数量", "图文完成数量不能超过已指派数量")
|
||
}
|
||
}
|
||
newPending := assignRecord.PendingPostCount - req.CompleteCount
|
||
if newPending < 0 {
|
||
// 暂时都只记录错误,不报错;并且不更新
|
||
if req.AssignRecordsUUID != "false" {
|
||
app.ModuleClients.Lg.Info("待发图文数不足,跳过更新",
|
||
zap.String("assignRecordsUUID", assignRecord.AssignRecordsUUID),
|
||
zap.String("employeeName", req.EmployeeName),
|
||
zap.String("employeeNum", req.EmployeeNum),
|
||
zap.Int("currentPendingCount", assignRecord.PendingPostCount),
|
||
zap.Int("requestCompleteCount", req.CompleteCount),
|
||
zap.Int("newPendingCount", newPending),
|
||
)
|
||
tx.Rollback()
|
||
return nil
|
||
} else {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(nil, "待发图文数不足", "待发图文数不能小于0")
|
||
}
|
||
}
|
||
updateData["complete_post_count"] = newCompleteCount
|
||
updateData["pending_post_count"] = newPending
|
||
case "data":
|
||
newCompleteCount := assignRecord.CompleteDataCount + req.CompleteCount
|
||
if newCompleteCount > assignRecord.AssignDataCount {
|
||
// 暂时都只记录错误,不报错;并且不更新
|
||
if req.AssignRecordsUUID != "false" {
|
||
app.ModuleClients.Lg.Info("数据完成数量超出限制,跳过更新",
|
||
zap.String("assignRecordsUUID", assignRecord.AssignRecordsUUID),
|
||
zap.String("employeeName", req.EmployeeName),
|
||
zap.String("employeeNum", req.EmployeeNum),
|
||
zap.Int("currentCompleteCount", assignRecord.CompleteDataCount),
|
||
zap.Int("requestCompleteCount", req.CompleteCount),
|
||
zap.Int("newCompleteCount", newCompleteCount),
|
||
zap.Int("assignDataCount", assignRecord.AssignDataCount),
|
||
)
|
||
tx.Rollback()
|
||
return nil
|
||
} else {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(nil, "完成数量超出指派数量", "数据完成数量不能超过已指派数量")
|
||
}
|
||
}
|
||
newPending := assignRecord.PendingDataCount - req.CompleteCount
|
||
if newPending < 0 {
|
||
// 暂时都只记录错误,不报错;并且不更新
|
||
if req.AssignRecordsUUID != "false" {
|
||
app.ModuleClients.Lg.Info("待发数据数不足,跳过更新",
|
||
zap.String("assignRecordsUUID", assignRecord.AssignRecordsUUID),
|
||
zap.String("employeeName", req.EmployeeName),
|
||
zap.String("employeeNum", req.EmployeeNum),
|
||
zap.Int("currentPendingCount", assignRecord.PendingDataCount),
|
||
zap.Int("requestCompleteCount", req.CompleteCount),
|
||
zap.Int("newPendingCount", newPending),
|
||
)
|
||
tx.Rollback()
|
||
return nil
|
||
} else {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(nil, "待发数据数不足", "待发数据数不能小于0")
|
||
}
|
||
}
|
||
updateData["complete_data_count"] = newCompleteCount
|
||
updateData["pending_data_count"] = newPending
|
||
case "script":
|
||
newCompleteCount := assignRecord.CompleteVideoScriptCount + req.CompleteCount
|
||
if newCompleteCount > assignRecord.AssignVideoScriptCount {
|
||
// 暂时都只记录错误,不报错;并且不更新
|
||
if req.AssignRecordsUUID != "false" {
|
||
app.ModuleClients.Lg.Info("数据完成数量超出限制,跳过更新",
|
||
zap.String("assignRecordsUUID", assignRecord.AssignRecordsUUID),
|
||
zap.String("employeeName", req.EmployeeName),
|
||
zap.String("employeeNum", req.EmployeeNum),
|
||
zap.Int("currentCompleteCount", assignRecord.CompleteDataCount),
|
||
zap.Int("requestCompleteCount", req.CompleteCount),
|
||
zap.Int("newCompleteCount", newCompleteCount),
|
||
zap.Int("assignDataCount", assignRecord.AssignDataCount),
|
||
)
|
||
tx.Rollback()
|
||
return nil
|
||
} else {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(nil, "完成数量超出指派数量", "数据完成数量不能超过已指派数量")
|
||
}
|
||
}
|
||
newPending := assignRecord.PendingVideoScriptCount - req.CompleteCount
|
||
if newPending < 0 {
|
||
// 暂时都只记录错误,不报错;并且不更新
|
||
if req.AssignRecordsUUID != "false" {
|
||
app.ModuleClients.Lg.Info("待发数据数不足,跳过更新",
|
||
zap.String("assignRecordsUUID", assignRecord.AssignRecordsUUID),
|
||
zap.String("employeeName", req.EmployeeName),
|
||
zap.String("employeeNum", req.EmployeeNum),
|
||
zap.Int("currentPendingCount", assignRecord.PendingDataCount),
|
||
zap.Int("requestCompleteCount", req.CompleteCount),
|
||
zap.Int("newPendingCount", newPending),
|
||
)
|
||
tx.Rollback()
|
||
return nil
|
||
} else {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(nil, "待发数据数不足", "待发数据数不能小于0")
|
||
}
|
||
}
|
||
updateData["complete_video_script_count"] = newCompleteCount
|
||
updateData["pending_video_script_count"] = newPending
|
||
default:
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(nil, "无效的任务类型", "任务类型必须是视频、图文、数据或脚本")
|
||
}
|
||
|
||
// 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, "更新实际完成状态失败", "更新实际完成状态失败: ")
|
||
}
|
||
}
|
||
|
||
// 6. 记录本次完成项的 UUID
|
||
var typeCode int
|
||
switch req.TaskType {
|
||
case "video":
|
||
typeCode = 1
|
||
case "post":
|
||
typeCode = 2
|
||
case "data":
|
||
typeCode = 3
|
||
case "script":
|
||
typeCode = 4
|
||
}
|
||
if req.UUID != "" {
|
||
uuidItem := &model.TaskAssignUUIDItems{
|
||
AssignRecordsUUID: assignRecord.AssignRecordsUUID,
|
||
Type: typeCode,
|
||
UUID: req.UUID,
|
||
}
|
||
if err = tx.Create(uuidItem).Error; err != nil {
|
||
tx.Rollback()
|
||
return commonErr.ReturnError(err, "插入任务UUID记录失败", "插入任务UUID记录失败: ")
|
||
}
|
||
}
|
||
|
||
// 提交事务
|
||
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
|
||
}
|
||
|
||
// 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 多条件查询操作记录表
|
||
func GetTaskAssignRecordsList(req *dto.TaskAssignRecordsQueryRequest) ([]*model.TaskAssignRecords, int64, *dto.TaskAssignRecordsSummary, error) {
|
||
var records []*model.TaskAssignRecords
|
||
var total int64
|
||
var summary dto.TaskAssignRecordsSummary
|
||
|
||
// 构建查询条件
|
||
query := app.ModuleClients.TaskBenchDB.Model(&model.TaskAssignRecords{}).
|
||
Select("task_assign_records.*, rn.name AS artist_name").
|
||
Joins("LEFT JOIN `micro-account`.`user` u ON u.sub_num COLLATE utf8mb4_general_ci = task_assign_records.sub_num COLLATE utf8mb4_general_ci").
|
||
Joins("LEFT JOIN `micro-account`.real_name rn ON u.real_name_id = rn.id").
|
||
Where("u.deleted_at = 0")
|
||
|
||
// 关键词搜索(艺人姓名、编号、手机号)
|
||
if req.Keyword != "" {
|
||
query = query.Where("task_assign_records.sub_num LIKE ? OR task_assign_records.tel_num LIKE ? OR rn.name LIKE ?",
|
||
"%"+req.Keyword+"%", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
|
||
}
|
||
|
||
// 指派人姓名
|
||
if req.TaskAssignee != "" {
|
||
query = query.Where("task_assign_records.task_assignee LIKE ?", "%"+req.TaskAssignee+"%")
|
||
}
|
||
|
||
// 操作人姓名或手机号
|
||
if req.Operator != "" {
|
||
query = query.Where("task_assign_records.operator LIKE ? OR task_assign_records.operator_num LIKE ?", "%"+req.Operator+"%", "%"+req.Operator+"%")
|
||
}
|
||
|
||
// 操作人手机号
|
||
if req.OperatorNum != "" {
|
||
query = query.Where("task_assign_records.operator_num LIKE ?", "%"+req.OperatorNum+"%")
|
||
}
|
||
|
||
// 任务批次
|
||
if req.TaskBatch != "" {
|
||
query = query.Where("task_assign_records.task_batch LIKE ?", "%"+req.TaskBatch+"%")
|
||
}
|
||
|
||
// 操作时间区间
|
||
if req.StartTime != "" && req.EndTime != "" {
|
||
query = query.Where("task_assign_records.operator_time BETWEEN ? AND ?", req.StartTime, req.EndTime)
|
||
} else if req.StartTime != "" {
|
||
query = query.Where("task_assign_records.operator_time >= ?", req.StartTime)
|
||
} else if req.EndTime != "" {
|
||
query = query.Where("task_assign_records.operator_time <= ?", req.EndTime)
|
||
}
|
||
|
||
// 反馈完成状态
|
||
if req.Status != 0 {
|
||
query = query.Where("task_assign_records.status = ?", req.Status)
|
||
}
|
||
|
||
// 实际完成状态
|
||
if req.ActualStatus != 0 {
|
||
query = query.Where("task_assign_records.actual_status = ?", req.ActualStatus)
|
||
}
|
||
|
||
// 计算总数
|
||
query.Count(&total)
|
||
|
||
// 汇总:根据筛选条件分页前的所有艺人待发与已完成数量(同筛选条件,关联用户与实名表以支持按实名筛选)
|
||
sumQuery := app.ModuleClients.TaskBenchDB.Model(&model.TaskAssignRecords{}).
|
||
Joins("LEFT JOIN `micro-account`.`user` u ON u.sub_num COLLATE utf8mb4_general_ci = task_assign_records.sub_num COLLATE utf8mb4_general_ci").
|
||
Joins("LEFT JOIN `micro-account`.real_name rn ON u.real_name_id = rn.id").
|
||
Where("u.deleted_at = 0")
|
||
|
||
if req.Keyword != "" {
|
||
// 与列表查询保持一致:按艺人编号、手机号、实名表姓名进行模糊匹配
|
||
sumQuery = sumQuery.Where("task_assign_records.sub_num LIKE ? OR task_assign_records.tel_num LIKE ? OR rn.name LIKE ?",
|
||
"%"+req.Keyword+"%", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
|
||
}
|
||
if req.TaskAssignee != "" {
|
||
sumQuery = sumQuery.Where("task_assign_records.task_assignee LIKE ?", "%"+req.TaskAssignee+"%")
|
||
}
|
||
if req.Operator != "" {
|
||
sumQuery = sumQuery.Where("task_assign_records.operator LIKE ? OR task_assign_records.operator_num LIKE ?", "%"+req.Operator+"%", "%"+req.Operator+"%")
|
||
}
|
||
if req.OperatorNum != "" {
|
||
sumQuery = sumQuery.Where("task_assign_records.operator_num LIKE ?", "%"+req.OperatorNum+"%")
|
||
}
|
||
if req.TaskBatch != "" {
|
||
sumQuery = sumQuery.Where("task_assign_records.task_batch LIKE ?", "%"+req.TaskBatch+"%")
|
||
}
|
||
if req.StartTime != "" && req.EndTime != "" {
|
||
sumQuery = sumQuery.Where("task_assign_records.operator_time BETWEEN ? AND ?", req.StartTime, req.EndTime)
|
||
} else if req.StartTime != "" {
|
||
sumQuery = sumQuery.Where("task_assign_records.operator_time >= ?", req.StartTime)
|
||
} else if req.EndTime != "" {
|
||
sumQuery = sumQuery.Where("task_assign_records.operator_time <= ?", req.EndTime)
|
||
}
|
||
if req.Status != 0 {
|
||
sumQuery = sumQuery.Where("task_assign_records.status = ?", req.Status)
|
||
}
|
||
if req.ActualStatus != 0 {
|
||
sumQuery = sumQuery.Where("task_assign_records.actual_status = ?", req.ActualStatus)
|
||
}
|
||
|
||
// 执行汇总查询(不分页)
|
||
errSum := sumQuery.Select("\n SUM(task_assign_records.assign_video_script_count) AS total_pending_video_script_count,\n SUM(task_assign_records.assign_video_count) AS total_pending_video_count,\n SUM(task_assign_records.assign_post_count) AS total_pending_post_count,\n SUM(task_assign_records.assign_data_count) AS total_pending_data_count,\n SUM(task_assign_records.complete_video_script_count) AS total_complete_video_script_count,\n SUM(task_assign_records.complete_video_count) AS total_complete_video_count,\n SUM(task_assign_records.complete_post_count) AS total_complete_post_count,\n SUM(task_assign_records.complete_data_count) AS total_complete_data_count\n ").Scan(&summary).Error
|
||
if errSum != nil {
|
||
return nil, 0, nil, commonErr.ReturnError(errSum, "查询操作记录汇总失败", "查询操作记录汇总失败: ")
|
||
}
|
||
|
||
// 分页
|
||
if req.PageSize > 0 && req.Page > 0 {
|
||
offset := (req.Page - 1) * req.PageSize
|
||
query = query.Limit(req.PageSize).Offset(offset)
|
||
}
|
||
|
||
// 排序处理:白名单列 + sortType
|
||
// 允许的排序字段,避免SQL注入
|
||
allowed := map[string]bool{
|
||
"operator_time": true,
|
||
"updated_at": true,
|
||
"complete_time": true,
|
||
"status": true,
|
||
"actual_status": true,
|
||
"task_batch": true,
|
||
"artist_name": true,
|
||
"sub_num": true,
|
||
"tel_num": true,
|
||
"task_assignee": true,
|
||
"operator": true,
|
||
"operator_num": true,
|
||
"task_assignee_num": true,
|
||
"pending_video_count": true,
|
||
"pending_post_count": true,
|
||
"pending_data_count": true,
|
||
"pending_video_script_count": true,
|
||
"assign_video_count": true,
|
||
"assign_post_count": true,
|
||
"assign_data_count": true,
|
||
"assign_video_script_count": true,
|
||
"complete_video_count": true,
|
||
"complete_post_count": true,
|
||
"complete_data_count": true,
|
||
"complete_video_script_count": true,
|
||
"created_at": true,
|
||
"assign_records_uuid": true,
|
||
}
|
||
|
||
// 默认按操作时间降序
|
||
orderClause := "operator_time DESC"
|
||
if req.SortBy != "" && allowed[req.SortBy] {
|
||
orderClause = fmt.Sprintf("%s %s", req.SortBy, req.SortType)
|
||
}
|
||
|
||
// 应用排序
|
||
err := query.Order(orderClause).Find(&records).Error
|
||
if err != nil {
|
||
return nil, 0, nil, commonErr.ReturnError(err, "查询操作记录失败", "查询操作记录失败: ")
|
||
}
|
||
return records, total, &summary, nil
|
||
}
|
||
|
||
// GetValidArtistList 查询套餐状态为有效中的艺人数据列表
|
||
func GetValidArtistList() ([]dto.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").Unscoped().
|
||
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 + bb.manual_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 COLLATE utf8mb4_general_ci = bor.uuid COLLATE utf8mb4_general_ci").
|
||
Joins("LEFT JOIN bundle_activate bc ON bc.user_id = u.id").
|
||
Where("rn.name IS NOT NULL").
|
||
Where("u.deleted_at = 0").
|
||
Where("bor.deleted_at IS NULL").
|
||
Where("bb.expired_at > ?", time.Now()).
|
||
Where("bc.activate = ?", 2).
|
||
Where("bb.month = ?", time.Now().Format("2006-01")).
|
||
Order("bor.expiration_time desc")
|
||
|
||
var data []dto.ValidArtistInfo
|
||
err := session.Find(&data).Error
|
||
if err != nil {
|
||
return nil, commonErr.ReturnError(err, "查询有效艺人失败", "查询有效艺人失败: ")
|
||
}
|
||
|
||
fmt.Println(data)
|
||
return data, nil
|
||
}
|
||
|
||
// 新增:根据艺人手机号统计进行中的与已完成任务数量
|
||
func GetArtistTaskStatsBySubNum(SubNum string) (int, int, error) {
|
||
if SubNum == "" {
|
||
return 0, 0, nil
|
||
}
|
||
|
||
var res struct {
|
||
ProgressTaskCount int
|
||
CompleteTaskCount int
|
||
}
|
||
|
||
err := app.ModuleClients.TaskBenchDB.Table("task_assign_records").
|
||
Select("COUNT(CASE WHEN status = 1 THEN 1 END) as progress_task_count, COUNT(CASE WHEN status = 2 THEN 1 END) as complete_task_count").
|
||
Where("sub_num = ? AND deleted_at = 0", SubNum).
|
||
Scan(&res).Error
|
||
|
||
if err != nil {
|
||
return 0, 0, nil
|
||
}
|
||
return res.ProgressTaskCount, res.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
|
||
}
|
||
|
||
// CreateTaskWorkLog 创建任务日志记录
|
||
func CreateTaskWorkLog(req *dto.CreateTaskWorkLogRequest) error {
|
||
// 参数校验
|
||
if req == nil {
|
||
return commonErr.ReturnError(nil, "参数错误", "请求参数不能为空")
|
||
}
|
||
|
||
// 生成日志UUID
|
||
workLogUUID := uuid.New().String()
|
||
|
||
// 获取当前时间戳(Unix时间戳,秒级)
|
||
now := int(time.Now().Unix())
|
||
|
||
// 构建日志记录
|
||
workLog := &model.TaskWorkLog{
|
||
WorkLogUUID: workLogUUID,
|
||
AssignRecordsUUID: req.AssignRecordsUUID,
|
||
WorkUUID: req.WorkUUID,
|
||
Title: req.Title,
|
||
ArtistUUID: req.ArtistUUID,
|
||
SubNum: req.SubNum,
|
||
TelNum: req.TelNum,
|
||
ArtistName: req.ArtistName,
|
||
OperationType: req.OperationType,
|
||
TaskType: req.TaskType,
|
||
TaskCount: req.TaskCount,
|
||
Remark: req.Remark,
|
||
OperatorName: req.OperatorName,
|
||
OperatorNum: req.OperatorNum,
|
||
OperationTime: now,
|
||
CreatedAt: now,
|
||
UpdatedAt: now,
|
||
}
|
||
|
||
// 插入数据库
|
||
if err := app.ModuleClients.TaskBenchDB.Create(workLog).Error; err != nil {
|
||
return commonErr.ReturnError(err, "创建任务日志失败", "创建任务日志失败: ")
|
||
}
|
||
|
||
return nil
|
||
}
|