Files
cunkebao_v3/Server/script/migration/fix_user_company.php
Manus AI 5517457929 sync: 以本地为准同步全量变更至 GitHub
含四端需求文档、前端/后端/超管/触客宝迭代及部署脚本更新;未拉取远程。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-30 04:24:56 +08:00

155 lines
4.9 KiB
PHP
Raw Permalink 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.

<?php
/**
* 存客宝 · 单用户公司归属修复(按手机号迁移到目标 companyId
*
* 用途13779954946 应从卡若(914)迁到黑科技/老坑爹/魔兽(2130)15880802661 保持 914。
* 只迁移该 userId 名下的计划、内容库、算力账户,不影响同公司其他用户。
*
* 用法:
* docker compose exec -T server php /var/www/html/script/migration/fix_user_company.php \
* phone=13779954946 toCompany=2130
*
* docker compose exec -T server php /var/www/html/script/migration/fix_user_company.php \
* phone=13779954946 toCompany=2130 apply=1
*/
declare(strict_types=1);
require __DIR__ . '/../../thinkphp/base.php';
use think\Db;
use think\App;
$app = new App(realpath(__DIR__ . '/../../application'));
$app->initialize();
$args = [];
foreach ($argv as $arg) {
if (strpos($arg, '=') !== false) {
list($k, $v) = explode('=', $arg, 2);
$args[trim($k)] = trim($v);
}
}
$phone = $args['phone'] ?? '';
$toCompany = (int)($args['toCompany'] ?? 0);
$apply = !empty($args['apply']);
if (!$phone || !$toCompany) {
fwrite(STDERR, "usage: phone=1XXXXXXXXXX toCompany=2130 [apply=1]\n");
exit(1);
}
$user = Db::name('users')
->where('phone', $phone)
->whereOr('account', $phone)
->find();
if (!$user) {
fwrite(STDERR, "未找到手机号 {$phone} 对应用户\n");
exit(1);
}
$userId = (int)$user['id'];
$fromCompany = (int)$user['companyId'];
if ($fromCompany === $toCompany) {
echo "用户 {$phone} (id={$userId}) 已在 companyId={$toCompany},无需迁移。\n";
exit(0);
}
$mode = $apply ? '【APPLY】' : '【DRY-RUN】';
echo "{$mode} phone={$phone} userId={$userId} username={$user['username']} "
. "companyId {$fromCompany}{$toCompany}\n";
$fromName = Db::name('company')->where('id', $fromCompany)->value('name') ?: '?';
$toName = Db::name('company')->where('id', $toCompany)->value('name') ?: '?';
echo " 源公司: {$fromName} ({$fromCompany})\n";
echo " 目标公司: {$toName} ({$toCompany})\n";
$plan = [];
Db::startTrans();
try {
// 1) 用户主表
$plan['users'] = 1;
echo " · ck_users: 1 行\n";
if ($apply) {
Db::name('users')->where('id', $userId)->update([
'companyId' => $toCompany,
'updateTime' => time(),
]);
}
// 2) 按 userId 迁移的业务表
$userScoped = [
['customer_acquisition_task', 'userId', 'companyId', '获客计划'],
['content_library', 'userId', 'companyId', '内容库'],
['tokens_company', 'userId', 'companyId', '算力账户'],
];
foreach ($userScoped as [$table, $userField, $companyField, $desc]) {
try {
$count = (int)Db::name($table)
->where($userField, $userId)
->where($companyField, $fromCompany)
->count();
$plan[$table] = $count;
echo " · {$table} ({$desc}): {$count}\n";
if ($apply && $count > 0) {
Db::name($table)
->where($userField, $userId)
->where($companyField, $fromCompany)
->update([$companyField => $toCompany]);
}
} catch (\Throwable $e) {
echo " · [SKIP] {$table}: {$e->getMessage()}\n";
}
}
// 3) 该用户名下、仍挂在旧公司的内容库素材libraryId 归属已迁则自动正确)
try {
$libIds = Db::name('content_library')->where('userId', $userId)->column('id');
if ($libIds) {
// 无 companyId 字段,仅统计
$itemCount = (int)Db::name('content_item')
->whereIn('libraryId', $libIds)
->where('isDel', 0)
->count();
$plan['content_item'] = $itemCount;
echo " · content_item (素材,随内容库): {$itemCount}\n";
}
} catch (\Throwable $e) {
echo " · [SKIP] content_item: {$e->getMessage()}\n";
}
if ($apply) {
Db::commit();
echo "\n✅ 迁移完成\n";
} else {
Db::rollback();
echo "\n[DRY-RUN] 未写入。确认后加 apply=1 重跑。\n";
}
} catch (\Throwable $e) {
Db::rollback();
fwrite(STDERR, "失败回滚:{$e->getMessage()}\n");
exit(2);
}
$reportDir = dirname(__DIR__, 2) . '/runtime/migration/' . $phone;
if (!is_dir($reportDir)) {
@mkdir($reportDir, 0755, true);
}
$reportPath = $reportDir . '/fix-user-company-' . ($apply ? 'apply' : 'dryrun') . '-' . date('YmdHis') . '.json';
file_put_contents($reportPath, json_encode([
'phone' => $phone,
'userId' => $userId,
'fromCompany' => $fromCompany,
'toCompany' => $toCompany,
'fromName' => $fromName,
'toName' => $toName,
'mode' => $apply ? 'apply' : 'dry-run',
'plan' => $plan,
'finishedAt' => date('c'),
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
echo "report: {$reportPath}\n";