'integer', 'update_time' => 'integer', 'locked_points' => 'integer', 'available_points' => 'integer', 'today_limit' => 'integer', 'today_claimed' => 'integer', 'admin_id' => 'integer', ]; /** * H5 临时登录:按用户名查找或创建资产行,playx_user_id 使用 mall_{id} */ public static function ensureForUsername(string $username): self { $username = trim($username); $existing = self::where('username', $username)->find(); if ($existing) { return $existing; } $phone = self::allocateUniquePhone(); if ($phone === null) { throw new \RuntimeException('Failed to allocate unique phone'); } $pwd = hash_password(Random::build('alnum', 16)); $now = time(); $temporaryPlayxId = 'tmp_' . bin2hex(random_bytes(16)); $created = self::create([ 'playx_user_id' => $temporaryPlayxId, 'username' => $username, 'phone' => $phone, 'password' => $pwd, 'admin_id' => 0, 'locked_points' => 0, 'available_points' => 0, 'today_limit' => 0, 'today_claimed' => 0, 'today_limit_date' => null, 'create_time' => $now, 'update_time' => $now, ]); if (!$created) { throw new \RuntimeException('Failed to create mall_user_asset'); } $id = $created->getKey(); $finalPlayxId = 'mall_' . $id; if (self::where('playx_user_id', $finalPlayxId)->where('id', '<>', $id)->find()) { $finalPlayxId = 'mall_' . $id . '_' . bin2hex(random_bytes(4)); } $created->playx_user_id = $finalPlayxId; $created->save(); return $created; } private static function allocateUniquePhone(): ?string { for ($i = 0; $i < 8; $i++) { $candidate = '13' . str_pad(mt_rand(0, 999999999), 9, '0', STR_PAD_LEFT); if (!self::where('phone', $candidate)->find()) { return $candidate; } } return null; } }