smile 1 viikko sitten
vanhempi
commit
e9cca44f7c
3 muutettua tiedostoa jossa 769 lisäystä ja 419 poistoa
  1. 569 409
      Config
  2. 199 9
      GameConfig/gen_pojo_from_xls_json.js
  3. 1 1
      protocol/buildjava.bat

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 569 - 409
Config


+ 199 - 9
GameConfig/gen_pojo_from_xls_json.js

@@ -2,9 +2,21 @@
 const fs = require('fs');
 const path = require('path');
 
+// 尝试加载xlsx模块,如果不存在则提供友好的错误信息
+// 注意:xlsx模块支持读取.xls格式文件
+let xlsx;
+try {
+	xlsx = require('xlsx');
+} catch (e) {
+	console.error('Error: xlsx module not found. Please install it with: npm install xlsx');
+	console.error('Note: This script only supports .xls format files, not .xlsx');
+	process.exit(1);
+}
+
 // Usage:
 //   node gen_pojo_from_xls_json.js <sourceJsonDir> <sourceXlsDir> <packageName> <outputDir> [Tables...] [--ignore=a,b,c]
 // Tables can be names with or without .json/.xls, case-insensitive. Multiple can be space- or comma-separated.
+// Note: Only .xls format is supported for Excel files, not .xlsx
 const [,, jsonDir, xlsDir, packageName, outputDir, ...rest] = process.argv;
 
 if (!jsonDir || !xlsDir || !packageName || !outputDir) {
@@ -59,6 +71,17 @@ function pickSample(root) {
 	return null;
 }
 
+function analyzeAllSamples(root) {
+	if (Array.isArray(root)) {
+		return root; // 分析所有样本
+	}
+	if (root && typeof root === 'object') {
+		const keys = Object.keys(root);
+		return keys.map(k => root[k]); // 分析所有样本
+	}
+	return [];
+}
+
 function upperFirst(str) {
 	if (!str) return str;
 	return str.charAt(0).toUpperCase() + str.slice(1);
@@ -74,9 +97,35 @@ function isInteger(num) {
 	return typeof num === 'number' && Number.isInteger(num);
 }
 
+function isLong(num) {
+	return typeof num === 'number' && Number.isInteger(num) && (num > 2147483647 || num < -2147483648);
+}
+
+function isFloat(num) {
+	return typeof num === 'number' && !Number.isInteger(num);
+}
+
+// 检查字符串是否包含小数点(用于识别BigDecimal类型)
+function hasDecimalPoint(str) {
+	return typeof str === 'string' && str.includes('.') && !isNaN(Number(str));
+}
+
+// 检查字符串是否为有效的数字格式
+function isValidNumberString(str) {
+	if (typeof str !== 'string') return false;
+	const trimmed = str.trim();
+	if (trimmed === '') return false;
+	return !isNaN(Number(trimmed)) && isFinite(Number(trimmed));
+}
+
 function inferJavaType(value) {
 	if (value === null || value === undefined) return 'String';
-	if (typeof value === 'number') return isInteger(value) ? 'Integer' : 'BigDecimal';
+	if (typeof value === 'number') {
+		if (isLong(value)) return 'Long';
+		if (isInteger(value)) return 'Integer';
+		if (isFloat(value)) return 'BigDecimal';
+		return 'BigDecimal';
+	}
 	if (typeof value === 'boolean') return 'Boolean';
 	if (typeof value === 'string') return 'String';
 	if (Array.isArray(value)) {
@@ -87,6 +136,129 @@ function inferJavaType(value) {
 	return 'String';
 }
 
+function inferJavaTypeFromSamples(samples, fieldName) {
+	if (samples.length === 0) return 'String';
+	
+	// 统计所有样本的类型
+	const typeStats = {
+		null: 0,
+		undefined: 0,
+		number: 0,
+		boolean: 0,
+		string: 0,
+		array: 0,
+		object: 0
+	};
+	
+	const numberValues = [];
+	const stringValues = [];
+	const arrayValues = [];
+	
+	for (const sample of samples) {
+		if (sample === null) {
+			typeStats.null++;
+		} else if (sample === undefined) {
+			typeStats.undefined++;
+		} else if (typeof sample === 'number') {
+			typeStats.number++;
+			numberValues.push(sample);
+		} else if (typeof sample === 'boolean') {
+			typeStats.boolean++;
+		} else if (typeof sample === 'string') {
+			typeStats.string++;
+			stringValues.push(sample);
+		} else if (Array.isArray(sample)) {
+			typeStats.array++;
+			arrayValues.push(sample);
+		} else if (typeof sample === 'object') {
+			typeStats.object++;
+		}
+	}
+	
+	// 找出主要类型
+	const total = samples.length;
+	const maxType = Object.keys(typeStats).reduce((a, b) => typeStats[a] > typeStats[b] ? a : b);
+	const maxRatio = typeStats[maxType] / total;
+	
+	// 如果主要类型占比超过80%,使用该类型
+	if (maxRatio >= 0.8) {
+		switch (maxType) {
+			case 'number':
+				if (numberValues.length === 0) return 'Integer';
+				// 检查是否需要Long类型
+				const hasLong = numberValues.some(n => isLong(n));
+				const hasFloat = numberValues.some(n => isFloat(n));
+				if (hasLong) return 'Long';
+				if (hasFloat) return 'BigDecimal';
+				return 'Integer';
+			case 'boolean':
+				return 'Boolean';
+			case 'string':
+				// 检查字符串是否都是数字格式
+				const allNumeric = stringValues.every(s => isValidNumberString(s));
+				if (allNumeric && stringValues.length > 0) {
+					const allNumbers = stringValues.map(s => Number(s));
+					const hasLong = allNumbers.some(n => isLong(n));
+					const hasFloat = allNumbers.some(n => isFloat(n));
+					// 检查原始字符串是否包含小数点
+					const hasDecimalPointInStrings = stringValues.some(s => hasDecimalPoint(s));
+					if (hasLong) return 'Long';
+					if (hasFloat || hasDecimalPointInStrings) return 'BigDecimal';
+					return 'Integer';
+				}
+				return 'String';
+			case 'array':
+				if (arrayValues.length === 0) return 'List<Object>';
+				// 分析数组元素类型
+				const allArrayElements = [];
+				for (const arr of arrayValues) {
+					allArrayElements.push(...arr.slice(0, 3)); // 取前3个元素
+				}
+				if (allArrayElements.length === 0) return 'List<Object>';
+				const elementType = inferJavaTypeFromSamples(allArrayElements, fieldName + '_element');
+				return `List<${elementType}>`;
+			case 'object':
+				return 'Object';
+			default:
+				return 'String';
+		}
+	}
+	
+	// 混合类型,优先选择数字类型(可能是字符串形式的数字)
+	if (typeStats.number > 0 && typeStats.string > 0) {
+		// 检查字符串是否都是有效数字
+		const allNumeric = stringValues.every(s => isValidNumberString(s));
+		if (allNumeric) {
+			const allNumbers = [...numberValues, ...stringValues.map(s => Number(s))];
+			const hasLong = allNumbers.some(n => isLong(n));
+			const hasFloat = allNumbers.some(n => isFloat(n));
+			// 检查原始字符串是否包含小数点
+			const hasDecimalPointInStrings = stringValues.some(s => hasDecimalPoint(s));
+			if (hasLong) return 'Long';
+			if (hasFloat || hasDecimalPointInStrings) return 'BigDecimal';
+			return 'Integer';
+		}
+	}
+	
+	// 如果只有字符串类型,检查是否都是数字格式
+	if (typeStats.string > 0 && typeStats.number === 0) {
+		const allNumeric = stringValues.every(s => isValidNumberString(s));
+		if (allNumeric && stringValues.length > 0) {
+			const allNumbers = stringValues.map(s => Number(s));
+			const hasLong = allNumbers.some(n => isLong(n));
+			const hasFloat = allNumbers.some(n => isFloat(n));
+			// 检查原始字符串是否包含小数点
+			const hasDecimalPointInStrings = stringValues.some(s => hasDecimalPoint(s));
+			if (hasLong) return 'Long';
+			if (hasFloat || hasDecimalPointInStrings) return 'BigDecimal';
+			return 'Integer';
+		}
+	}
+	
+	// 默认返回String
+	return 'String';
+}
+
 const JAVA_KEYWORDS = new Set([
 	'abstract','assert','boolean','break','byte','case','catch','char','class','const','continue','default','do','double','else','enum','extends','final','finally','float','for','goto','if','implements','import','instanceof','int','interface','long','native','new','package','private','protected','public','return','short','static','strictfp','super','switch','synchronized','this','throw','throws','transient','try','void','volatile','while','null','true','false'
 ]);
@@ -146,13 +318,12 @@ function readXlsComments(xlsFile) {
 }
 
 function findXlsForBase(xlsDir, baseName) {
-	// Try .xls then .xlsx
-	const cands = [path.join(xlsDir, baseName + '.xls'), path.join(xlsDir, baseName + '.xlsx')];
-	for (const f of cands) {
-		if (fs.existsSync(f)) return f;
-	}
-	// Case-insensitive search fallback
-	const files = fs.readdirSync(xlsDir).filter(n => /\.xlsx?$/.test(n));
+	// Only try .xls format
+	const xlsFile = path.join(xlsDir, baseName + '.xls');
+	if (fs.existsSync(xlsFile)) return xlsFile;
+	
+	// Case-insensitive search fallback for .xls files only
+	const files = fs.readdirSync(xlsDir).filter(n => /\.xls$/i.test(n));
 	for (const n of files) {
 		if (normalizeName(n) === normalizeName(baseName)) return path.join(xlsDir, n);
 	}
@@ -178,6 +349,8 @@ function generateJavaClass(pkg, className, fieldsOrder, fieldTypes, fieldComment
 		} else if (type === 'List<BigDecimal>') {
 			usedTypes.add('java.util.List');
 			usedTypes.add('java.math.BigDecimal');
+		} else if (type === 'List<Long>') {
+			usedTypes.add('java.util.List');
 		} else if (type === 'BigDecimal') {
 			usedTypes.add('java.math.BigDecimal');
 		}
@@ -255,12 +428,29 @@ function generateJavaClass(pkg, className, fieldsOrder, fieldTypes, fieldComment
 			const jsonRoot = readJson(jf);
 			const sample = pickSample(jsonRoot);
 			if (!sample || typeof sample !== 'object') { console.warn('Skip (no object sample):', jf); continue; }
+			
+			// 分析所有样本以获得更准确的类型推断
+			const allSamples = analyzeAllSamples(jsonRoot);
+			if (allSamples.length === 0) { console.warn('Skip (no samples):', jf); continue; }
+			
 			const xlsPath = findXlsForBase(xlsDir, base);
 			const { sheetName, comments } = xlsPath ? readXlsComments(xlsPath) : { sheetName: '', comments: new Map() };
 			const className = toClassName(base) + 'Pojo';
 			const fieldsOrder = Object.keys(sample);
 			const fieldTypes = new Map();
-			for (const k of fieldsOrder) fieldTypes.set(k, inferJavaType(sample[k]));
+			
+			// 使用多样本分析进行类型推断
+			for (const fieldName of fieldsOrder) {
+				const fieldSamples = allSamples.map(s => s[fieldName]).filter(v => v !== undefined);
+				const inferredType = inferJavaTypeFromSamples(fieldSamples, fieldName);
+				fieldTypes.set(fieldName, inferredType);
+				
+				// 调试输出
+				if (fieldSamples.length > 0) {
+					console.log(`  ${fieldName}: ${inferredType} (samples: ${fieldSamples.length}, types: ${[...new Set(fieldSamples.map(v => typeof v))].join(', ')})`);
+				}
+			}
+			
 			const classComment = sheetName ? `${base}(${sheetName})` : `${base}`;
 			const jsonFileName = path.basename(jf);
 			const content = generateJavaClass(packageName, className, fieldsOrder, fieldTypes, comments, classComment, jsonFileName);

+ 1 - 1
protocol/buildjava.bat

@@ -42,7 +42,7 @@ REM Proto项目目录 - 用于将proto文件拷贝到独立的目标目录 (如
 set DEFAULT_PROTO_PROJECT_DIR=D:\project\game-framework\game-battle\src\main\resources\proto
 
 REM 拷贝到项目功能 - 是否启用自动拷贝到项目目录功能 (true/false)
-set DEFAULT_COPY_TO_PROJECT=true
+set DEFAULT_COPY_TO_PROJECT=false
 
 REM 包含文件模式 - 指定要处理的proto文件模式,支持通配符 (如: *.proto, battle_*.proto)
 REM set DEFAULT_INCLUDE_PROTO_FILES=*.proto

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä