20260914223013

This commit is contained in:
oneao committed 2026-09-14 22:30:13 +08:00
1 parent 549c8bd2eb
commit 63514476d7
60 files changed
+6478 -2004

No files matched your search

@@ -0,0 +1,367 @@
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* 只读探测工具,迁移期间使用:
* list 列出旧库 b_other_bmfl 全部分组与模块清单(启用、旧库行数、新库是否已建)
* tables <表名...> 输出指定表的三段信息:新库结构、旧库结构、旧库数据样例(前 3 行)
*
* 输出写入 tools/migration/ 下的文件(UTF-8 BOM,便于 Windows 记事本打开):
* list → otherdata-modules.txt
* tables → otherdata-probe-out.txt
*
* 用法(源码启动模式,不产生 .class 文件):
* cd fms-api
* java -cp "tools/migration/mssql-jdbc-13.4.0.jre11.jar" tools/migration/OtherDataProbe.java list
* java -cp "tools/migration/mssql-jdbc-13.4.0.jre11.jar" tools/migration/OtherDataProbe.java tables b_port b_money
*/
public final class OtherDataProbe {
private static final Path MIGRATION_DIR = Path.of("").toAbsolutePath().normalize().resolve("tools/migration");
public static void main(String[] args) throws Exception {
if (args.length == 0) {
usage();
return;
}
switch (args[0]) {
case "list" -> listModules();
case "columns" -> listColumns();
case "tables" -> {
if (args.length < 2) {
usage();
return;
}
probeTables(Arrays.copyOfRange(args, 1, args.length));
}
default -> usage();
}
}
private static void usage() {
System.out.println("用法:");
System.out.println(" list 列出旧库 b_other_bmfl 全部分组与模块清单");
System.out.println(" columns 导出其他数据各字典在旧库 s_columnLib 的列配置");
System.out.println(" tables b_port b_money 输出指定表的新库结构、旧库结构、旧库数据样例");
}
// ------------------------------------------------------------ list 模式
/** 旧库 b_other_bmfl 的一个模块(一条数据表登记) */
private record Module(String table, String name, boolean enabled) {}
private static void listModules() throws Exception {
Path output = MIGRATION_DIR.resolve("otherdata-modules.txt");
try (PrintStream out = newBomPrintStream(output);
Connection old = connect("G3HY2025");
Connection fms = connect("G3HD")) {
Map<String, List<Module>> groups = new LinkedHashMap<>();
try (Statement statement = old.createStatement();
ResultSet rows = statement.executeQuery("""
select isnull(nullif(b_groupby, ''), '(未分组)') as grp,
ltrim(rtrim(b_tablename)) as tbl,
isnull(mc_b, '') as mc,
isnull(b_canuse, '') as canuse
from G3HY2025.dbo.b_other_bmfl
where b_tablename is not null and ltrim(rtrim(b_tablename)) != ''
order by grp, b_xh, mc
""")) {
while (rows.next()) {
groups.computeIfAbsent(rows.getString("grp"), key -> new ArrayList<>())
.add(new Module(rows.getString("tbl"), rows.getString("mc"),
"1".equals(rows.getString("canuse"))));
}
}
Set<String> fmsObjects = fmsObjectNames(fms);
out.println("=== 旧库 b_other_bmfl 模块清单 ===");
out.println(" 表名" + pad("", 22) + "名称" + pad("", 20) + "状态 旧库行数 新库");
int total = 0;
int totalEnabled = 0;
int totalWithData = 0;
int totalBuilt = 0;
int groupIndex = 0;
for (Map.Entry<String, List<Module>> entry : groups.entrySet()) {
groupIndex++;
int enabled = 0;
int withData = 0;
int built = 0;
StringBuilder body = new StringBuilder();
for (Module module : entry.getValue()) {
long oldRows = countRows(old, module.table());
boolean exists = existsInFms(fmsObjects, module.table());
if (module.enabled()) {
enabled++;
}
if (oldRows > 0) {
withData++;
}
if (exists) {
built++;
}
body.append(" ")
.append(pad(module.table(), 26))
.append(pad(module.name(), 24))
.append(pad(module.enabled() ? "启用" : "停用", 6))
.append(pad(oldRows < 0 ? "-" : String.valueOf(oldRows), 10))
.append(exists ? "已建" : "-")
.append(System.lineSeparator());
}
total += entry.getValue().size();
totalEnabled += enabled;
totalWithData += withData;
totalBuilt += built;
out.println();
out.println("[" + groupIndex + "] " + entry.getKey() + " —— " + entry.getValue().size()
+ " 张(启用 " + enabled + "、有数据 " + withData + "、新库已建 " + built + ")");
out.print(body);
}
out.println();
out.println("合计 " + total + " 张(启用 " + totalEnabled + "、有数据 " + totalWithData
+ "、新库已建 " + totalBuilt + ")");
}
System.out.println("输出已写入: " + output);
}
private static long countRows(Connection old, String table) {
for (String name : List.of(table, "b_" + table, "v_" + table)) {
try (Statement statement = old.createStatement();
ResultSet rows = statement.executeQuery(
"select count(*) from dbo.[" + name.replace("]", "]]") + "]")) {
rows.next();
return rows.getLong(1);
} catch (Exception ignored) {
// 试下一个候选名
}
}
return -1;
}
private static Set<String> fmsObjectNames(Connection fms) throws Exception {
Set<String> names = new HashSet<>();
try (Statement statement = fms.createStatement();
ResultSet rows = statement.executeQuery(
"select name from sys.objects where type in ('U', 'V')")) {
while (rows.next()) {
names.add(rows.getString(1).toLowerCase(Locale.ROOT));
}
}
return names;
}
private static boolean existsInFms(Set<String> fmsObjects, String table) {
return fmsObjects.contains(table.toLowerCase(Locale.ROOT))
|| fmsObjects.contains(("v_" + table).toLowerCase(Locale.ROOT));
}
// --------------------------------------------------------- columns 模式
/** 导出 base_otherdata 各字典表在旧库 s_columnLib 的列配置(列表列 / 表单字段) */
private static void listColumns() throws Exception {
Path output = MIGRATION_DIR.resolve("otherdata-columns.txt");
try (PrintStream out = newBomPrintStream(output);
Connection fms = connect("G3HD")) {
List<String[]> modules = new ArrayList<>();
try (Statement statement = fms.createStatement();
ResultSet rows = statement.executeQuery(
"select b_save_table, b_name from dbo.s_module "
+ "where b_path like '/base/base_otherdata/%' and b_module_type = 'data' "
+ "order by b_xh, b_id")) {
while (rows.next()) {
modules.add(new String[] {rows.getString(1), rows.getString(2)});
}
}
out.println("=== 旧库 s_columnLib 配置(base_otherdata 各字典) ===");
out.println(" 列表=col_Visible(1 进列表) 表单=col_edit(1 进表单) 宽=col_Width 位置=col_Position 表单位置=col_edit_position");
int total = 0;
int inList = 0;
int inEdit = 0;
for (String[] module : modules) {
String table = module[0];
out.println();
out.println("--- " + table + "(" + module[1] + ")---");
int count = 0;
try (PreparedStatement statement = fms.prepareStatement(
"select col_FieldName, col_Caption, col_Visible, col_edit, col_Width, col_Position, col_edit_position "
+ "from G3HY2025.dbo.s_columnLib where col_TableName = ? "
+ "order by isnull(col_Position, 9999), col_FieldName")) {
statement.setString(1, table);
try (ResultSet rows = statement.executeQuery()) {
while (rows.next()) {
count++;
String visible = text(rows.getString(3));
String edit = text(rows.getString(4));
Object width = rows.getObject(5);
Object position = rows.getObject(6);
Object editPosition = rows.getObject(7);
if ("1".equals(visible)) {
inList++;
}
if ("1".equals(edit)) {
inEdit++;
}
out.println(" " + pad(rows.getString(1), 24) + pad(rows.getString(2), 20)
+ "列表=" + pad(visible, 5) + "表单=" + pad(edit, 5)
+ "宽=" + pad(width == null ? "-" : String.valueOf(width), 7)
+ "位置=" + pad(position == null ? "-" : String.valueOf(position), 7)
+ "表单位置=" + (editPosition == null ? "-" : String.valueOf(editPosition)));
}
}
}
total += count;
if (count == 0) {
out.println(" (旧库无配置)");
}
}
out.println();
out.println("合计: " + modules.size() + " 个模块、" + total + " 条字段配置(进列表 " + inList
+ "、进表单 " + inEdit + ")");
}
System.out.println("输出已写入: " + output);
}
private static String text(String value) {
return value == null ? "" : value.trim();
}
// ---------------------------------------------------------- tables 模式
private static void probeTables(String[] tables) throws Exception {
Path output = MIGRATION_DIR.resolve("otherdata-probe-out.txt");
try (PrintStream out = newBomPrintStream(output);
Connection old = connect("G3HY2025");
Connection fms = connect("G3HD")) {
out.println("=== 新库 FMS:表结构 ===");
for (String table : tables) {
out.println();
out.println("--- " + table + " ---");
columns(fms, out, table);
}
out.println();
out.println("=== 旧库 G3HY2025:表结构 ===");
for (String table : tables) {
out.println();
out.println("--- " + table + " ---");
columns(old, out, table);
}
out.println();
out.println("=== 旧库 G3HY2025:数据样例(每表前 3 行) ===");
for (String table : tables) {
out.println();
out.println("--- " + table + " ---");
query(old, out, "select top 3 * from dbo.[" + table.replace("]", "]]") + "]");
}
}
System.out.println("输出已写入: " + output);
}
private static void columns(Connection connection, PrintStream out, String table) {
try (Statement statement = connection.createStatement();
ResultSet rows = statement.executeQuery(
"select name, type_name(user_type_id) as t, max_length, is_nullable from sys.columns "
+ "where object_id = object_id('dbo.[" + table.replace("]", "]]") + "]') order by column_id")) {
int count = 0;
while (rows.next()) {
count++;
out.println(" " + pad(rows.getString(1), 24) + pad(rows.getString(2), 14)
+ pad(String.valueOf(rows.getInt(3)), 8) + (rows.getBoolean(4) ? "null" : "not null"));
}
if (count == 0) {
out.println(" (表不存在)");
}
} catch (Exception e) {
out.println(" 读取失败: " + e.getMessage());
}
}
private static void query(Connection connection, PrintStream out, String sql) {
try (Statement statement = connection.createStatement();
ResultSet rows = statement.executeQuery(sql)) {
ResultSetMetaData meta = rows.getMetaData();
StringBuilder header = new StringBuilder(" ");
for (int i = 1; i <= meta.getColumnCount(); i++) {
header.append(pad(meta.getColumnLabel(i), 20));
}
out.println(header.toString().stripTrailing());
int count = 0;
while (rows.next()) {
count++;
StringBuilder line = new StringBuilder(" ");
for (int i = 1; i <= meta.getColumnCount(); i++) {
Object value = rows.getObject(i);
String text = value == null ? "(null)" : String.valueOf(value).replaceAll("\\s+", " ");
if (text.length() > 60) {
text = text.substring(0, 60) + "…";
}
line.append(pad(text, 20));
}
out.println(line.toString().stripTrailing());
}
out.println(" (" + count + " 行)");
} catch (Exception e) {
out.println(" 查询失败: " + e.getMessage());
}
}
// -------------------------------------------------------------- 基础设施
private static PrintStream newBomPrintStream(Path output) throws Exception {
FileOutputStream file = new FileOutputStream(output.toFile());
file.write(0xEF);
file.write(0xBB);
file.write(0xBF);
return new PrintStream(file, true, StandardCharsets.UTF_8);
}
private static Connection connect(String org) throws Exception {
Path apiRoot = Path.of("").toAbsolutePath().normalize();
Properties properties = new Properties();
try (InputStream input = Files.newInputStream(apiRoot.resolve("config/dbconfigs/" + org + ".properties"))) {
properties.load(input);
}
return DriverManager.getConnection(
properties.getProperty("url"),
properties.getProperty("username"),
properties.getProperty("password"));
}
private static String pad(String value, int width) {
String text = value == null ? "" : value;
int display = text.length();
while (display < width) {
text += " ";
display++;
}
return text;
}
}