first commit

This commit is contained in:
jakciehan
2026-04-10 22:59:39 +08:00
commit cc2e7b9bb0
89 changed files with 23631 additions and 0 deletions
+227
View File
@@ -0,0 +1,227 @@
/**
* ComplianceManager.js
* Manages underage protection, probability disclosure, and anti-cheat measures.
* Ensures compliance with Chinese gaming regulations and WeChat platform rules.
*/
class ComplianceManager {
constructor() {
this._isMinor = false;
this._monthlySpending = 0;
this._dailyAdCount = 0;
this._trackingDate = '';
this._load();
this._checkMinorStatus();
}
// ============================================================
// Persistence
// ============================================================
/** @private */
_getTodayKey() {
const d = new Date();
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
}
/** @private */
_getMonthKey() {
const d = new Date();
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`;
}
/** @private */
_load() {
try {
if (GameGlobal && GameGlobal.storageManager) {
const data = GameGlobal.storageManager.get('compliance', null);
if (data) {
this._isMinor = data.isMinor || false;
// Monthly spending
const currentMonth = this._getMonthKey();
if (data.spendingMonth === currentMonth) {
this._monthlySpending = data.monthlySpending || 0;
}
// Daily ad count
const today = this._getTodayKey();
if (data.adDate === today) {
this._dailyAdCount = data.dailyAdCount || 0;
}
this._trackingDate = today;
}
}
} catch (e) {
console.warn('[ComplianceManager] Failed to load:', e);
}
}
/** @private */
_save() {
try {
if (GameGlobal && GameGlobal.storageManager) {
GameGlobal.storageManager.set('compliance', {
isMinor: this._isMinor,
spendingMonth: this._getMonthKey(),
monthlySpending: this._monthlySpending,
adDate: this._getTodayKey(),
dailyAdCount: this._dailyAdCount,
});
}
} catch (e) {}
}
// ============================================================
// Minor Status Detection
// ============================================================
/**
* Check if the user is a minor via WeChat platform API.
* @private
*/
_checkMinorStatus() {
// WeChat provides user age info through specific APIs
// In production, this would call wx.getUserInfo or a server-side check
// For now, default to non-minor
try {
if (typeof wx !== 'undefined' && typeof wx.getSetting === 'function') {
// Placeholder: in production, check real-name verification status
console.log('[ComplianceManager] Minor status check: defaulting to adult');
}
} catch (e) {}
}
// ============================================================
// Public API
// ============================================================
/**
* Whether the current user is identified as a minor.
* @returns {boolean}
*/
isMinor() {
return this._isMinor;
}
/**
* Set minor status (called after real-name verification).
* @param {boolean} isMinor
*/
setMinorStatus(isMinor) {
this._isMinor = isMinor;
this._save();
}
/**
* Check if a purchase is allowed for the current user.
* Minors: monthly limit ¥400, single purchase > ¥50 requires confirmation.
* @param {number} amountFen - Purchase amount in fen (分).
* @returns {{ allowed: boolean, needsConfirmation: boolean, reason?: string }}
*/
checkPurchaseRestriction(amountFen) {
if (!this._isMinor) {
return { allowed: true, needsConfirmation: false };
}
const amountYuan = amountFen / 100;
// Monthly limit: ¥400
const newTotal = this._monthlySpending + amountFen;
if (newTotal > 40000) { // 400 yuan in fen
return {
allowed: false,
needsConfirmation: false,
reason: 'monthly_limit',
};
}
// Single purchase > ¥50 needs confirmation
if (amountYuan > 50) {
return {
allowed: true,
needsConfirmation: true,
reason: 'large_purchase',
};
}
return { allowed: true, needsConfirmation: false };
}
/**
* Record a successful purchase amount.
* @param {number} amountFen
*/
recordPurchase(amountFen) {
this._monthlySpending += amountFen;
this._save();
}
/**
* Check if an ad can be shown to the current user.
* Minors: max 5 ads per day.
* @returns {boolean}
*/
canShowAd() {
if (!this._isMinor) return true;
const today = this._getTodayKey();
if (this._trackingDate !== today) {
this._trackingDate = today;
this._dailyAdCount = 0;
}
return this._dailyAdCount < 5;
}
/**
* Record an ad shown to the user.
*/
recordAdShown() {
if (!this._isMinor) return;
const today = this._getTodayKey();
if (this._trackingDate !== today) {
this._trackingDate = today;
this._dailyAdCount = 0;
}
this._dailyAdCount++;
this._save();
}
/**
* Validate game session data for anti-cheat.
* Checks for impossible stats (e.g., too many kills in too short time).
* @param {object} stats - { kills, timeElapsed, score }
* @returns {{ valid: boolean, flags: string[] }}
*/
validateGameSession(stats) {
const flags = [];
if (!stats) return { valid: true, flags };
// Check impossible kill rate (>10 kills per minute)
if (stats.kills && stats.timeElapsed) {
const killsPerMinute = stats.kills / (stats.timeElapsed / 60);
if (killsPerMinute > 10) {
flags.push('suspicious_kill_rate');
}
}
// Check impossible score
if (stats.score && stats.score > 100000) {
flags.push('suspicious_score');
}
// Check suspicious ad reward frequency
// (anti-cheat for ad reward manipulation)
return {
valid: flags.length === 0,
flags,
};
}
}
module.exports = ComplianceManager;