Design_SubModule/Scripts/Editor/Design_Tools/Friends/InviteRewardEditor.cs
2026-03-16 14:56:09 +08:00

331 lines
11 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using UnityEditor;
using UnityEngine;
using OfficeOpenXml;
using Debug = UnityEngine.Debug;
namespace DesignTools.Friends
{
/// <summary>
/// 好友邀请奖励配置工具
/// 读取 Docs/config/Invite.xlsx 的 Reward Sheet仅支持编辑固定 4 档邀请奖励
/// </summary>
public class InviteRewardEditor : BaseDesignToolEditor
{
private const string INVITE_EXCEL_NAME = "Invite.xlsx";
private const string REWARD_SHEET_NAME = "Reward";
private const int COL_ID = 1;
private const int COL_NEED = 2;
private const int COL_ITEMS = 3;
private const int FIXED_STAGE_COUNT = 4;
private readonly List<InviteRewardData> rewardDataList = new List<InviteRewardData>();
[MenuItem("策划工具/好友/邀请奖励")]
public static void ShowWindow()
{
var window = GetWindow<InviteRewardEditor>("邀请奖励配置");
window.minSize = window.GetMinWindowSize();
window.Show();
}
protected override string GetDocsPathPrefKey()
{
return "InviteRewardEditor_DocsPath";
}
protected override string GetWindowTitle()
{
return "邀请奖励配置工具";
}
protected override Vector2 GetMinWindowSize()
{
return new Vector2(820, 520);
}
protected override void LoadConfigData()
{
LoadInviteRewardExcel();
}
protected override void DrawDataEditor()
{
DrawTips();
EditorGUILayout.Space(6);
DrawRewardTable();
}
protected override void SaveDataToExcel()
{
SaveToExcel();
}
private void DrawTips()
{
EditorGUILayout.BeginVertical("box");
EditorGUILayout.HelpBox("如果要添加阶段数量请联系开发组", MessageType.Warning);
if (rewardDataList.Count != FIXED_STAGE_COUNT)
{
EditorGUILayout.HelpBox(
$"当前 Reward Sheet 读取到 {rewardDataList.Count} 行有效数据,工具仅支持固定 {FIXED_STAGE_COUNT} 行配置。",
MessageType.Error);
}
bool hasParseError = false;
for (int i = 0; i < rewardDataList.Count; i++)
{
if (!rewardDataList[i].IsItemsValid)
{
hasParseError = true;
break;
}
}
if (hasParseError)
{
EditorGUILayout.HelpBox("存在无法解析的奖励数据,请先检查 Excel 中 Items 列格式,必须为 [{\"Id\":100001,\"Num\":50}] 这种结构。", MessageType.Error);
}
EditorGUILayout.EndVertical();
}
private void DrawRewardTable()
{
EditorGUILayout.BeginVertical("box");
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField("阶段", EditorStyles.boldLabel, GUILayout.Width(80));
EditorGUILayout.LabelField("需要邀请人数", EditorStyles.boldLabel, GUILayout.Width(180));
EditorGUILayout.LabelField("奖励数量", EditorStyles.boldLabel, GUILayout.Width(180));
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
EditorGUILayout.Space(4);
scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
for (int i = 0; i < rewardDataList.Count; i++)
{
InviteRewardData data = rewardDataList[i];
EditorGUILayout.BeginVertical("box");
EditorGUILayout.BeginHorizontal();
EditorGUILayout.LabelField($"第 {i + 1} 档", GUILayout.Width(80));
data.Need = EditorGUILayout.IntField(data.Need, GUILayout.Width(180));
data.RewardNum = EditorGUILayout.IntField(data.RewardNum, GUILayout.Width(180));
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
EditorGUILayout.BeginHorizontal();
GUILayout.Space(84);
EditorGUILayout.LabelField($"配置Id: {data.Id}", EditorStyles.miniLabel, GUILayout.Width(120));
EditorGUILayout.LabelField($"奖励ItemId: {data.RewardItemId}", EditorStyles.miniLabel, GUILayout.Width(160));
if (!data.IsItemsValid)
{
EditorGUILayout.LabelField("Items 格式异常", EditorStyles.miniBoldLabel, GUILayout.Width(100));
}
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
EditorGUILayout.EndVertical();
}
EditorGUILayout.EndScrollView();
EditorGUILayout.EndVertical();
}
private void LoadInviteRewardExcel()
{
rewardDataList.Clear();
string excelPath = GetDocsConfigFilePath(INVITE_EXCEL_NAME);
if (!File.Exists(excelPath))
{
throw new FileNotFoundException($"未找到 {INVITE_EXCEL_NAME}", excelPath);
}
using (var package = new ExcelPackage(new FileInfo(excelPath)))
{
var worksheet = package.Workbook.Worksheets[REWARD_SHEET_NAME];
if (worksheet == null)
{
throw new Exception($"未找到 Sheet: {REWARD_SHEET_NAME}");
}
int rowCount = worksheet.Dimension?.Rows ?? 0;
for (int row = 3; row <= rowCount; row++)
{
string idText = worksheet.Cells[row, COL_ID].Text.Trim();
if (string.IsNullOrEmpty(idText))
{
continue;
}
if (!int.TryParse(idText, out int id))
{
Debug.LogWarning($"Invite.xlsx/{REWARD_SHEET_NAME} 第 {row} 行 Id 不是有效整数,已跳过");
continue;
}
InviteRewardData data = new InviteRewardData
{
Id = id,
Need = ParseInt(worksheet.Cells[row, COL_NEED].Text.Trim()),
ItemsRawText = worksheet.Cells[row, COL_ITEMS].Text.Trim(),
RowIndex = row
};
data.IsItemsValid = TryParseItems(data.ItemsRawText, out int rewardItemId, out int rewardNum);
data.RewardItemId = rewardItemId;
data.RewardNum = rewardNum;
rewardDataList.Add(data);
}
}
if (rewardDataList.Count == 0)
{
throw new Exception("未读取到任何邀请奖励数据");
}
}
private void SaveToExcel()
{
if (!ValidateRewardData(out string errorMessage))
{
EditorUtility.DisplayDialog("校验失败", errorMessage, "确定");
return;
}
try
{
string excelPath = GetDocsConfigFilePath(INVITE_EXCEL_NAME);
using (var package = new ExcelPackage(new FileInfo(excelPath)))
{
var worksheet = package.Workbook.Worksheets[REWARD_SHEET_NAME];
if (worksheet == null)
{
EditorUtility.DisplayDialog("错误", $"未找到 Sheet: {REWARD_SHEET_NAME}", "确定");
return;
}
foreach (InviteRewardData data in rewardDataList)
{
int row = data.RowIndex;
worksheet.Cells[row, COL_ID].Value = data.Id;
worksheet.Cells[row, COL_NEED].Value = data.Need;
worksheet.Cells[row, COL_ITEMS].Value = BuildItemsJson(data.RewardItemId, data.RewardNum);
}
package.Save();
}
EditorUtility.DisplayDialog("成功", "邀请奖励配置已保存到 Excel", "确定");
Debug.Log("邀请奖励配置已保存");
}
catch (Exception e)
{
EditorUtility.DisplayDialog("错误", $"保存失败: {e.Message}", "确定");
Debug.LogError($"保存邀请奖励配置失败: {e}");
}
}
private bool ValidateRewardData(out string errorMessage)
{
if (rewardDataList.Count != FIXED_STAGE_COUNT)
{
errorMessage = $"当前 Reward Sheet 不是固定的 {FIXED_STAGE_COUNT} 行配置,不能通过该工具保存。\n\n如果要添加阶段数量请联系开发组";
return false;
}
int previousNeed = int.MinValue;
for (int i = 0; i < rewardDataList.Count; i++)
{
InviteRewardData data = rewardDataList[i];
if (!data.IsItemsValid || data.RewardItemId <= 0)
{
errorMessage = $"第 {i + 1} 档奖励数据格式异常,无法保存。";
return false;
}
if (data.Need < 0)
{
errorMessage = $"第 {i + 1} 档的需要邀请人数不能小于 0。";
return false;
}
if (data.RewardNum < 0)
{
errorMessage = $"第 {i + 1} 档的奖励数量必须是整数,且不能小于 0。";
return false;
}
if (data.Need <= previousNeed)
{
errorMessage = "需要邀请人数必须按从小到大的顺序填写。";
return false;
}
previousNeed = data.Need;
}
errorMessage = string.Empty;
return true;
}
private bool TryParseItems(string itemsText, out int itemId, out int rewardNum)
{
itemId = 0;
rewardNum = 0;
if (string.IsNullOrWhiteSpace(itemsText))
{
return false;
}
Match idMatch = Regex.Match(itemsText, @"""Id""\s*:\s*(\d+)");
Match numMatch = Regex.Match(itemsText, @"""Num""\s*:\s*(-?\d+)");
if (!idMatch.Success || !numMatch.Success)
{
return false;
}
return int.TryParse(idMatch.Groups[1].Value, out itemId) &&
int.TryParse(numMatch.Groups[1].Value, out rewardNum);
}
private string BuildItemsJson(int itemId, int rewardNum)
{
return $"[{{\"Id\":{itemId},\"Num\":{rewardNum}}}]";
}
private int ParseInt(string text)
{
return int.TryParse(text, out int value) ? value : 0;
}
}
[Serializable]
public class InviteRewardData
{
public int Id;
public int Need;
public int RewardItemId;
public int RewardNum;
public bool IsItemsValid;
public int RowIndex;
public string ItemsRawText;
}
}