T14 数据迁移工具 S1/S2 阶段: S1 基础设施: - source_app/migrate_task 建表 + 实体 + Mapper - TencentImClient 双密钥 callApiAs(老应用拉取,不走主应用限流,审计记 migrate) - 后台「老应用配置」页(CRUD + 测试连通) S2 用户迁移: - batchImport(multiaccount_import,每批≤100) - MigrateService.migrateUsers(前缀转换+导入+映射+校验,幂等) - 后台「数据迁移」页(上传清单文件/粘贴列表 + 任务记录) 联调通过:3用户清单→加sa前缀→腾讯导入OK→user_mapping→校验3/3/3。 Co-Authored-By: Claude <noreply@anthropic.com>
95 lines
4.6 KiB
Plaintext
95 lines
4.6 KiB
Plaintext
<#import "_macros.ftl" as m>
|
||
<@m.layout active="sourceapp" title="老应用配置">
|
||
<#if msg?has_content>
|
||
<div class="alert">
|
||
<#if msg=='saved'>保存成功
|
||
<#elseif msg=='toggled'>状态已切换
|
||
<#elseif msg=='test_ok'>✅ 连通成功(SDKAppId/密钥/sig 正确)
|
||
<#elseif msg=='test_sigfail'>❌ 连通失败:UserSig 错误(70003,SDKAppId/密钥不匹配)
|
||
<#elseif msg=='test_fail'>❌ 连通失败(详见日志)
|
||
<#elseif msg=='sdkappid_required'>SDKAppID 必填
|
||
<#elseif msg=='tenant_required'>对应租户必填
|
||
<#elseif msg=='notfound'>老应用不存在
|
||
<#else>${msg}
|
||
</#if>
|
||
</div>
|
||
</#if>
|
||
<p class="muted">数据迁移源:每个老应用对应主应用一个租户(决定 UserID 前缀)。迁移时用其 SDKAppID/密钥调腾讯 API 拉取存量数据,加前缀后导入主应用。密钥仅本工具持有。</p>
|
||
<div style="margin-bottom:14px">
|
||
<button class="btn btn-success" onclick="document.getElementById('addModal').classList.add('show')">➕ 新增老应用</button>
|
||
</div>
|
||
<#if apps?has_content>
|
||
<table class="data-table">
|
||
<thead>
|
||
<tr>
|
||
<th>ID</th><th>应用名称</th><th>SDKAppID</th><th>密钥</th><th>对应租户</th>
|
||
<th>状态</th><th>操作</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<#list apps as a>
|
||
<tr>
|
||
<td>${a.id!}</td>
|
||
<td>${a.appName!}</td>
|
||
<td>${a.sdkAppId!}</td>
|
||
<td class="muted">••••••••</td>
|
||
<td>${a.tenantId!}</td>
|
||
<td>
|
||
<#if a.status?? && a.status == 1>
|
||
<span class="badge badge-ok">启用</span>
|
||
<#else>
|
||
<span class="badge badge-no">停用</span>
|
||
</#if>
|
||
</td>
|
||
<td>
|
||
<form method="post" action="${basePath}/admin/sourceapp/toggle" style="display:inline">
|
||
<input type="hidden" name="id" value="${a.id!}">
|
||
<#if a.status?? && a.status == 1>
|
||
<input type="hidden" name="status" value="0">
|
||
<button class="btn btn-sm btn-secondary">停用</button>
|
||
<#else>
|
||
<input type="hidden" name="status" value="1">
|
||
<button class="btn btn-sm btn-success">启用</button>
|
||
</#if>
|
||
</form>
|
||
<form method="post" action="${basePath}/admin/sourceapp/test" style="display:inline">
|
||
<input type="hidden" name="id" value="${a.id!}">
|
||
<button class="btn btn-sm btn-primary">测试连通</button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
</#list>
|
||
</tbody>
|
||
</table>
|
||
<#else>
|
||
<div class="empty">暂无老应用配置,点击「新增老应用」添加</div>
|
||
</#if>
|
||
|
||
<div id="addModal" class="modal" onclick="if(event.target===this)this.classList.remove('show')">
|
||
<div class="modal-content">
|
||
<div class="modal-title">新增老应用</div>
|
||
<form method="post" action="${basePath}/admin/sourceapp/save">
|
||
<div class="modal-body">
|
||
<div class="form-group"><label>应用名称</label><input name="appName" required></div>
|
||
<div class="form-group"><label>SDKAppID(老应用)</label><input name="sdkAppId" required></div>
|
||
<div class="form-group"><label>SecretKey(老应用密钥)</label><input name="secretKey" required></div>
|
||
<div class="form-group">
|
||
<label>对应租户(决定 UserID 前缀)</label>
|
||
<select name="tenantId" required>
|
||
<option value="">请选择租户</option>
|
||
<#list tenants as t>
|
||
<option value="${t.tenantId!}">${t.tenantId!}(${t.tenantName!},前缀 ${t.prefixCode!})</option>
|
||
</#list>
|
||
</select>
|
||
</div>
|
||
<div class="form-group"><label>备注</label><input name="remark" placeholder="如:原XX系统IM应用"></div>
|
||
</div>
|
||
<div class="modal-footer">
|
||
<button type="button" class="btn btn-secondary" onclick="document.getElementById('addModal').classList.remove('show')">取消</button>
|
||
<button type="submit" class="btn btn-primary">保存</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</@m.layout>
|