qiuqiu 3 months ago
parent
commit
027780098f

+ 11 - 2
.idea/workspace.xml

@@ -7,9 +7,17 @@
   </component>
   <component name="ChangeListManager">
     <list default="true" id="fec10672-acda-4616-894b-a4b6f93aea6f" name="Default Changelist" comment="提交">
-      <change beforePath="$PROJECT_DIR$/笔记文件/2.笔记/工作占比.md" beforeDir="false" afterPath="$PROJECT_DIR$/笔记文件/2.笔记/工作占比.md" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/笔记文件/2.笔记/不拘一格.md" beforeDir="false" afterPath="$PROJECT_DIR$/笔记文件/2.笔记/不拘一格.md" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/笔记文件/2.笔记/数据密集型应用系统设计.md" beforeDir="false" afterPath="$PROJECT_DIR$/笔记文件/2.笔记/数据密集型应用系统设计.md" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/笔记文件/2.笔记/灵光系统 临时记录.md" beforeDir="false" afterPath="$PROJECT_DIR$/笔记文件/2.笔记/灵光系统 临时记录.md" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/笔记文件/2.笔记/灵感_文档.md" beforeDir="false" afterPath="$PROJECT_DIR$/笔记文件/2.笔记/灵感_文档.md" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/笔记文件/2.笔记/编码.md" beforeDir="false" />
+      <change beforePath="$PROJECT_DIR$/笔记文件/2.笔记/网页游戏平台.md" beforeDir="false" afterPath="$PROJECT_DIR$/笔记文件/2.笔记/网页游戏平台.md" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/笔记文件/日记/2025_05_09_星期五.md" beforeDir="false" afterPath="$PROJECT_DIR$/笔记文件/日记/2025_05_09_星期五.md" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/笔记文件/日记/2025_08_05_星期二.md" beforeDir="false" afterPath="$PROJECT_DIR$/笔记文件/日记/2025_08_05_星期二.md" afterDir="false" />
       <change beforePath="$PROJECT_DIR$/笔记文件/日记/2025_08_25_星期一.md" beforeDir="false" afterPath="$PROJECT_DIR$/笔记文件/日记/2025_08_25_星期一.md" afterDir="false" />
-      <change beforePath="$PROJECT_DIR$/笔记文件/日记/2025_08_26_星期二.md" beforeDir="false" afterPath="$PROJECT_DIR$/笔记文件/日记/2025_08_26_星期二.md" afterDir="false" />
+      <change beforePath="$PROJECT_DIR$/笔记文件/看板/看板.md" beforeDir="false" afterPath="$PROJECT_DIR$/笔记文件/看板/看板.md" afterDir="false" />
     </list>
     <option name="SHOW_DIALOG" value="false" />
     <option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -133,6 +141,7 @@
       <workItem from="1755992899281" duration="2568000" />
       <workItem from="1756084740504" duration="1915000" />
       <workItem from="1756170753278" duration="695000" />
+      <workItem from="1756342598549" duration="1798000" />
     </task>
     <task id="LOCAL-00017" summary="提交">
       <created>1744025445201</created>

+ 1 - 6
笔记文件/2.笔记/不拘一格.md

@@ -2,9 +2,4 @@
 ---
 tags: [杂七杂八常识]
 annotation-target: 3.PDF/不拘一格.pdf
----
-
-
-
-
-
+---

+ 0 - 0
笔记文件/2.笔记/数据密集型应用系统设计.md → 笔记文件/2.笔记/数据密集型应用系统设计 读后感.md


+ 2 - 761
笔记文件/2.笔记/灵光系统 临时记录.md

@@ -1,764 +1,5 @@
 #灵感 
 
-接入文档:https://inspire.sg.larksuite.com/docx/RPgndd77VofvNBxPcQ8lBMJngeb
+[[灵光系统 临时记录_第一章|第一章]]
 
-渠道号:
-
-![[Pasted image 20250814091956.png]]
-
-hj和ds的生产环境配置:
-
-```
-
-测试环境:http://192.168.1.33:8080/platform_new/
-正式环境:https://developer.ilnc.inspiregames.cn:8888/platform/
-
-hj
-123456
-
-ds
-123456
-
-正式环境 用户信息
-X项目:
-junbao@inspiregames.cn
-244466666
-
-元素:
-aide@inspiregames.cn
-Yuan1234
-
-正式环境 上报地址:
-元素:ilnc.icongamesg.com
-X项目:ilnc.doomsurvivor.com
-```
-
-参考接入逻辑:
-## FirebaseMessageUtil
-
-``` cs
-using Firebase.Extensions;
-using Ideatech;
-using LuaInterface;
-using Newtonsoft.Json;
-using System.Collections;
-using System.Collections.Generic;
-using System.Linq;
-using UnityEngine;
-using UnityEngine.Networking;
-
-/// <summary>
-/// FCM推送相关
-/// </summary>
-public class FirebaseMessageUtil
-{
-    private static FirebaseMessageUtil _instance;
-
-    public bool isSuccess = false;//初始化是否成功的标志,在FirebaseUtils脚本中同步该标志位
-    private string fcmAppid_PT; //Application ID of fcm for PTServer
-    private string fcmAppid_MX; //Application ID of fcm for MXServer
-    private string fcmAppid_Test; //Application ID of fcm for TestServer
-    public string fcmToken; //token of fcm
-
-    private FirebaseMessageUtil() { }
-
-    public static FirebaseMessageUtil Instance
-    {
-        get
-        {
-            if (_instance == null)
-            {
-                _instance = new FirebaseMessageUtil();
-            }
-
-            return _instance;
-        }
-    }
-
-    [NoToLua]
-    public void InitializeFirebase()
-    {
-        Firebase.Messaging.FirebaseMessaging.MessageReceived += OnMessageReceived;
-        Firebase.Messaging.FirebaseMessaging.TokenReceived += OnTokenReceived;
-
-        if (!ThirdPartyConst.TryGetValue("fcmAppidpt", out fcmAppid_PT))
-        {
-            Debug.LogWarning("Failed to obtain the FCM application ID: fcmAppidpt");
-        }
-
-        if (!ThirdPartyConst.TryGetValue("fcmAppidmx", out fcmAppid_MX))
-        {
-            Debug.LogWarning("Failed to obtain the FCM application ID: fcmAppidmx");
-        }
-
-        if (!ThirdPartyConst.TryGetValue("fcmAppidtest", out fcmAppid_Test))
-        {
-            Debug.LogWarning("Failed to obtain the FCM application ID: fcmAppidtest");
-        }
-    }
-
-    [NoToLua]
-    public void RemoveFirebase()
-    {
-        Firebase.Messaging.FirebaseMessaging.MessageReceived -= OnMessageReceived;
-        Firebase.Messaging.FirebaseMessaging.TokenReceived -= OnTokenReceived;
-    }
-
-    private void OnMessageReceived(object sender, Firebase.Messaging.MessageReceivedEventArgs e)
-    {
-        Dictionary<string, object> args = new Dictionary<string, object>();
-        args.Add(NativeEventConst.EVENT_ID_KEY, NativeEventConst.FIREBASE_MESSAGE_RECEIVED);
-
-        var notification = e.Message.Notification;
-        if (notification != null)
-        {
-            args.Add("title", notification.Title);
-            args.Add("body", notification.Body);
-
-            var android = notification.Android;
-            if (android != null)
-            {
-                args.Add("android channel_id", android.ChannelId);
-            }
-        }
-
-        if (e.Message.From.Length > 0)
-        {
-            args.Add("from", e.Message.From);
-        }
-
-        if (e.Message.Link != null)
-        {
-            args.Add("link", e.Message.Link.ToString());
-        }
-
-        if (e.Message.Data.Count > 0)
-        {
-            foreach (KeyValuePair<string, string> item in e.Message.Data)
-            {
-                args.Add(item.Key, item.Value);
-            }
-        }
-
-        //NativeUtils.CallBackToLua(args);
-    }
-
-    private void OnTokenReceived(object sender, Firebase.Messaging.TokenReceivedEventArgs token)
-    {
-        fcmToken = token.Token;
-        Debug.Log("Received FCM Registration Token: " + token.Token);
-    }
-
-    /// <summary>
-    /// 检测json合法性
-    /// </summary>
-    /// <param name="jsonStr"></param>
-    /// <returns></returns>
-    private (bool isValid, string error) CheckJsonDataValid<TKey, TValue>(Dictionary<TKey, TValue> jsonMap)
-    {
-        if (jsonMap == null)
-            return (false, "The dictionary object is null");
-
-        var emptyItems = jsonMap
-            .Where(kv => IsInvalidValue(kv.Value))
-            .Select(kv => kv.Key.ToString())
-            .ToList();
-
-        return emptyItems.Count == 0
-            ? (true, null)
-            : (false, $"The following key values are empty: {string.Join(", ", emptyItems)}");
-    }
-
-    private bool IsInvalidValue<T>(T value)
-    {
-        if (value == null) return true;
-        if (value is string str) return string.IsNullOrEmpty(str);
-        return false;
-    }
-
-    /// <summary>
-    /// 供Lua层调用,设备上报
-    /// </summary>
-    public void EquipmentPost(string url, string jsonData)
-    {
-        if (isSuccess)
-        {
-            //检测json键值合法性
-            Dictionary<string, string> paramsMap = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonData);
-            var (isValid, error) = CheckJsonDataValid(paramsMap);
-            if (!isValid)
-            {
-                Debug.LogWarning($"Equipment reporting failed: {error}");
-                return;
-            }
-
-            CoroutineManager.AddCoroutine(PostRequest(url, jsonData));
-        }
-        else
-        {
-            Debug.LogWarning("The device failed to report and the SDK was not successfully initialized");
-        }
-    }
-
-    [System.Serializable]
-    private class PostResponse
-    {
-        public int code;
-        public string message;
-        public string data;
-    }
-
-    private IEnumerator PostRequest(string url, string jsonData)
-    {
-        using (UnityWebRequest request = UnityWebRequest.Post(url, "POST"))
-        {
-            byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonData);
-
-            request.uploadHandler = new UploadHandlerRaw(bodyRaw);
-            request.downloadHandler = new DownloadHandlerBuffer();
-            request.SetRequestHeader("Content-Type", "application/json");
-
-            yield return request.SendWebRequest();
-
-            if (request.result != UnityWebRequest.Result.Success)
-            {
-                Debug.LogWarning("Equipment reporting failed." + request.error);
-                yield break;
-            }
-
-            var response = JsonUtility.FromJson<PostResponse>(request.downloadHandler.text);
-            if (response.code != 0)
-            {
-                Debug.LogWarning("The device failed to report the error code" + response.code);
-            }
-        };
-    }
-
-    /// <summary>
-    /// 供Lua调用,订阅消息主题,订阅成功后可以通过主题推送
-    /// </summary>
-    /// <param name="topic"></param>
-    public void SubscribeTopic(string topicJsonStr)
-    {
-        if (!isSuccess)
-        {
-            Debug.LogWarning("Subscription to the topic failed! The SDK was not initialized successfully");
-            return;
-        }
-
-        //检测json键值合法性
-        Dictionary<string, string> paramsMap = JsonConvert.DeserializeObject<Dictionary<string, string>>(topicJsonStr);
-        var (isValid, error) = CheckJsonDataValid(paramsMap);
-        if (!isValid)
-        {
-            Debug.LogWarning($"Failed to subscribe to the topic: {error}");
-            return;
-        }
-
-        foreach (string key in paramsMap.Keys)
-        {
-            Firebase.Messaging.FirebaseMessaging.SubscribeAsync(paramsMap[key]).ContinueWithOnMainThread(task =>
-            {
-                if (task.IsFaulted)
-                    Debug.LogWarning($"Failed to subscribe to the topic: {task.Exception}");
-            });
-        }
-    }
-
-    /// <summary>
-    /// 供Lua调用,根据服务器类型,获取应用ID
-    /// </summary>
-    /// <param name="serverCode"></param>
-    /// <returns></returns>
-    public string GetAppIdByServer(string serverCode)
-    {
-        if (string.IsNullOrEmpty(serverCode))
-        {
-            return fcmAppid_Test;
-        }
-        else
-        {
-            string lowerServerCode = serverCode.ToLower();
-            if (lowerServerCode == "pt")
-            {
-                return fcmAppid_PT;
-            }
-            else if (lowerServerCode == "es")
-            {
-                return fcmAppid_MX;
-            }
-            else
-            {
-                return "";
-            }
-        }
-    }
-}
-
-```
-
-## ios消息通知扩展:
-
-参考链接:
-https://firebase.google.com/docs/cloud-messaging/ios/send-image?hl=zh-cn#node.js
-
-https://blog.csdn.net/qq_38718912/article/details/126975533
-
-FCM带图片,发送给客户端回包,数据结构:
-
-``` json
-FCM: userInfo: {
-    aps =     {
-        alert =         {
-            body = 2;
-            title = 1;
-        };
-        "mutable-content" = 1;
-    };
-    "fcm_options" =     {
-        image = "https://octodex.github.com/images/codercat.jpg";
-    };
-    "gcm.message_id" = 1755861458675849;
-    "google.c.a.e" = 1;
-    "google.c.fid" = cRZAyUCe5Uxcihd2iGk7Yf;
-    "google.c.sender.id" = 967184518610;
-    traceId = "D0bWCJoRYhib3+FPKvN8ueLz6EX+ITcDBifBlie+Ki6iMKwlC4h4dVYQoWGC845E";
-}.
-FCM: userInfo: {
-    aps =     {
-        alert =         {
-            body = 2;
-            title = 1;
-        };
-        "mutable-content" = 1;
-    };
-    "fcm_options" =     {
-        image = "https://octodex.github.com/images/codercat.jpg";
-    };
-    "gcm.message_id" = 1755861458675849;
-    "google.c.a.e" = 1;
-    "google.c.fid" = cRZAyUCe5Uxcihd2iGk7Yf;
-    "google.c.sender.id" = 967184518610;
-    traceId = "D0bWCJoRYhib3+FPKvN8ueLz6EX+ITcDBifBlie+Ki6iMKwlC4h4dVYQoWGC845E";
-}.
-```
-
-如有需要,还要在podFile里面,添加fcm相关的插件,到消息通知扩展插件
-```
-target 'NotificationService' do
-  pod 'Firebase/Messaging', '12.0.0'
-end
-```
-
-
-编辑Editor脚本:
-## NotificationServiceExtensionCreator.cs
-
-``` cs
-#if UNITY_IOS
-using System.IO;
-using UnityEditor;
-using UnityEditor.Callbacks;
-using UnityEditor.iOS.Xcode;
-using UnityEditor.iOS.Xcode.Extensions;
-using UnityEngine;
-using UnityEditor.iOS.Xcode.PBX;
-
-
-public static class PureNotificationExtensionCreator
-{
-    private const string ExtensionName = "NotificationService";
-
-    [PostProcessBuild(200)]
-    public static void OnPostProcessBuild(BuildTarget target, string buildPath)
-    {
-        if (target != BuildTarget.iOS) return;
-
-        Debug.Log("🛠️ 开始创建纯净版 Notification Service Extension...");
-
-        string projPath = PBXProject.GetPBXProjectPath(buildPath);
-        PBXProject proj = new PBXProject();
-        proj.ReadFromFile(projPath);
-
-#if UNITY_2019_3_OR_NEWER
-        string mainTargetGuid = proj.GetUnityMainTargetGuid();
-#else
-        string mainTargetGuid = proj.TargetGuidByName(PBXProject.GetUnityTargetName());
-#endif
-        
-        // 1. 创建 Extension Target(这会自动创建基础文件)
-        string extGuid = proj.AddAppExtension(mainTargetGuid, ExtensionName, ExtensionName, 
-            $"{ExtensionName}/Info.plist");
-
-        Debug.Log($"✅ 创建 Extension Target: {extGuid}");
-
-        // 2. 配置基本设置
-        proj.SetBuildProperty(extGuid, "PRODUCT_BUNDLE_IDENTIFIER", 
-            PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.iOS) + "." + ExtensionName);
-        proj.SetBuildProperty(extGuid, "ENABLE_BITCODE", "NO");
-        proj.SetBuildProperty(extGuid, "IPHONEOS_DEPLOYMENT_TARGET", "15.0");
-
-        // 3. 配置签名
-        ConfigureCodeSigning(proj, extGuid);
-
-        // 4. 移动预制的代码文件到扩展目录
-        MovePrebuiltSourceFiles(buildPath);
-
-        // 5. 添加系统框架
-        proj.AddFrameworkToProject(extGuid, "UserNotifications.framework", false);
-
-        // 6. 配置 Push Notifications 能力
-        ConfigurePushNotificationsCapability(proj, extGuid, buildPath);
-
-        // 7. 关键修复:确保文件被添加到 Xcode 项目结构和编译阶段
-        AddFilesToProjectStructure(proj, extGuid, buildPath);
-
-        // 8. 保存工程
-        proj.WriteToFile(projPath);
-        AssetDatabase.Refresh();
-
-        Debug.Log("🎉 纯净版 Notification Service Extension 创建完成!");
-    }
-    // 关键方法:确保文件被添加到 Xcode 项目结构
-    private static void AddFilesToProjectStructure(PBXProject proj, string extGuid, string buildPath)
-    {
-        Debug.Log("🔧 开始添加文件到 Xcode 项目结构...");
-    
-        // 1. 获取或创建 Sources Build Phase
-        var buildPhaseID = proj.AddSourcesBuildPhase(extGuid);
-        Debug.Log($"✅ Sources Build Phase ID: {buildPhaseID}");
-    
-        // 2. 添加 .h 文件到项目结构
-        string headerPath = Path.Combine(ExtensionName, "NotificationService.h");
-        string headerGuid = proj.AddFile(headerPath, headerPath, PBXSourceTree.Source);
-        Debug.Log($"✅ Header 文件添加到项目: {headerGuid}");
-    
-        // 3. 添加 .m 文件到项目结构
-        string sourcePath = Path.Combine(ExtensionName, "NotificationService.m");
-        string sourceGuid = proj.AddFile(sourcePath, sourcePath, PBXSourceTree.Source);
-        Debug.Log($"✅ Source 文件添加到项目: {sourceGuid}");
-    
-        // 4. 将文件添加到编译阶段(确保 Target Membership 正确)
-        proj.AddFileToBuildSection(extGuid, buildPhaseID, headerGuid);
-        proj.AddFileToBuildSection(extGuid, buildPhaseID, sourceGuid);
-        Debug.Log($"✅ 文件已添加到编译阶段");
-    
-        // 5. 确保 Info.plist 也被添加到项目
-        string plistPath = Path.Combine(ExtensionName, "Info.plist");
-        string plistGuid = proj.AddFile(plistPath, plistPath, PBXSourceTree.Source);
-        Debug.Log($"✅ Info.plist 添加到项目: {plistGuid}");
-    
-        Debug.Log("🎯 所有文件已成功添加到 Xcode 项目结构!");
-    }
-
-
-    private static void ConfigureCodeSigning(PBXProject proj, string extGuid)
-    {
-        string teamId = PlayerSettings.iOS.appleDeveloperTeamID;
-        if (!string.IsNullOrEmpty(teamId))
-        {
-            proj.SetBuildProperty(extGuid, "DEVELOPMENT_TEAM", teamId);
-            proj.SetBuildProperty(extGuid, "CODE_SIGN_STYLE", "Automatic");
-        }
-    }
-
-    private static void MovePrebuiltSourceFiles(string buildPath)
-    {
-        string extDir = Path.Combine(buildPath, ExtensionName);
-        
-        // 确保目录存在
-        if (!Directory.Exists(extDir))
-            Directory.CreateDirectory(extDir);
-
-        // 源文件路径(Plugins/iOS 目录)
-        string sourceDir = Path.Combine(Application.dataPath, "Plugins", "iOS");
-        
-        // 移动 .h 文件
-        string headerSource = Path.Combine(sourceDir, "NotificationService.h");
-        string headerDest = Path.Combine(extDir, "NotificationService.h");
-        if (File.Exists(headerSource))
-        {
-            // 如果目标文件已存在,先删除
-            if (File.Exists(headerDest))
-            {
-                File.Delete(headerDest);
-                Debug.Log("🗑️ 删除已存在的目标文件: " + headerDest);
-            }
-            
-            File.Move(headerSource, headerDest);
-            Debug.Log("✅ 移动 NotificationService.h 文件");
-        }
-        else
-        {
-            Debug.LogError("❌ 找不到源文件: " + headerSource);
-        }
-
-        // 移动 .m 文件
-        string sourceSource = Path.Combine(sourceDir, "NotificationService.m");
-        string sourceDest = Path.Combine(extDir, "NotificationService.m");
-        if (File.Exists(sourceSource))
-        {
-            // 如果目标文件已存在,先删除
-            if (File.Exists(sourceDest))
-            {
-                File.Delete(sourceDest);
-                Debug.Log("🗑️ 删除已存在的目标文件: " + sourceDest);
-            }
-            
-            File.Move(sourceSource, sourceDest);
-            Debug.Log("✅ 移动 NotificationService.m 文件");
-        }
-        else
-        {
-            Debug.LogError("❌ 找不到源文件: " + sourceSource);
-        }
-
-        // 创建 Info.plist(如果需要)
-        string plistPath = Path.Combine(extDir, "Info.plist");
-        if (!File.Exists(plistPath))
-        {
-            File.WriteAllText(plistPath, PureInfoPlistContent);
-            Debug.Log("✅ 创建 Info.plist 文件");
-        }
-    }
-
-    private static void ConfigurePushNotificationsCapability(PBXProject proj, string extGuid, string buildPath)
-    {
-        Debug.Log("🔧 开始配置 Push Notifications 能力...");
-        
-        // 1. 添加 Push Notifications 能力
-        proj.AddCapability(extGuid, PBXCapabilityType.PushNotifications);
-        Debug.Log("✅ 添加 Push Notifications 能力");
-        
-        // 2. 确保签名配置正确
-        string teamId = PlayerSettings.iOS.appleDeveloperTeamID;
-        if (!string.IsNullOrEmpty(teamId))
-        {
-            proj.SetBuildProperty(extGuid, "DEVELOPMENT_TEAM", teamId);
-            proj.SetBuildProperty(extGuid, "CODE_SIGN_STYLE", "Automatic");
-            Debug.Log($"✅ 配置开发团队: {teamId}");
-        }
-        
-        Debug.Log("✅ Push Notifications 能力配置完成");
-    }
-
-
-
-    // 纯净的 Info.plist 内容
-    private static readonly string PureInfoPlistContent =
-@"<?xml version=""1.0"" encoding=""UTF-8""?>
-<!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd"">
-<plist version=""1.0"">
-<dict>
-    <key>CFBundleDevelopmentRegion</key>
-    <string>$(DEVELOPMENT_LANGUAGE)</string>
-    <key>CFBundleDisplayName</key>
-    <string>NotificationService</string>
-    <key>CFBundleExecutable</key>
-    <string>$(EXECUTABLE_NAME)</string>
-    <key>CFBundleIdentifier</key>
-    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
-    <key>CFBundleInfoDictionaryVersion</key>
-    <string>6.0</string>
-    <key>CFBundleName</key>
-    <string>$(PRODUCT_NAME)</string>
-    <key>CFBundlePackageType</key>
-    <string>XPC!</string>
-    <key>CFBundleShortVersionString</key>
-    <string>1.0</string>
-    <key>CFBundleVersion</key>
-    <string>1</string>
-    <key>NSExtension</key>
-    <dict>
-        <key>NSExtensionPointIdentifier</key>
-        <string>com.apple.usernotifications.service</string>
-        <key>NSExtensionPrincipalClass</key>
-        <string>NotificationService</string>
-    </dict>
-</dict>
-</plist>";
-}
-
-#endif
-```
-
-## NotificationService.h
-
-``` cpp
-//
-//  NotificationService.h
-//  NotificationService
-//
-//  Created by unity on 2025/8/23.
-//
-
-#import <UserNotifications/UserNotifications.h>
-
-@interface NotificationService : UNNotificationServiceExtension
-
-@end
-
-```
-
-## NotificationService.m
-
-``` cpp
-//
-//  NotificationService.m
-//  NotificationService
-//
-//  Created by unity on 2025/8/23.
-//
-
-#import "NotificationService.h"
-#import <UIKit/UIKit.h>
-
-@interface NotificationService ()
-
-@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
-@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
-
-@end
-
-@implementation NotificationService
-
-- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
-    
-    NSLog(@"✅ Extension被调用!");
-    NSLog(@"📦 原始通知内容: %@", request.content.userInfo);
-    
-    self.contentHandler = contentHandler;
-    self.bestAttemptContent = [request.content mutableCopy];
-    
-    self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.title];
-    
-    // 1. 从userInfo中获取图片URL
-    NSDictionary *userInfo = request.content.userInfo;
-    NSString *imageUrlString = nil;
-    
-    // 检查FCM格式的图片URL (fcm_options -> image)
-    if (userInfo[@"fcm_options"] && userInfo[@"fcm_options"][@"image"]) {
-        imageUrlString = userInfo[@"fcm_options"][@"image"];
-        NSLog(@"🖼️ 找到FCM图片URL: %@", imageUrlString);
-    }
-    // 也可以检查其他自定义字段
-    else if (userInfo[@"image_url"]) {
-        imageUrlString = userInfo[@"image_url"];
-        NSLog(@"🖼️ 找到自定义图片URL: %@", imageUrlString);
-    }
-    
-    // 2. 如果没有图片URL,直接返回原始内容
-    if (!imageUrlString) {
-        NSLog(@"⚠️ 未找到图片URL,使用原始内容");
-        self.contentHandler(self.bestAttemptContent);
-        return;
-    }
-    
-    // 3. 下载图片并添加到通知
-    [self loadAttachmentForUrlString:imageUrlString completionHandler:^(UNNotificationAttachment *attachment) {
-        if (attachment) {
-            NSLog(@"✅ 图片下载成功,添加到通知");
-            self.bestAttemptContent.attachments = @[attachment];
-        } else {
-            NSLog(@"❌ 图片下载失败,使用原始内容");
-        }
-        
-        // 4. 最终返回通知内容
-        self.contentHandler(self.bestAttemptContent);
-    }];
-}
-
-- (void)loadAttachmentForUrlString:(NSString *)urlString completionHandler:(void (^)(UNNotificationAttachment *))completionHandler {
-    
-    __block UNNotificationAttachment *attachment = nil;
-    NSURL *attachmentURL = [NSURL URLWithString:urlString];
-    
-    if (!attachmentURL) {
-        completionHandler(nil);
-        return;
-    }
-    
-    NSLog(@"🌐 开始下载图片: %@", urlString);
-    
-    NSURLSession *session = [NSURLSession sharedSession];
-    NSURLSessionDownloadTask *task = [session downloadTaskWithURL:attachmentURL completionHandler:^(NSURL *temporaryFileLocation, NSURLResponse *response, NSError *error) {
-        
-        // 检查下载错误
-        if (error) {
-            NSLog(@"❌ 图片下载错误: %@", error);
-            completionHandler(nil);
-            return;
-        }
-        
-        // 检查文件是否存在
-        if (!temporaryFileLocation) {
-            NSLog(@"❌ 临时文件位置为空");
-            completionHandler(nil);
-            return;
-        }
-        
-        // 获取文件扩展名
-        NSString *fileExtension = [self getFileExtensionForResponse:response] ?: @"jpg";
-        NSLog(@"📄 文件扩展名: %@", fileExtension);
-        
-        // 创建唯一文件名
-        NSString *uniqueFileName = [NSString stringWithFormat:@"%@.%@", [[NSUUID UUID] UUIDString], fileExtension];
-        NSString *tempFile = [NSTemporaryDirectory() stringByAppendingPathComponent:uniqueFileName];
-        
-        // 移动文件到临时目录
-        NSError *moveError = nil;
-        [[NSFileManager defaultManager] moveItemAtURL:temporaryFileLocation toURL:[NSURL fileURLWithPath:tempFile] error:&moveError];
-        
-        if (moveError) {
-            NSLog(@"❌ 移动文件错误: %@", moveError);
-            completionHandler(nil);
-            return;
-        }
-        
-        // 创建通知附件
-        attachment = [UNNotificationAttachment attachmentWithIdentifier:@"image"
-                                                                    URL:[NSURL fileURLWithPath:tempFile]
-                                                                options:nil
-                                                                  error:&moveError];
-        
-        if (moveError) {
-            NSLog(@"❌ 创建附件错误: %@", moveError);
-            completionHandler(nil);
-            return;
-        }
-        
-        completionHandler(attachment);
-    }];
-    
-    [task resume];
-}
-
-- (NSString *)getFileExtensionForResponse:(NSURLResponse *)response {
-    if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
-        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
-        NSString *contentType = httpResponse.allHeaderFields[@"Content-Type"];
-        
-        if ([contentType isEqualToString:@"image/jpeg"]) return @"jpg";
-        if ([contentType isEqualToString:@"image/jpg"]) return @"jpg";
-        if ([contentType isEqualToString:@"image/png"]) return @"png";
-        if ([contentType isEqualToString:@"image/gif"]) return @"gif";
-        if ([contentType isEqualToString:@"image/webp"]) return @"webp";
-    }
-    
-    // 从URL路径推断扩展名
-    NSString *pathExtension = response.URL.pathExtension;
-    if (pathExtension.length > 0) {
-        return pathExtension;
-    }
-    
-    return @"jpg"; // 默认使用jpg
-}
-
-- (void)serviceExtensionTimeWillExpire {
-    NSLog(@"⏰ Extension处理超时,使用原始内容");
-    self.contentHandler(self.bestAttemptContent);
-}
-
-@end
-
-```
+[[灵光系统 临时记录_第二章|第二章]]

+ 763 - 0
笔记文件/2.笔记/灵光系统 临时记录_第一章.md

@@ -0,0 +1,763 @@
+
+接入文档:https://inspire.sg.larksuite.com/docx/RPgndd77VofvNBxPcQ8lBMJngeb
+
+渠道号:
+
+![[Pasted image 20250814091956.png]]
+
+hj和ds的生产环境配置:
+
+```
+
+测试环境:http://192.168.1.33:8080/platform_new/
+正式环境:https://developer.ilnc.inspiregames.cn:8888/platform/
+
+hj
+123456
+
+ds
+123456
+
+正式环境 用户信息
+X项目:
+junbao@inspiregames.cn
+244466666
+
+元素:
+aide@inspiregames.cn
+Yuan1234
+
+正式环境 上报地址:
+元素:ilnc.icongamesg.com
+X项目:ilnc.doomsurvivor.com
+```
+
+参考接入逻辑:
+## FirebaseMessageUtil
+
+``` cs
+using Firebase.Extensions;
+using Ideatech;
+using LuaInterface;
+using Newtonsoft.Json;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+using UnityEngine.Networking;
+
+/// <summary>
+/// FCM推送相关
+/// </summary>
+public class FirebaseMessageUtil
+{
+    private static FirebaseMessageUtil _instance;
+
+    public bool isSuccess = false;//初始化是否成功的标志,在FirebaseUtils脚本中同步该标志位
+    private string fcmAppid_PT; //Application ID of fcm for PTServer
+    private string fcmAppid_MX; //Application ID of fcm for MXServer
+    private string fcmAppid_Test; //Application ID of fcm for TestServer
+    public string fcmToken; //token of fcm
+
+    private FirebaseMessageUtil() { }
+
+    public static FirebaseMessageUtil Instance
+    {
+        get
+        {
+            if (_instance == null)
+            {
+                _instance = new FirebaseMessageUtil();
+            }
+
+            return _instance;
+        }
+    }
+
+    [NoToLua]
+    public void InitializeFirebase()
+    {
+        Firebase.Messaging.FirebaseMessaging.MessageReceived += OnMessageReceived;
+        Firebase.Messaging.FirebaseMessaging.TokenReceived += OnTokenReceived;
+
+        if (!ThirdPartyConst.TryGetValue("fcmAppidpt", out fcmAppid_PT))
+        {
+            Debug.LogWarning("Failed to obtain the FCM application ID: fcmAppidpt");
+        }
+
+        if (!ThirdPartyConst.TryGetValue("fcmAppidmx", out fcmAppid_MX))
+        {
+            Debug.LogWarning("Failed to obtain the FCM application ID: fcmAppidmx");
+        }
+
+        if (!ThirdPartyConst.TryGetValue("fcmAppidtest", out fcmAppid_Test))
+        {
+            Debug.LogWarning("Failed to obtain the FCM application ID: fcmAppidtest");
+        }
+    }
+
+    [NoToLua]
+    public void RemoveFirebase()
+    {
+        Firebase.Messaging.FirebaseMessaging.MessageReceived -= OnMessageReceived;
+        Firebase.Messaging.FirebaseMessaging.TokenReceived -= OnTokenReceived;
+    }
+
+    private void OnMessageReceived(object sender, Firebase.Messaging.MessageReceivedEventArgs e)
+    {
+        Dictionary<string, object> args = new Dictionary<string, object>();
+        args.Add(NativeEventConst.EVENT_ID_KEY, NativeEventConst.FIREBASE_MESSAGE_RECEIVED);
+
+        var notification = e.Message.Notification;
+        if (notification != null)
+        {
+            args.Add("title", notification.Title);
+            args.Add("body", notification.Body);
+
+            var android = notification.Android;
+            if (android != null)
+            {
+                args.Add("android channel_id", android.ChannelId);
+            }
+        }
+
+        if (e.Message.From.Length > 0)
+        {
+            args.Add("from", e.Message.From);
+        }
+
+        if (e.Message.Link != null)
+        {
+            args.Add("link", e.Message.Link.ToString());
+        }
+
+        if (e.Message.Data.Count > 0)
+        {
+            foreach (KeyValuePair<string, string> item in e.Message.Data)
+            {
+                args.Add(item.Key, item.Value);
+            }
+        }
+
+        //NativeUtils.CallBackToLua(args);
+    }
+
+    private void OnTokenReceived(object sender, Firebase.Messaging.TokenReceivedEventArgs token)
+    {
+        fcmToken = token.Token;
+        Debug.Log("Received FCM Registration Token: " + token.Token);
+    }
+
+    /// <summary>
+    /// 检测json合法性
+    /// </summary>
+    /// <param name="jsonStr"></param>
+    /// <returns></returns>
+    private (bool isValid, string error) CheckJsonDataValid<TKey, TValue>(Dictionary<TKey, TValue> jsonMap)
+    {
+        if (jsonMap == null)
+            return (false, "The dictionary object is null");
+
+        var emptyItems = jsonMap
+            .Where(kv => IsInvalidValue(kv.Value))
+            .Select(kv => kv.Key.ToString())
+            .ToList();
+
+        return emptyItems.Count == 0
+            ? (true, null)
+            : (false, $"The following key values are empty: {string.Join(", ", emptyItems)}");
+    }
+
+    private bool IsInvalidValue<T>(T value)
+    {
+        if (value == null) return true;
+        if (value is string str) return string.IsNullOrEmpty(str);
+        return false;
+    }
+
+    /// <summary>
+    /// 供Lua层调用,设备上报
+    /// </summary>
+    public void EquipmentPost(string url, string jsonData)
+    {
+        if (isSuccess)
+        {
+            //检测json键值合法性
+            Dictionary<string, string> paramsMap = JsonConvert.DeserializeObject<Dictionary<string, string>>(jsonData);
+            var (isValid, error) = CheckJsonDataValid(paramsMap);
+            if (!isValid)
+            {
+                Debug.LogWarning($"Equipment reporting failed: {error}");
+                return;
+            }
+
+            CoroutineManager.AddCoroutine(PostRequest(url, jsonData));
+        }
+        else
+        {
+            Debug.LogWarning("The device failed to report and the SDK was not successfully initialized");
+        }
+    }
+
+    [System.Serializable]
+    private class PostResponse
+    {
+        public int code;
+        public string message;
+        public string data;
+    }
+
+    private IEnumerator PostRequest(string url, string jsonData)
+    {
+        using (UnityWebRequest request = UnityWebRequest.Post(url, "POST"))
+        {
+            byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonData);
+
+            request.uploadHandler = new UploadHandlerRaw(bodyRaw);
+            request.downloadHandler = new DownloadHandlerBuffer();
+            request.SetRequestHeader("Content-Type", "application/json");
+
+            yield return request.SendWebRequest();
+
+            if (request.result != UnityWebRequest.Result.Success)
+            {
+                Debug.LogWarning("Equipment reporting failed." + request.error);
+                yield break;
+            }
+
+            var response = JsonUtility.FromJson<PostResponse>(request.downloadHandler.text);
+            if (response.code != 0)
+            {
+                Debug.LogWarning("The device failed to report the error code" + response.code);
+            }
+        };
+    }
+
+    /// <summary>
+    /// 供Lua调用,订阅消息主题,订阅成功后可以通过主题推送
+    /// </summary>
+    /// <param name="topic"></param>
+    public void SubscribeTopic(string topicJsonStr)
+    {
+        if (!isSuccess)
+        {
+            Debug.LogWarning("Subscription to the topic failed! The SDK was not initialized successfully");
+            return;
+        }
+
+        //检测json键值合法性
+        Dictionary<string, string> paramsMap = JsonConvert.DeserializeObject<Dictionary<string, string>>(topicJsonStr);
+        var (isValid, error) = CheckJsonDataValid(paramsMap);
+        if (!isValid)
+        {
+            Debug.LogWarning($"Failed to subscribe to the topic: {error}");
+            return;
+        }
+
+        foreach (string key in paramsMap.Keys)
+        {
+            Firebase.Messaging.FirebaseMessaging.SubscribeAsync(paramsMap[key]).ContinueWithOnMainThread(task =>
+            {
+                if (task.IsFaulted)
+                    Debug.LogWarning($"Failed to subscribe to the topic: {task.Exception}");
+            });
+        }
+    }
+
+    /// <summary>
+    /// 供Lua调用,根据服务器类型,获取应用ID
+    /// </summary>
+    /// <param name="serverCode"></param>
+    /// <returns></returns>
+    public string GetAppIdByServer(string serverCode)
+    {
+        if (string.IsNullOrEmpty(serverCode))
+        {
+            return fcmAppid_Test;
+        }
+        else
+        {
+            string lowerServerCode = serverCode.ToLower();
+            if (lowerServerCode == "pt")
+            {
+                return fcmAppid_PT;
+            }
+            else if (lowerServerCode == "es")
+            {
+                return fcmAppid_MX;
+            }
+            else
+            {
+                return "";
+            }
+        }
+    }
+}
+
+```
+
+## ios消息通知扩展:
+
+参考链接:
+https://firebase.google.com/docs/cloud-messaging/ios/send-image?hl=zh-cn#node.js
+
+https://blog.csdn.net/qq_38718912/article/details/126975533
+
+FCM带图片,发送给客户端回包,数据结构:
+
+``` json
+FCM: userInfo: {
+    aps =     {
+        alert =         {
+            body = 2;
+            title = 1;
+        };
+        "mutable-content" = 1;
+    };
+    "fcm_options" =     {
+        image = "https://octodex.github.com/images/codercat.jpg";
+    };
+    "gcm.message_id" = 1755861458675849;
+    "google.c.a.e" = 1;
+    "google.c.fid" = cRZAyUCe5Uxcihd2iGk7Yf;
+    "google.c.sender.id" = 967184518610;
+    traceId = "D0bWCJoRYhib3+FPKvN8ueLz6EX+ITcDBifBlie+Ki6iMKwlC4h4dVYQoWGC845E";
+}.
+FCM: userInfo: {
+    aps =     {
+        alert =         {
+            body = 2;
+            title = 1;
+        };
+        "mutable-content" = 1;
+    };
+    "fcm_options" =     {
+        image = "https://octodex.github.com/images/codercat.jpg";
+    };
+    "gcm.message_id" = 1755861458675849;
+    "google.c.a.e" = 1;
+    "google.c.fid" = cRZAyUCe5Uxcihd2iGk7Yf;
+    "google.c.sender.id" = 967184518610;
+    traceId = "D0bWCJoRYhib3+FPKvN8ueLz6EX+ITcDBifBlie+Ki6iMKwlC4h4dVYQoWGC845E";
+}.
+```
+
+如有需要,还要在podFile里面,添加fcm相关的插件,到消息通知扩展插件
+```
+target 'NotificationService' do
+  pod 'Firebase/Messaging', '12.0.0'
+end
+```
+
+
+编辑Editor脚本:
+## NotificationServiceExtensionCreator.cs
+
+``` cs
+#if UNITY_IOS
+using System.IO;
+using UnityEditor;
+using UnityEditor.Callbacks;
+using UnityEditor.iOS.Xcode;
+using UnityEditor.iOS.Xcode.Extensions;
+using UnityEngine;
+using UnityEditor.iOS.Xcode.PBX;
+
+
+public static class PureNotificationExtensionCreator
+{
+    private const string ExtensionName = "NotificationService";
+
+    [PostProcessBuild(200)]
+    public static void OnPostProcessBuild(BuildTarget target, string buildPath)
+    {
+        if (target != BuildTarget.iOS) return;
+
+        Debug.Log("🛠️ 开始创建纯净版 Notification Service Extension...");
+
+        string projPath = PBXProject.GetPBXProjectPath(buildPath);
+        PBXProject proj = new PBXProject();
+        proj.ReadFromFile(projPath);
+
+#if UNITY_2019_3_OR_NEWER
+        string mainTargetGuid = proj.GetUnityMainTargetGuid();
+#else
+        string mainTargetGuid = proj.TargetGuidByName(PBXProject.GetUnityTargetName());
+#endif
+        
+        // 1. 创建 Extension Target(这会自动创建基础文件)
+        string extGuid = proj.AddAppExtension(mainTargetGuid, ExtensionName, ExtensionName, 
+            $"{ExtensionName}/Info.plist");
+
+        Debug.Log($"✅ 创建 Extension Target: {extGuid}");
+
+        // 2. 配置基本设置
+        proj.SetBuildProperty(extGuid, "PRODUCT_BUNDLE_IDENTIFIER", 
+            PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.iOS) + "." + ExtensionName);
+        proj.SetBuildProperty(extGuid, "ENABLE_BITCODE", "NO");
+        proj.SetBuildProperty(extGuid, "IPHONEOS_DEPLOYMENT_TARGET", "15.0");
+
+        // 3. 配置签名
+        ConfigureCodeSigning(proj, extGuid);
+
+        // 4. 移动预制的代码文件到扩展目录
+        MovePrebuiltSourceFiles(buildPath);
+
+        // 5. 添加系统框架
+        proj.AddFrameworkToProject(extGuid, "UserNotifications.framework", false);
+
+        // 6. 配置 Push Notifications 能力
+        ConfigurePushNotificationsCapability(proj, extGuid, buildPath);
+
+        // 7. 关键修复:确保文件被添加到 Xcode 项目结构和编译阶段
+        AddFilesToProjectStructure(proj, extGuid, buildPath);
+
+        // 8. 保存工程
+        proj.WriteToFile(projPath);
+        AssetDatabase.Refresh();
+
+        Debug.Log("🎉 纯净版 Notification Service Extension 创建完成!");
+    }
+    // 关键方法:确保文件被添加到 Xcode 项目结构
+    private static void AddFilesToProjectStructure(PBXProject proj, string extGuid, string buildPath)
+    {
+        Debug.Log("🔧 开始添加文件到 Xcode 项目结构...");
+    
+        // 1. 获取或创建 Sources Build Phase
+        var buildPhaseID = proj.AddSourcesBuildPhase(extGuid);
+        Debug.Log($"✅ Sources Build Phase ID: {buildPhaseID}");
+    
+        // 2. 添加 .h 文件到项目结构
+        string headerPath = Path.Combine(ExtensionName, "NotificationService.h");
+        string headerGuid = proj.AddFile(headerPath, headerPath, PBXSourceTree.Source);
+        Debug.Log($"✅ Header 文件添加到项目: {headerGuid}");
+    
+        // 3. 添加 .m 文件到项目结构
+        string sourcePath = Path.Combine(ExtensionName, "NotificationService.m");
+        string sourceGuid = proj.AddFile(sourcePath, sourcePath, PBXSourceTree.Source);
+        Debug.Log($"✅ Source 文件添加到项目: {sourceGuid}");
+    
+        // 4. 将文件添加到编译阶段(确保 Target Membership 正确)
+        proj.AddFileToBuildSection(extGuid, buildPhaseID, headerGuid);
+        proj.AddFileToBuildSection(extGuid, buildPhaseID, sourceGuid);
+        Debug.Log($"✅ 文件已添加到编译阶段");
+    
+        // 5. 确保 Info.plist 也被添加到项目
+        string plistPath = Path.Combine(ExtensionName, "Info.plist");
+        string plistGuid = proj.AddFile(plistPath, plistPath, PBXSourceTree.Source);
+        Debug.Log($"✅ Info.plist 添加到项目: {plistGuid}");
+    
+        Debug.Log("🎯 所有文件已成功添加到 Xcode 项目结构!");
+    }
+
+
+    private static void ConfigureCodeSigning(PBXProject proj, string extGuid)
+    {
+        string teamId = PlayerSettings.iOS.appleDeveloperTeamID;
+        if (!string.IsNullOrEmpty(teamId))
+        {
+            proj.SetBuildProperty(extGuid, "DEVELOPMENT_TEAM", teamId);
+            proj.SetBuildProperty(extGuid, "CODE_SIGN_STYLE", "Automatic");
+        }
+    }
+
+    private static void MovePrebuiltSourceFiles(string buildPath)
+    {
+        string extDir = Path.Combine(buildPath, ExtensionName);
+        
+        // 确保目录存在
+        if (!Directory.Exists(extDir))
+            Directory.CreateDirectory(extDir);
+
+        // 源文件路径(Plugins/iOS 目录)
+        string sourceDir = Path.Combine(Application.dataPath, "Plugins", "iOS");
+        
+        // 移动 .h 文件
+        string headerSource = Path.Combine(sourceDir, "NotificationService.h");
+        string headerDest = Path.Combine(extDir, "NotificationService.h");
+        if (File.Exists(headerSource))
+        {
+            // 如果目标文件已存在,先删除
+            if (File.Exists(headerDest))
+            {
+                File.Delete(headerDest);
+                Debug.Log("🗑️ 删除已存在的目标文件: " + headerDest);
+            }
+            
+            File.Move(headerSource, headerDest);
+            Debug.Log("✅ 移动 NotificationService.h 文件");
+        }
+        else
+        {
+            Debug.LogError("❌ 找不到源文件: " + headerSource);
+        }
+
+        // 移动 .m 文件
+        string sourceSource = Path.Combine(sourceDir, "NotificationService.m");
+        string sourceDest = Path.Combine(extDir, "NotificationService.m");
+        if (File.Exists(sourceSource))
+        {
+            // 如果目标文件已存在,先删除
+            if (File.Exists(sourceDest))
+            {
+                File.Delete(sourceDest);
+                Debug.Log("🗑️ 删除已存在的目标文件: " + sourceDest);
+            }
+            
+            File.Move(sourceSource, sourceDest);
+            Debug.Log("✅ 移动 NotificationService.m 文件");
+        }
+        else
+        {
+            Debug.LogError("❌ 找不到源文件: " + sourceSource);
+        }
+
+        // 创建 Info.plist(如果需要)
+        string plistPath = Path.Combine(extDir, "Info.plist");
+        if (!File.Exists(plistPath))
+        {
+            File.WriteAllText(plistPath, PureInfoPlistContent);
+            Debug.Log("✅ 创建 Info.plist 文件");
+        }
+    }
+
+    private static void ConfigurePushNotificationsCapability(PBXProject proj, string extGuid, string buildPath)
+    {
+        Debug.Log("🔧 开始配置 Push Notifications 能力...");
+        
+        // 1. 添加 Push Notifications 能力
+        proj.AddCapability(extGuid, PBXCapabilityType.PushNotifications);
+        Debug.Log("✅ 添加 Push Notifications 能力");
+        
+        // 2. 确保签名配置正确
+        string teamId = PlayerSettings.iOS.appleDeveloperTeamID;
+        if (!string.IsNullOrEmpty(teamId))
+        {
+            proj.SetBuildProperty(extGuid, "DEVELOPMENT_TEAM", teamId);
+            proj.SetBuildProperty(extGuid, "CODE_SIGN_STYLE", "Automatic");
+            Debug.Log($"✅ 配置开发团队: {teamId}");
+        }
+        
+        Debug.Log("✅ Push Notifications 能力配置完成");
+    }
+
+
+
+    // 纯净的 Info.plist 内容
+    private static readonly string PureInfoPlistContent =
+@"<?xml version=""1.0"" encoding=""UTF-8""?>
+<!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd"">
+<plist version=""1.0"">
+<dict>
+    <key>CFBundleDevelopmentRegion</key>
+    <string>$(DEVELOPMENT_LANGUAGE)</string>
+    <key>CFBundleDisplayName</key>
+    <string>NotificationService</string>
+    <key>CFBundleExecutable</key>
+    <string>$(EXECUTABLE_NAME)</string>
+    <key>CFBundleIdentifier</key>
+    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+    <key>CFBundleInfoDictionaryVersion</key>
+    <string>6.0</string>
+    <key>CFBundleName</key>
+    <string>$(PRODUCT_NAME)</string>
+    <key>CFBundlePackageType</key>
+    <string>XPC!</string>
+    <key>CFBundleShortVersionString</key>
+    <string>1.0</string>
+    <key>CFBundleVersion</key>
+    <string>1</string>
+    <key>NSExtension</key>
+    <dict>
+        <key>NSExtensionPointIdentifier</key>
+        <string>com.apple.usernotifications.service</string>
+        <key>NSExtensionPrincipalClass</key>
+        <string>NotificationService</string>
+    </dict>
+</dict>
+</plist>";
+}
+
+#endif
+```
+
+## NotificationService.h
+
+``` cpp
+//
+//  NotificationService.h
+//  NotificationService
+//
+//  Created by unity on 2025/8/23.
+//
+
+#import <UserNotifications/UserNotifications.h>
+
+@interface NotificationService : UNNotificationServiceExtension
+
+@end
+
+```
+
+## NotificationService.m
+
+``` cpp
+//
+//  NotificationService.m
+//  NotificationService
+//
+//  Created by unity on 2025/8/23.
+//
+
+#import "NotificationService.h"
+#import <UIKit/UIKit.h>
+
+@interface NotificationService ()
+
+@property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver);
+@property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent;
+
+@end
+
+@implementation NotificationService
+
+- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
+    
+    NSLog(@"✅ Extension被调用!");
+    NSLog(@"📦 原始通知内容: %@", request.content.userInfo);
+    
+    self.contentHandler = contentHandler;
+    self.bestAttemptContent = [request.content mutableCopy];
+    
+    self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.title];
+    
+    // 1. 从userInfo中获取图片URL
+    NSDictionary *userInfo = request.content.userInfo;
+    NSString *imageUrlString = nil;
+    
+    // 检查FCM格式的图片URL (fcm_options -> image)
+    if (userInfo[@"fcm_options"] && userInfo[@"fcm_options"][@"image"]) {
+        imageUrlString = userInfo[@"fcm_options"][@"image"];
+        NSLog(@"🖼️ 找到FCM图片URL: %@", imageUrlString);
+    }
+    // 也可以检查其他自定义字段
+    else if (userInfo[@"image_url"]) {
+        imageUrlString = userInfo[@"image_url"];
+        NSLog(@"🖼️ 找到自定义图片URL: %@", imageUrlString);
+    }
+    
+    // 2. 如果没有图片URL,直接返回原始内容
+    if (!imageUrlString) {
+        NSLog(@"⚠️ 未找到图片URL,使用原始内容");
+        self.contentHandler(self.bestAttemptContent);
+        return;
+    }
+    
+    // 3. 下载图片并添加到通知
+    [self loadAttachmentForUrlString:imageUrlString completionHandler:^(UNNotificationAttachment *attachment) {
+        if (attachment) {
+            NSLog(@"✅ 图片下载成功,添加到通知");
+            self.bestAttemptContent.attachments = @[attachment];
+        } else {
+            NSLog(@"❌ 图片下载失败,使用原始内容");
+        }
+        
+        // 4. 最终返回通知内容
+        self.contentHandler(self.bestAttemptContent);
+    }];
+}
+
+- (void)loadAttachmentForUrlString:(NSString *)urlString completionHandler:(void (^)(UNNotificationAttachment *))completionHandler {
+    
+    __block UNNotificationAttachment *attachment = nil;
+    NSURL *attachmentURL = [NSURL URLWithString:urlString];
+    
+    if (!attachmentURL) {
+        completionHandler(nil);
+        return;
+    }
+    
+    NSLog(@"🌐 开始下载图片: %@", urlString);
+    
+    NSURLSession *session = [NSURLSession sharedSession];
+    NSURLSessionDownloadTask *task = [session downloadTaskWithURL:attachmentURL completionHandler:^(NSURL *temporaryFileLocation, NSURLResponse *response, NSError *error) {
+        
+        // 检查下载错误
+        if (error) {
+            NSLog(@"❌ 图片下载错误: %@", error);
+            completionHandler(nil);
+            return;
+        }
+        
+        // 检查文件是否存在
+        if (!temporaryFileLocation) {
+            NSLog(@"❌ 临时文件位置为空");
+            completionHandler(nil);
+            return;
+        }
+        
+        // 获取文件扩展名
+        NSString *fileExtension = [self getFileExtensionForResponse:response] ?: @"jpg";
+        NSLog(@"📄 文件扩展名: %@", fileExtension);
+        
+        // 创建唯一文件名
+        NSString *uniqueFileName = [NSString stringWithFormat:@"%@.%@", [[NSUUID UUID] UUIDString], fileExtension];
+        NSString *tempFile = [NSTemporaryDirectory() stringByAppendingPathComponent:uniqueFileName];
+        
+        // 移动文件到临时目录
+        NSError *moveError = nil;
+        [[NSFileManager defaultManager] moveItemAtURL:temporaryFileLocation toURL:[NSURL fileURLWithPath:tempFile] error:&moveError];
+        
+        if (moveError) {
+            NSLog(@"❌ 移动文件错误: %@", moveError);
+            completionHandler(nil);
+            return;
+        }
+        
+        // 创建通知附件
+        attachment = [UNNotificationAttachment attachmentWithIdentifier:@"image"
+                                                                    URL:[NSURL fileURLWithPath:tempFile]
+                                                                options:nil
+                                                                  error:&moveError];
+        
+        if (moveError) {
+            NSLog(@"❌ 创建附件错误: %@", moveError);
+            completionHandler(nil);
+            return;
+        }
+        
+        completionHandler(attachment);
+    }];
+    
+    [task resume];
+}
+
+- (NSString *)getFileExtensionForResponse:(NSURLResponse *)response {
+    if ([response isKindOfClass:[NSHTTPURLResponse class]]) {
+        NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *)response;
+        NSString *contentType = httpResponse.allHeaderFields[@"Content-Type"];
+        
+        if ([contentType isEqualToString:@"image/jpeg"]) return @"jpg";
+        if ([contentType isEqualToString:@"image/jpg"]) return @"jpg";
+        if ([contentType isEqualToString:@"image/png"]) return @"png";
+        if ([contentType isEqualToString:@"image/gif"]) return @"gif";
+        if ([contentType isEqualToString:@"image/webp"]) return @"webp";
+    }
+    
+    // 从URL路径推断扩展名
+    NSString *pathExtension = response.URL.pathExtension;
+    if (pathExtension.length > 0) {
+        return pathExtension;
+    }
+    
+    return @"jpg"; // 默认使用jpg
+}
+
+- (void)serviceExtensionTimeWillExpire {
+    NSLog(@"⏰ Extension处理超时,使用原始内容");
+    self.contentHandler(self.bestAttemptContent);
+}
+
+@end
+
+```

+ 960 - 0
笔记文件/2.笔记/灵光系统 临时记录_第二章.md

@@ -0,0 +1,960 @@
+## FirebaseCloudMessage.cs
+
+``` cs
+using System;
+using Firebase.Extensions;
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using Firebase;
+using UnityEngine;
+using UnityEngine.Networking;
+using Newtonsoft.Json;
+using UnityEngine.Android;
+using Firebase.Extensions;
+
+/// <summary>
+/// FCM推送插件 & 灵光系统
+/// </summary>
+public class FirebaseCloudMessage : MonoBehaviour
+{
+
+    private static string firebaseMessageReportUrl = String.Empty; //灵光平台 上报地址
+    
+    private static string _android_channel; //安卓渠道号(和服务端确认即可)
+    private static string _ios_channel; //ios渠道号(和服务端确认即可)
+    private static string _language; //语言
+    private static string _country; //国家
+    private static string fcmToken = String.Empty; //token of fcm令牌
+    
+    private static GameObject firebaseMessageUtilGameObject;
+    private static string userID; //玩家ID
+    private static bool inited = false; //是否初始化完成
+
+    private string _platform; //移动平台
+    private string _channel; //渠道号
+
+    // private static FirebaseApp app;
+
+    /// <summary>
+    /// 初始化
+    /// </summary>
+    /// <param name="appID">应用代号</param>
+    /// <param name="ReportUrl">上报地址</param>
+    /// <param name="android_channel">安卓渠道</param>
+    /// <param name="ios_channel">ios渠道</param>
+    /// <param name="language">语言</param>
+    /// <param name="country">国家</param>
+    public static void Init(string appID,string ReportUrl,string android_channel = "2001",string ios_channel = "1001",string language = "en",string country = "US")
+    {
+        
+        Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => {
+            var dependencyStatus = task.Result;
+            if (dependencyStatus == Firebase.DependencyStatus.Available) {
+                // Create and hold a reference to your FirebaseApp,
+                // where app is a Firebase.FirebaseApp property of your application class.
+                // app = Firebase.FirebaseApp.DefaultInstance;
+                firebaseMessageReportUrl = $"{ReportUrl}/api/app/{appID}/user/update";
+                _android_channel = android_channel;
+                _ios_channel = ios_channel;
+                _language = language;
+                _country = country;
+        
+                RequestNotificationPermission();
+
+                // Set a flag here to indicate whether Firebase is ready to use by your app.
+            } 
+            else {
+                Debug.Log("当前设备 谷歌框架等依赖缺失 不支持Firebase");
+                // UnityEngine.Debug.LogError(System.String.Format(
+                //     "Could not resolve all Firebase dependencies: {0}", dependencyStatus));
+                // Firebase Unity SDK is not safe to use here.
+            }
+        });
+    }
+
+    /// <summary>
+    /// 设置玩家ID
+    /// </summary>
+    /// <param name="userid"></param>
+    public static void SetUserID(string userid)
+    {
+#if Firebase_Debug
+        Debug.Log($"Firebase 功能测试 设置玩家ID{userid}");
+#endif
+        userID = userid;
+#if !UNITY_EDITOR
+        // 设置完用户ID后,初始化Firebase获取token
+        GetInstance().InitializeFirebase();
+#endif
+    }
+    /// <summary>
+    /// 订阅消息主题,订阅成功后可以通过主题推送
+    /// </summary>
+    /// <param name="topic"></param>
+    public static void SubscribeTopic(string topicJsonStr)
+    {
+        if (String.IsNullOrEmpty(fcmToken))
+        {
+            Debug.LogError("灵光系统 无法获得fcm token令牌");
+            return;
+        }
+
+        //检测json键值合法性
+        Dictionary<string, string> paramsMap = JsonConvert.DeserializeObject<Dictionary<string, string>>(topicJsonStr);
+        var (isValid, error) = CheckJsonDataValid(paramsMap);
+        if (!isValid)
+        {
+            Debug.LogWarning($"Failed to subscribe to the topic: {error}");
+            return;
+        }
+
+        foreach (string key in paramsMap.Keys)
+        {
+            Firebase.Messaging.FirebaseMessaging.SubscribeAsync(paramsMap[key]).ContinueWithOnMainThread(task =>
+            {
+                if (task.IsFaulted)
+                    Debug.LogWarning($"Failed to subscribe to the topic: {task.Exception}");
+            });
+        }
+    }
+
+    /// <summary>
+    /// 取消订阅消息主题
+    /// </summary>
+    /// <param name="topicJsonStr"></param>
+    public static void unSubscribeTopic(string topicJsonStr)
+    {
+        if (String.IsNullOrEmpty(fcmToken))
+        {
+            Debug.LogError("灵光系统 无法获得fcm token令牌");
+            return;
+        }
+
+        //检测json键值合法性
+        Dictionary<string, string> paramsMap = JsonConvert.DeserializeObject<Dictionary<string, string>>(topicJsonStr);
+        var (isValid, error) = CheckJsonDataValid(paramsMap);
+        if (!isValid)
+        {
+            Debug.LogWarning($"Failed to subscribe to the topic: {error}");
+            return;
+        }
+
+        foreach (string key in paramsMap.Keys)
+        {
+            Firebase.Messaging.FirebaseMessaging.UnsubscribeAsync(paramsMap[key]).ContinueWithOnMainThread(task =>
+            {
+                if (task.IsFaulted)
+                    Debug.LogWarning($"Failed to subscribe to the topic: {task.Exception}");
+            });
+        }
+    }
+
+    private void OnDestroy()
+    {
+        RemoveFirebase();
+    }
+    
+    /// <summary>
+    /// 获取单例实例
+    /// </summary>
+    private static FirebaseCloudMessage GetInstance()
+    {
+        if (firebaseMessageUtilGameObject == null)
+        {
+            firebaseMessageUtilGameObject = new GameObject();
+            DontDestroyOnLoad(firebaseMessageUtilGameObject);
+            firebaseMessageUtilGameObject.name = "firebaseMessage";
+            firebaseMessageUtilGameObject.AddComponent<FirebaseCloudMessage>();
+        }
+        return firebaseMessageUtilGameObject.GetComponent<FirebaseCloudMessage>();
+    }
+
+    /// <summary>
+    /// 初始化FirebaseCloudMessage消息推送
+    /// </summary>
+    private void InitializeFirebase()
+    {
+        // 避免重复初始化
+        if (inited)
+        {
+            return;
+        }
+        inited = true;
+        
+        Firebase.Messaging.FirebaseMessaging.MessageReceived += OnMessageReceived;
+        Firebase.Messaging.FirebaseMessaging.TokenReceived += OnTokenReceived;
+    }
+
+    /// <summary>
+    /// 移除FirebaseCloudMessage消息推送
+    /// </summary>
+    private void RemoveFirebase()
+    {
+        Firebase.Messaging.FirebaseMessaging.MessageReceived -= OnMessageReceived;
+        Firebase.Messaging.FirebaseMessaging.TokenReceived -= OnTokenReceived;
+        inited = false;
+    }
+    
+    private void OnMessageReceived(object sender, Firebase.Messaging.MessageReceivedEventArgs e)
+    {
+        Dictionary<string, object> args = new Dictionary<string, object>();
+        // args.Add(NativeEventConst.EVENT_ID_KEY, NativeEventConst.FIREBASE_MESSAGE_RECEIVED);
+        //
+        var notification = e.Message.Notification;
+        if (notification != null)
+        {
+            args.Add("title", notification.Title);
+            args.Add("body", notification.Body);
+        
+#if !UNITY_EDITOR && UNITY_ANDROID
+            var android = notification.Android;
+            if (android != null)
+            {
+                args.Add("android channel_id", android.ChannelId);
+            }
+#endif
+        }
+
+        if (e.Message.From.Length > 0)
+        {
+            args.Add("from", e.Message.From);
+        }
+        
+        if (e.Message.Link != null)
+        {
+            args.Add("link", e.Message.Link.ToString());
+        }
+        
+        if (e.Message.Data.Count > 0)
+        {
+            foreach (KeyValuePair<string, string> item in e.Message.Data)
+            {
+                args.Add(item.Key, item.Value);
+            }
+        }
+        
+    }
+
+    private void OnTokenReceived(object sender, Firebase.Messaging.TokenReceivedEventArgs token)
+    {
+        fcmToken = token.Token;
+        
+        // 获取到token后,立即发送给服务器(此时已经有用户ID了)
+        PostToServer();
+    }
+
+
+    /// <summary>
+    /// 发送给灵光服务器
+    /// </summary>
+    private void PostToServer()
+    {
+        SetPlatform();
+        var deviceData = new
+        {
+            token = fcmToken,
+            platform = _platform,
+            channel = _channel,
+            device = SystemInfo.deviceUniqueIdentifier,
+            uid = userID,
+            language = _language,
+            country = _country,
+        };
+
+        string jsonString = JsonConvert.SerializeObject(deviceData);
+        CoroutineManager.Instance.StartManagedCoroutine(PostRequest(firebaseMessageReportUrl, jsonString));
+    }
+
+    /// <summary>
+    /// 设置 移动平台 & 渠道号相关(渠道号和服务端确认即可)
+    /// </summary>
+    private void SetPlatform()
+    {
+        switch (Application.platform)
+        {
+            case RuntimePlatform.Android:
+                _platform = "android";
+                _channel = _android_channel;
+                break;
+            case RuntimePlatform.IPhonePlayer:
+                _platform = "ios";
+                _channel = _ios_channel;
+                break;
+        }
+    }
+
+    /// <summary>
+    /// 检测json合法性
+    /// </summary>
+    /// <param name="jsonStr"></param>
+    /// <returns></returns>
+    private static (bool isValid, string error) CheckJsonDataValid<TKey, TValue>(Dictionary<TKey, TValue> jsonMap)
+    {
+        if (jsonMap == null)
+            return (false, "The dictionary object is null");
+
+        var emptyItems = jsonMap
+            .Where(kv => IsInvalidValue(kv.Value))
+            .Select(kv => kv.Key.ToString())
+            .ToList();
+
+        return emptyItems.Count == 0
+            ? (true, null)
+            : (false, $"The following key values are empty: {string.Join(", ", emptyItems)}");
+    }
+
+    private static bool IsInvalidValue<T>(T value)
+    {
+        if (value == null) return true;
+        if (value is string str) return string.IsNullOrEmpty(str);
+        return false;
+    }
+    
+    
+    /// <summary>
+    /// 主动申请通知权限
+    /// </summary>
+    private static void RequestNotificationPermission()
+    {
+#if !UNITY_EDITOR
+#if UNITY_ANDROID
+        // 检查Android版本
+        if (GetAndroidVersion() >= 33) // Android 13+
+        {
+            // 检查是否已经有通知权限
+            if (!CheckNotificationPermissionStatus())
+            {
+                Debug.Log("请求通知权限...");
+                // 请求通知权限
+                Permission.RequestUserPermission("android.permission.POST_NOTIFICATIONS");
+                
+                // 添加权限回调
+                PermissionCallbacks callbacks = new PermissionCallbacks();
+                callbacks.PermissionGranted += (permissionName) => {
+                    Debug.Log($"通知权限已授予: {permissionName}");
+                };
+                callbacks.PermissionDenied += (permissionName) => {
+                    Debug.LogWarning($"通知权限被拒绝: {permissionName}");
+                };
+                callbacks.PermissionDeniedAndDontAskAgain += (permissionName) => {
+                    Debug.LogWarning($"通知权限被拒绝且不再询问: {permissionName}");
+                };
+                
+                Permission.RequestUserPermission("android.permission.POST_NOTIFICATIONS", callbacks);
+            }
+            else
+            {
+                Debug.Log("已有通知权限");
+            }
+        }
+        else
+        {
+            Debug.Log("Android版本低于13,无需申请通知权限");
+        }
+#elif UNITY_IOS
+        // 使用iOSNotificationPermission类处理iOS通知权限
+        if (!iOSNotificationPermission.CheckPermission())
+        {
+            Debug.Log("请求iOS通知权限...");
+            iOSNotificationPermission.RequestPermission();
+        }
+        else
+        {
+            Debug.Log("已有iOS通知权限");
+        }
+#else
+        Debug.Log("其他平台,无需申请通知权限");
+#endif
+#else
+        Debug.Log("Unity编辑器模式,跳过通知权限申请");
+#endif
+    }
+    
+    /// <summary>
+    /// 获取Android版本
+    /// </summary>
+    private static int GetAndroidVersion()
+    {
+#if !UNITY_EDITOR && UNITY_ANDROID
+        try
+        {
+            using (AndroidJavaClass buildVersion = new AndroidJavaClass("android.os.Build$VERSION"))
+            {
+                return buildVersion.GetStatic<int>("SDK_INT");
+            }
+        }
+        catch (Exception e)
+        {
+            Debug.LogError($"获取Android版本失败: {e.Message}");
+            return 0;
+        }
+#else
+        return 0; // 非Android平台返回0
+#endif
+    }
+    
+    /// <summary>
+    /// 检查通知权限状态
+    /// </summary>
+    private static bool CheckNotificationPermissionStatus()
+    {
+#if !UNITY_EDITOR && UNITY_ANDROID
+        if (GetAndroidVersion() >= 33)
+        {
+            try
+            {
+                using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
+                using (AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
+                using (AndroidJavaObject context = currentActivity.Call<AndroidJavaObject>("getApplicationContext"))
+                using (AndroidJavaClass notificationManager = new AndroidJavaClass("android.app.NotificationManager"))
+                using (AndroidJavaObject notificationService = context.Call<AndroidJavaObject>("getSystemService", "notification"))
+                {
+                    // 直接调用areNotificationsEnabled方法,因为Android 13+肯定支持这个方法
+                    return notificationService.Call<bool>("areNotificationsEnabled");
+                }
+            }
+            catch (Exception e)
+            {
+                Debug.LogError($"检查通知权限状态失败: {e.Message}");
+            }
+        }
+        return true; // 低版本默认返回true
+#elif !UNITY_EDITOR && UNITY_IOS
+        // 使用iOSNotificationPermission类检查iOS通知权限状态
+        return iOSNotificationPermission.CheckPermission();
+#else
+        // Unity编辑器或其他平台,默认返回true
+        return true;
+#endif
+    }
+    
+    [System.Serializable]
+    private class PostResponse
+    {
+        public int code;
+        public string message;
+        public string data;
+    }
+
+    /// <summary>
+    /// 上报灵光事件
+    /// </summary>
+    /// <param name="url"></param>
+    /// <param name="jsonData"></param>
+    /// <returns></returns>
+    private static IEnumerator PostRequest(string url,string jsonData)
+    {
+
+#if Firebase_Debug
+        Debug.Log($"Firebase 功能测试,发送链接:{url}");
+        Debug.Log($"Firebase 功能测试,发送数据:{jsonData}");
+#endif
+
+        using (UnityWebRequest request = UnityWebRequest.PostWwwForm(url, "POST"))
+        {
+            byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonData);
+
+            request.uploadHandler = new UploadHandlerRaw(bodyRaw);
+            request.downloadHandler = new DownloadHandlerBuffer();
+            request.SetRequestHeader("Content-Type", "application/json");
+
+            yield return request.SendWebRequest();
+
+            if (request.result != UnityWebRequest.Result.Success)
+            {
+#if Firebase_Debug
+                Debug.Log($"Firebase 功能测试,错误码:{request.error}");
+#endif
+                Debug.LogWarning("Equipment reporting failed." + request.error);
+                yield break;
+            }
+
+            var response = JsonUtility.FromJson<PostResponse>(request.downloadHandler.text);
+            if (response.code != 0)
+            {
+                Debug.LogError($"灵光系统 错误码:{response.code}");
+            }
+            else
+            {
+#if Firebase_Debug
+                Debug.Log($"Firebase 功能测试,发送回包:{request.downloadHandler.text}");
+#endif
+            }
+        };
+    }
+}
+```
+
+## NotificationServiceExtensionCreator.cs
+
+``` cs
+#if UNITY_IOS
+using System.IO;
+using UnityEditor;
+using UnityEditor.Callbacks;
+using UnityEditor.iOS.Xcode;
+using UnityEditor.iOS.Xcode.Extensions;
+using UnityEngine;
+using UnityEditor.iOS.Xcode.PBX;
+using System.Diagnostics;
+using System.Threading;
+using System;
+using Debug = UnityEngine.Debug;
+
+public static class FirebaseNotificationServiceExtensionCreator
+{
+    private const string ExtensionName = "FirebaseNotificationService";
+
+    [PostProcessBuild(200)]
+    public static void OnPostProcessBuild(BuildTarget target, string buildPath)
+    {
+        if (target != BuildTarget.iOS) return;
+#if Firebase_Debug
+        Debug.Log("FireBase功能测试 开始创建 Firebase Notification Service Extension...");
+#endif
+
+        string projPath = PBXProject.GetPBXProjectPath(buildPath);
+        PBXProject proj = new PBXProject();
+        proj.ReadFromFile(projPath);
+
+#if UNITY_2019_3_OR_NEWER
+        string mainTargetGuid = proj.GetUnityMainTargetGuid();
+#else
+        string mainTargetGuid = proj.TargetGuidByName(PBXProject.GetUnityTargetName());
+#endif
+        
+        // 1. 创建 Extension Target(这会自动创建基础文件)
+        string extGuid = proj.AddAppExtension(mainTargetGuid, ExtensionName, ExtensionName, 
+            $"{ExtensionName}/Info.plist");
+
+#if Firebase_Debug
+        Debug.Log($" FireBase功能测试 创建 Extension Target: {extGuid}");
+#endif
+        
+
+        // 2. 配置基本设置
+        proj.SetBuildProperty(extGuid, "PRODUCT_BUNDLE_IDENTIFIER", 
+            PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.iOS) + "." + ExtensionName);
+        proj.SetBuildProperty(extGuid, "ENABLE_BITCODE", "NO");
+        proj.SetBuildProperty(extGuid, "IPHONEOS_DEPLOYMENT_TARGET", "15.0");
+
+        // 3. 配置签名
+        ConfigureCodeSigning(proj, extGuid);
+
+        // 4. 移动预制的代码文件到扩展目录
+        MovePrebuiltSourceFiles(buildPath);
+
+        // 5. 添加系统框架
+        proj.AddFrameworkToProject(extGuid, "UserNotifications.framework", false);
+
+        // 6. 配置 Push Notifications 能力
+        ConfigurePushNotificationsCapability(proj, extGuid, buildPath);
+
+        // 7. 关键修复:确保文件被添加到 Xcode 项目结构和编译阶段
+        AddFilesToProjectStructure(proj, extGuid, buildPath);
+
+        // 8. 配置 Podfile
+        ConfigureFirebasePodfile(buildPath);
+
+        // 9. 保存工程
+        proj.WriteToFile(projPath);
+        AssetDatabase.Refresh();
+
+        // 10. 执行 pod install
+        PodInstallFirebaseNotificationService(buildPath);
+
+    }
+
+    // 关键方法:确保文件被添加到 Xcode 项目结构
+    private static void AddFilesToProjectStructure(PBXProject proj, string extGuid, string buildPath)
+    {
+#if Firebase_Debug
+        Debug.Log("FireBase功能测试 开始添加文件到 Xcode 项目结构...");
+#endif
+    
+        // 1. 获取或创建 Sources Build Phase
+        var buildPhaseID = proj.AddSourcesBuildPhase(extGuid);
+#if Firebase_Debug
+        Debug.Log($"FireBase功能测试 Sources Build Phase ID: {buildPhaseID}");
+#endif
+
+        // 2. 添加 .h 文件到项目结构
+        string headerPath = Path.Combine(ExtensionName, "FirebaseNotificationService.h");
+        string headerGuid = proj.AddFile(headerPath, headerPath, PBXSourceTree.Source);
+        
+#if Firebase_Debug
+        Debug.Log($"FireBase功能测试 Header 文件添加到项目: {headerGuid}");
+#endif
+    
+        // 3. 添加 .m 文件到项目结构
+        string sourcePath = Path.Combine(ExtensionName, "FirebaseNotificationService.m");
+        string sourceGuid = proj.AddFile(sourcePath, sourcePath, PBXSourceTree.Source);
+      
+#if Firebase_Debug
+        Debug.Log($"FireBase功能测试 Source 文件添加到项目: {sourceGuid}");
+#endif
+    
+        // 4. 将文件添加到编译阶段(确保 Target Membership 正确)
+        proj.AddFileToBuildSection(extGuid, buildPhaseID, headerGuid);
+        proj.AddFileToBuildSection(extGuid, buildPhaseID, sourceGuid);
+        
+#if Firebase_Debug
+        Debug.Log($"FireBase功能测试 文件已添加到编译阶段");
+#endif
+    
+        // 5. 确保 Info.plist 也被添加到项目
+        string plistPath = Path.Combine(ExtensionName, "Info.plist");
+        string plistGuid = proj.AddFile(plistPath, plistPath, PBXSourceTree.Source);
+        
+#if Firebase_Debug
+        Debug.Log($"FireBase功能测试 Info.plist 添加到项目: {plistGuid}");
+        Debug.Log("FireBase功能测试 🎯 所有文件已成功添加到 Xcode 项目结构!");
+#endif
+    
+    }
+
+    private static void ConfigureCodeSigning(PBXProject proj, string extGuid)
+    {
+        string teamId = PlayerSettings.iOS.appleDeveloperTeamID;
+        if (!string.IsNullOrEmpty(teamId))
+        {
+            proj.SetBuildProperty(extGuid, "DEVELOPMENT_TEAM", teamId);
+            proj.SetBuildProperty(extGuid, "CODE_SIGN_STYLE", "Automatic");
+        }
+    }
+
+    private static void MovePrebuiltSourceFiles(string buildPath)
+    {
+        string extDir = Path.Combine(buildPath, ExtensionName);
+        
+        // 确保目录存在
+        if (!Directory.Exists(extDir))
+            Directory.CreateDirectory(extDir);
+
+        // 源文件路径(Plugins/iOS 目录)
+        string sourceDir = Path.Combine(Application.dataPath, "Plugins", "iOS");
+        
+        // 移动 .h 文件
+        string headerSource = Path.Combine(sourceDir, "FirebaseNotificationService.h");
+        string headerDest = Path.Combine(extDir, "FirebaseNotificationService.h");
+        if (File.Exists(headerSource))
+        {
+            // 如果目标文件已存在,先删除
+            if (File.Exists(headerDest))
+            {
+                File.Delete(headerDest);
+                
+#if Firebase_Debug
+                Debug.Log("FireBase功能测试 删除已存在的目标文件: " + headerDest);
+#endif
+            }
+            
+            File.Copy(headerSource, headerDest);
+            
+#if Firebase_Debug
+            Debug.Log("FireBase功能测试 复制 FirebaseNotificationService.h 文件");
+#endif
+            
+        }
+        else
+        {
+            Debug.LogError("❌ 找不到源文件: " + headerSource);
+        }
+
+        // 移动 .m 文件
+        string sourceSource = Path.Combine(sourceDir, "FirebaseNotificationService.m");
+        string sourceDest = Path.Combine(extDir, "FirebaseNotificationService.m");
+        if (File.Exists(sourceSource))
+        {
+            // 如果目标文件已存在,先删除
+            if (File.Exists(sourceDest))
+            {
+                File.Delete(sourceDest);
+                
+#if Firebase_Debug
+                Debug.Log("FireBase功能测试️ 删除已存在的目标文件: " + sourceDest);
+#endif
+            }
+            
+            File.Copy(sourceSource, sourceDest);
+           
+#if Firebase_Debug
+            Debug.Log("FireBase功能测试 复制 FirebaseNotificationService.m 文件");
+#endif
+            
+        }
+        else
+        {
+            Debug.LogError("❌ 找不到源文件: " + sourceSource);
+        }
+
+        // 创建 Info.plist(如果需要)
+        string plistPath = Path.Combine(extDir, "Info.plist");
+        if (!File.Exists(plistPath))
+        {
+            File.WriteAllText(plistPath, FirebaseInfoPlistContent);
+#if Firebase_Debug
+            Debug.Log("FireBase功能测试 创建 Info.plist 文件");
+#endif
+        }
+    }
+
+    private static void ConfigurePushNotificationsCapability(PBXProject proj, string extGuid, string buildPath)
+    {
+        
+#if Firebase_Debug
+        Debug.Log("FireBase功能测试 开始配置 Push Notifications 能力...");
+#endif
+        
+        
+        // 1. 添加 Push Notifications 能力
+        proj.AddCapability(extGuid, PBXCapabilityType.PushNotifications);
+        
+#if Firebase_Debug
+        Debug.Log("FireBase功能测试 添加 Push Notifications 能力");
+#endif
+        
+        
+        // 2. 确保签名配置正确
+        string teamId = PlayerSettings.iOS.appleDeveloperTeamID;
+        if (!string.IsNullOrEmpty(teamId))
+        {
+            proj.SetBuildProperty(extGuid, "DEVELOPMENT_TEAM", teamId);
+            proj.SetBuildProperty(extGuid, "CODE_SIGN_STYLE", "Automatic");
+            
+            
+#if Firebase_Debug
+            Debug.Log($"FireBase功能测试 配置开发团队: {teamId}");
+#endif
+            
+        }
+        
+        
+#if Firebase_Debug
+        Debug.Log("FireBase功能测试 Push Notifications 能力配置完成");
+#endif
+        
+    }
+
+    /// <summary>
+    /// 配置 Podfile,添加 Firebase 相关依赖
+    /// </summary>
+    private static void ConfigureFirebasePodfile(string buildPath)
+    {
+        string podfilePath = Path.Combine(buildPath, "Podfile");
+        
+        // 如果 Podfile 不存在,直接警告并返回
+        if (!File.Exists(podfilePath))
+        {
+            Debug.LogWarning("FireBase功能测试 Podfile 不存在,跳过 Firebase 依赖配置");
+            return;
+        }
+        
+        // 检查是否包含 FirebaseNotificationService target
+        string podfileContent = File.ReadAllText(podfilePath);
+        
+        if (!podfileContent.Contains("target 'FirebaseNotificationService'"))
+        {
+            // 检测 UnityFramework 中的 Firebase 版本
+            string firebaseVersion = DetectFirebaseVersionInUnityFramework(podfileContent);
+            
+            // 在文件末尾添加 FirebaseNotificationService target
+            string firebaseTargetContent = $@"
+
+target 'FirebaseNotificationService' do
+  pod 'Firebase/Messaging', '{firebaseVersion}'
+end
+
+use_frameworks! :linkage => :static";
+            
+            podfileContent += firebaseTargetContent;
+            File.WriteAllText(podfilePath, podfileContent);
+            
+#if Firebase_Debug
+            Debug.Log($"FireBase功能测试 在现有 Podfile 中添加 FirebaseNotificationService target,使用 Firebase 版本: {firebaseVersion}");
+#endif
+        }
+        else
+        {
+#if Firebase_Debug
+            Debug.Log("FireBase功能测试 FirebaseNotificationService target 已存在于 Podfile 中");
+#endif
+        }
+    }
+
+    /// <summary>
+    /// 检测 UnityFramework target 中的 Firebase 版本
+    /// </summary>
+    private static string DetectFirebaseVersionInUnityFramework(string podfileContent)
+    {
+        // 默认版本
+        string defaultVersion = "12.0.0";
+        
+        try
+        {
+            // 查找 UnityFramework target 中的 Firebase 版本
+            int unityFrameworkStart = podfileContent.IndexOf("target 'UnityFramework' do");
+            if (unityFrameworkStart == -1)
+            {
+                Debug.LogWarning("FireBase功能测试 未找到 UnityFramework target,使用默认版本: " + defaultVersion);
+                return defaultVersion;
+            }
+            
+            int unityFrameworkEnd = podfileContent.IndexOf("end", unityFrameworkStart);
+            if (unityFrameworkEnd == -1)
+            {
+                Debug.LogWarning("FireBase功能测试 UnityFramework target 格式错误,使用默认版本: " + defaultVersion);
+                return defaultVersion;
+            }
+            
+            // 提取 UnityFramework target 的内容
+            string unityFrameworkContent = podfileContent.Substring(unityFrameworkStart, unityFrameworkEnd - unityFrameworkStart);
+            
+            // 查找 Firebase 相关的 pod 声明
+            string[] firebasePods = { "Firebase/Analytics", "Firebase/Core", "Firebase/Messaging", "Firebase" };
+            string detectedVersion = null;
+            
+            foreach (string pod in firebasePods)
+            {
+                int podIndex = unityFrameworkContent.IndexOf($"pod '{pod}'");
+                if (podIndex != -1)
+                {
+                    // 找到 pod 声明,提取版本号
+                    int versionStart = unityFrameworkContent.IndexOf("'", podIndex + pod.Length + 6);
+                    if (versionStart != -1)
+                    {
+                        int versionEnd = unityFrameworkContent.IndexOf("'", versionStart + 1);
+                        if (versionEnd != -1)
+                        {
+                            detectedVersion = unityFrameworkContent.Substring(versionStart + 1, versionEnd - versionStart - 1);
+                            break;
+                        }
+                    }
+                }
+            }
+            
+            if (!string.IsNullOrEmpty(detectedVersion))
+            {
+#if Firebase_Debug
+                Debug.Log($"FireBase功能测试 检测到 UnityFramework 中的 Firebase 版本: {detectedVersion}");
+#endif
+                return detectedVersion;
+            }
+            else
+            {
+                Debug.LogWarning("FireBase功能测试 未在 UnityFramework 中找到 Firebase 版本信息,使用默认版本: " + defaultVersion);
+                return defaultVersion;
+            }
+        }
+        catch (Exception ex)
+        {
+            Debug.LogError($"FireBase功能测试 检测 Firebase 版本时发生错误: {ex.Message}");
+            return defaultVersion;
+        }
+    }
+
+    /// <summary>
+    /// 执行 pod install 安装 Firebase 通知扩展组件
+    /// </summary>
+    private static void PodInstallFirebaseNotificationService(string buildPath)
+    {
+        Debug.Log("=== 开始执行 Pod 安装流程 ===");
+        
+        if (!Directory.Exists(buildPath))
+        {
+            Debug.LogError($"Xcode工程路径不存在: {buildPath}");
+            return;
+        }
+
+        string podfilePath = Path.Combine(buildPath, "Podfile");
+        if (!File.Exists(podfilePath))
+        {
+            Debug.LogError($"Podfile文件不存在: {podfilePath}");
+            return;
+        }
+
+        try
+        {
+            // 转义路径中的单引号
+            string escapedPath = buildPath.Replace("'", "'\\''");
+            
+            string appleScript = $@"
+            tell application ""Terminal""
+                activate
+                do script ""cd '{escapedPath}' && pod install && exit""
+            end tell";
+
+            Debug.Log($"正在启动终端执行pod install...");
+            
+            using (Process process = new Process())
+            {
+                process.StartInfo = new ProcessStartInfo
+                {
+                    FileName = "/usr/bin/osascript",
+                    Arguments = $"-e '{appleScript}'",
+                    UseShellExecute = false,
+                    RedirectStandardError = true,
+                    CreateNoWindow = true
+                };
+
+                process.Start();
+                process.WaitForExit();
+
+                if (process.ExitCode != 0)
+                {
+                    string error = process.StandardError.ReadToEnd();
+                    Debug.LogError($"启动终端失败: {error}");
+                }
+                else
+                {
+                    Debug.Log("已启动终端执行pod install,请稍后在终端中查看结果");
+                }
+            }
+        }
+        catch (Exception ex)
+        {
+            Debug.LogError($"执行命令时发生异常: {ex}");
+        }
+        
+        Debug.Log("=== Pod安装流程结束 ===");
+    }
+
+    // Firebase 的 Info.plist 内容
+    private static readonly string FirebaseInfoPlistContent =
+@"<?xml version=""1.0"" encoding=""UTF-8""?>
+<!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd"">
+<plist version=""1.0"">
+<dict>
+    <key>CFBundleDevelopmentRegion</key>
+    <string>$(DEVELOPMENT_LANGUAGE)</string>
+    <key>CFBundleDisplayName</key>
+    <string>FirebaseNotificationService</string>
+    <key>CFBundleExecutable</key>
+    <string>$(EXECUTABLE_NAME)</string>
+    <key>CFBundleIdentifier</key>
+    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+    <key>CFBundleInfoDictionaryVersion</key>
+    <string>6.0</string>
+    <key>CFBundleName</key>
+    <string>$(PRODUCT_NAME)</string>
+    <key>CFBundlePackageType</key>
+    <string>XPC!</string>
+    <key>CFBundleShortVersionString</key>
+    <string>1.0</string>
+    <key>CFBundleVersion</key>
+    <string>1</string>
+    <key>NSExtension</key>
+    <dict>
+        <key>NSExtensionPointIdentifier</key>
+        <string>com.apple.usernotifications.service</string>
+        <key>NSExtensionPrincipalClass</key>
+        <string>FirebaseNotificationService</string>
+    </dict>
+</dict>
+</plist>";
+
+}
+
+#endif
+```

+ 4 - 1
笔记文件/2.笔记/灵感_文档.md

@@ -6,4 +6,7 @@
 https://inspire.larksuite.com/wiki/HNxDwfe4OiqB9ZkGBHhuJbYAs6e?fromScene=spaceOverview
 
 SDK架构管理链接:
-https://inspire.sg.larksuite.com/wiki/Op5Rwck6fitA5zkHQf7lxASmgmg?fromScene=spaceOverview
+https://inspire.sg.larksuite.com/wiki/Op5Rwck6fitA5zkHQf7lxASmgmg?fromScene=spaceOverview
+
+日报链接:
+https://inspire.sg.larksuite.com/base/Jt6Db3kPcaD7qjsV7QSlqMJMge2?table=tblB8Zo0JEIaWvCA&view=vewSYXThbk

+ 0 - 0
笔记文件/2.笔记/编码.md → 笔记文件/2.笔记/编码 读后感.md


+ 2 - 1
笔记文件/2.笔记/网页游戏平台.md

@@ -3,4 +3,5 @@
 https://www.crazygames.com/
 https://poki.com/
 
-网页图片参考:https://octodex.github.com/images/codercat.jpg
+网页图片参考:https://octodex.github.com/images/codercat.jpg
+https://octodex.github.com/images/mona-lovelace.jpg

+ 2 - 2
笔记文件/日记/2025_05_09_星期五.md

@@ -29,8 +29,8 @@
 - [x] 处理一下 多表联结查询统计 数据分析
 - [ ] 弄一下 数据库 存储过程
 ---
-[[数据密集型应用系统设计]]
-[[编码]]
+[[数据密集型应用系统设计 读后感]]
+[[编码 读后感]]
 [[python性能监控 数据分析]]
 [[普吉岛行程参考]]
 # Journal

+ 1 - 1
笔记文件/日记/2025_08_05_星期二.md

@@ -23,5 +23,5 @@
 
 - [x] 记得处理一下 Plink的安卓依赖库逻辑
 ---
-[[灵光系统 临时记录]]
+[[灵光系统 临时记录_第一章]]
 # Journal

+ 2 - 2
笔记文件/日记/2025_08_25_星期一.md

@@ -24,10 +24,10 @@
 - [x] 记得要剃胡子
 - [x] 记得要准备一个红包
 - [x] 顺带测试一下 安卓灵光推送,图片是否正常
-- [ ] 把安卓依赖库的优化 同步到Plink框架
+- [x] 把安卓依赖库的优化 同步到Plink框架
 - [ ] 记得周末是预约了,更换高压燃油泵
 - [x] 写一下工作占比
-- [ ] 进阶一下,添加podfile,使用firebase的接口
+- [x] 进阶一下,添加podfile,使用firebase的接口
 ---
 [[keystore的hash确认]]
 [[hash设备指纹验证]]

+ 33 - 0
笔记文件/日记/2025_08_28_星期四.md

@@ -0,0 +1,33 @@
+
+09:14
+
+###### [[unity日常积累]]
+
+###### [[节奏天国]]
+
+###### [[帧同步王者荣耀]]
+
+###### [[从零开发跨平台通用日志插件]]
+
+###### [[通用异步网络通信库]]
+
+###### [[高性能定时系统实现]]
+
+###### [[学习资料]]
+
+###### [[其他]]
+
+#### [[看板]]
+
+# 今日任务
+
+- [ ] 今晚回去,先升级一下 车机系统
+- [ ] 今晚记得,把谍中谍7下载了
+- [x] 处理 提交一下 宏管理器
+- [ ] 记得说一声 外派打卡的事情
+- [ ] 梳理一下 明天外派 处理firebase sdk的事情
+- [ ] 解构一下 开源AkiBT行为树
+- [ ] 改对 plink拉取的cmd指令
+---
+[[灵光系统 临时记录]]
+# Journal

+ 1 - 1
笔记文件/看板/看板.md

@@ -12,6 +12,7 @@ kanban-plugin: basic
 
 ## 进行中
 
+- [ ] 行为树 算法实现
 - [ ] Spine动画制作
 - [ ] MMO状态同步组队战斗分布式滚服案例
 - [ ] Unity接入TapTap开发者服务
@@ -65,7 +66,6 @@ kanban-plugin: basic
 ## 后续计划
 
 - [ ] XLua框架搭建
-- [ ] 行为树 算法实现
 - [ ] HTN算法
 - [ ] 总结apk打包逻辑
 - [ ] MVC框架 复习

BIN
笔记文件/附件/宏管理器MacroManager.zip