diff --git a/server/app/adminapi/logic/stats/PersonalAccountCostLogic.php b/server/app/adminapi/logic/stats/PersonalAccountCostLogic.php index c6528f89..efab4c18 100644 --- a/server/app/adminapi/logic/stats/PersonalAccountCostLogic.php +++ b/server/app/adminapi/logic/stats/PersonalAccountCostLogic.php @@ -22,10 +22,8 @@ class PersonalAccountCostLogic extends BaseLogic return false; } - $conflict = self::findCostConflict($costDate, $mediaSource); - if ($conflict['conflict']) { - $by = $conflict['creator_name'] !== '' ? '(录入人:' . $conflict['creator_name'] . ')' : ''; - self::setError('该日期下该渠道的账户消耗已存在' . $by . ',请直接编辑该记录,避免重复汇总'); + if (self::isCostDuplicate($adminId, $costDate, $mediaSource)) { + self::setError('您在该日期下已录入过该渠道的账户消耗,请直接编辑'); return false; } diff --git a/server/app/adminapi/logic/stats/PersonalStatsScopeTrait.php b/server/app/adminapi/logic/stats/PersonalStatsScopeTrait.php index 79fe8154..6fc505bf 100644 --- a/server/app/adminapi/logic/stats/PersonalStatsScopeTrait.php +++ b/server/app/adminapi/logic/stats/PersonalStatsScopeTrait.php @@ -200,43 +200,32 @@ trait PersonalStatsScopeTrait } /** - * 同一天 + 同一渠道全局唯一(不分录入人):避免重复汇总。 - * 命中时返回首个已存在记录的录入人姓名,便于前端提示。 - * - * @return array{conflict: bool, creator_name: string} + * 同一录入人 + 同一天 + 同一渠道唯一(不同录入人可同日同渠道各录一条)。 */ - protected static function findYejiConflict(string $yejiDate, string $mediaSource, int $excludeId = 0): array + protected static function isYejiDuplicate(int $creatorId, string $yejiDate, string $mediaSource, int $excludeId = 0): bool { - $query = PersonalYeji::where('yeji_date', $yejiDate) + $query = PersonalYeji::where('creator_id', $creatorId) + ->where('yeji_date', $yejiDate) ->where('media_source', $mediaSource); if ($excludeId > 0) { $query->where('id', '<>', $excludeId); } - $row = $query->field('id, creator_name')->find(); - if (!$row) { - return ['conflict' => false, 'creator_name' => '']; - } - return ['conflict' => true, 'creator_name' => (string) ($row['creator_name'] ?? '')]; + return $query->count() > 0; } /** - * 同一天 + 同一渠道全局唯一(不分录入人):避免重复汇总。 - * - * @return array{conflict: bool, creator_name: string} + * 同一录入人 + 同一天 + 同一渠道唯一(不同录入人可同日同渠道各录一条)。 */ - protected static function findCostConflict(string $costDate, string $mediaSource, int $excludeId = 0): array + protected static function isCostDuplicate(int $creatorId, string $costDate, string $mediaSource, int $excludeId = 0): bool { - $query = PersonalAccountCost::where('cost_date', $costDate) + $query = PersonalAccountCost::where('creator_id', $creatorId) + ->where('cost_date', $costDate) ->where('media_source', $mediaSource); if ($excludeId > 0) { $query->where('id', '<>', $excludeId); } - $row = $query->field('id, creator_name')->find(); - if (!$row) { - return ['conflict' => false, 'creator_name' => '']; - } - return ['conflict' => true, 'creator_name' => (string) ($row['creator_name'] ?? '')]; + return $query->count() > 0; } } diff --git a/server/app/adminapi/logic/stats/PersonalYejiLogic.php b/server/app/adminapi/logic/stats/PersonalYejiLogic.php index 2fd4fe50..170c076b 100644 --- a/server/app/adminapi/logic/stats/PersonalYejiLogic.php +++ b/server/app/adminapi/logic/stats/PersonalYejiLogic.php @@ -22,10 +22,8 @@ class PersonalYejiLogic extends BaseLogic return false; } - $conflict = self::findYejiConflict($yejiDate, $mediaSource); - if ($conflict['conflict']) { - $by = $conflict['creator_name'] !== '' ? '(录入人:' . $conflict['creator_name'] . ')' : ''; - self::setError('该日期下该渠道的业绩已存在' . $by . ',请直接编辑该记录,避免重复汇总'); + if (self::isYejiDuplicate($adminId, $yejiDate, $mediaSource)) { + self::setError('您在该日期下已录入过该渠道的业绩,请直接编辑'); return false; } diff --git a/server/sql/1.9.20260521/alter_personal_unique_key.sql b/server/sql/1.9.20260521/alter_personal_unique_key.sql index 34443ccf..08ca4da3 100644 --- a/server/sql/1.9.20260521/alter_personal_unique_key.sql +++ b/server/sql/1.9.20260521/alter_personal_unique_key.sql @@ -1,10 +1,69 @@ --- 业绩 & 账户消耗:将「同录入人+同日+同渠道」唯一改为「同日+同渠道」全局唯一,避免重复汇总。 --- 注意:执行前请先清理同一天同一渠道下多录入人重复的数据,否则索引创建会失败。 +-- 自录业绩/账户消耗:确保唯一索引为「同录入人 + 同日 + 同渠道」。 +-- 可重复执行:不存在则跳过删除;目标索引已存在则跳过添加。 +-- +-- 适用场景: +-- 1) 曾改为 uk_date_media(全局唯一)的环境 → 删除 uk_date_media,恢复 uk_creator_date_media +-- 2) 一直是 uk_creator_date_media 的环境 → 本脚本全部跳过,不会报 1091 +-- 3) 尚无唯一索引的环境 → 直接添加 uk_creator_date_media --- zyt_personal_yeji -ALTER TABLE `zyt_personal_yeji` DROP INDEX `uk_creator_date_media`; -ALTER TABLE `zyt_personal_yeji` ADD UNIQUE KEY `uk_date_media` (`yeji_date`, `media_source`); +-- ========== 查看当前索引(可选) ========== +SELECT + TABLE_NAME, + INDEX_NAME, + GROUP_CONCAT(COLUMN_NAME ORDER BY SEQ_IN_INDEX) AS COLUMNS +FROM information_schema.STATISTICS +WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME IN ('zyt_personal_yeji', 'zyt_personal_account_cost') + AND INDEX_NAME IN ('uk_date_media', 'uk_creator_date_media') +GROUP BY TABLE_NAME, INDEX_NAME +ORDER BY TABLE_NAME, INDEX_NAME; --- zyt_personal_account_cost -ALTER TABLE `zyt_personal_account_cost` DROP INDEX `uk_creator_date_media`; -ALTER TABLE `zyt_personal_account_cost` ADD UNIQUE KEY `uk_date_media` (`cost_date`, `media_source`); +-- ========== zyt_personal_yeji ========== +SET @sql = IF( + (SELECT COUNT(*) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'zyt_personal_yeji' + AND INDEX_NAME = 'uk_date_media') > 0, + 'ALTER TABLE `zyt_personal_yeji` DROP INDEX `uk_date_media`', + 'SELECT ''zyt_personal_yeji: 无 uk_date_media,跳过删除'' AS message' +); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +SET @sql = IF( + (SELECT COUNT(*) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'zyt_personal_yeji' + AND INDEX_NAME = 'uk_creator_date_media') = 0, + 'ALTER TABLE `zyt_personal_yeji` ADD UNIQUE KEY `uk_creator_date_media` (`creator_id`, `yeji_date`, `media_source`)', + 'SELECT ''zyt_personal_yeji: uk_creator_date_media 已存在,跳过添加'' AS message' +); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +-- ========== zyt_personal_account_cost ========== +SET @sql = IF( + (SELECT COUNT(*) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'zyt_personal_account_cost' + AND INDEX_NAME = 'uk_date_media') > 0, + 'ALTER TABLE `zyt_personal_account_cost` DROP INDEX `uk_date_media`', + 'SELECT ''zyt_personal_account_cost: 无 uk_date_media,跳过删除'' AS message' +); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +SET @sql = IF( + (SELECT COUNT(*) FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = 'zyt_personal_account_cost' + AND INDEX_NAME = 'uk_creator_date_media') = 0, + 'ALTER TABLE `zyt_personal_account_cost` ADD UNIQUE KEY `uk_creator_date_media` (`creator_id`, `cost_date`, `media_source`)', + 'SELECT ''zyt_personal_account_cost: uk_creator_date_media 已存在,跳过添加'' AS message' +); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; diff --git a/server/sql/1.9.20260521/create_personal_account_cost_table.sql b/server/sql/1.9.20260521/create_personal_account_cost_table.sql index a01e0797..da0b07c5 100644 --- a/server/sql/1.9.20260521/create_personal_account_cost_table.sql +++ b/server/sql/1.9.20260521/create_personal_account_cost_table.sql @@ -13,7 +13,7 @@ CREATE TABLE IF NOT EXISTS `zyt_personal_account_cost` ( `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', `delete_time` int(11) unsigned DEFAULT NULL COMMENT '删除时间', PRIMARY KEY (`id`), - UNIQUE KEY `uk_date_media` (`cost_date`, `media_source`), + UNIQUE KEY `uk_creator_date_media` (`creator_id`, `cost_date`, `media_source`), KEY `idx_cost_date` (`cost_date`), KEY `idx_creator_id` (`creator_id`), KEY `idx_dept_id` (`dept_id`) diff --git a/server/sql/1.9.20260521/create_personal_yeji_table.sql b/server/sql/1.9.20260521/create_personal_yeji_table.sql index f1194121..23a50b7c 100644 --- a/server/sql/1.9.20260521/create_personal_yeji_table.sql +++ b/server/sql/1.9.20260521/create_personal_yeji_table.sql @@ -20,7 +20,7 @@ CREATE TABLE IF NOT EXISTS `zyt_personal_yeji` ( `update_time` int(11) unsigned NOT NULL DEFAULT '0' COMMENT '更新时间', `delete_time` int(11) unsigned DEFAULT NULL COMMENT '删除时间', PRIMARY KEY (`id`), - UNIQUE KEY `uk_date_media` (`yeji_date`, `media_source`), + UNIQUE KEY `uk_creator_date_media` (`creator_id`, `yeji_date`, `media_source`), KEY `idx_yeji_date` (`yeji_date`), KEY `idx_creator_id` (`creator_id`), KEY `idx_dept_id` (`dept_id`)