C#代码片段笔记

DLL嵌入exe

生成单一exe文件, 程序打包为一个文件
方法一:【推荐】 nuget引入Costura.Fody即可
方法二:
1,在项目下面建个目录放dll,把dll包含到项目里面,右键dll属性设置为嵌入式资源
2,添加这个静态类LoadResoureDll
3,在Main()里面调用LoadResoureDll.RegistDLL();
方法三:
如果引入的dll需要调用另外的dll,又不是强引入,就要结合方法一和方法二
例如:引入SqlSugar(.Net Framework 版本),再访问mysql的时候需要MySql.Data.dll
这是需要同时使用方法一和方法二:方法一会嵌入SqlSugar,方法二会嵌入MySql.Data.dll

static void Main()
{
	LoadResoureDll.RegistDLL(); //3,DLL嵌入exe
	Application.EnableVisualStyles();
	Application.SetCompatibleTextRenderingDefault(false);
	Application.Run(new Form1());
}

//2,添加这个静态类
public static class LoadResoureDll
{
	/// <summary> 已加载DLL
	/// </summary>
	private static Dictionary<string, Assembly> LoadedDlls = new Dictionary<string, Assembly>();
	/// <summary> 已处理程序集
	/// </summary>
	private static Dictionary<string, object> Assemblies = new Dictionary<string, object>();
	/// <summary> 在对程序集解释失败时触发
	/// </summary>
	/// <param name="sender">AppDomain</param>
	/// <param name="args">事件参数</param>
	private static Assembly AssemblyResolve(object sender, ResolveEventArgs args)
	{
		try
		{
			//程序集
			Assembly ass;
			//获取加载失败的程序集的全名
			var assName = new AssemblyName(args.Name).FullName;
			//判断Dlls集合中是否有已加载的同名程序集
			if (LoadedDlls.TryGetValue(assName, out ass) && ass != null)
			{
				LoadedDlls[assName] = null;//如果有则置空并返回
				return ass;
			}
			else
			{
				return ass;//dev的dll 这里有问题,可以绕过
				throw new DllNotFoundException(assName);//否则抛出加载失败的异常
			}
		}
		catch (System.Exception ex)
		{
			MessageBox.Show("error1:\n位置:AssemblyResolve()!\n描述:" + ex.Message);
			return null;
		}
	}

	/// <summary> 注册资源中的dll
	/// </summary>
	/// <param name="pattern">*表示连续的未知字符,_表示单个未知字符,如*.dll</param>
	public static void RegistDLL(string pattern = "*.dll")
	{
		System.IO.Directory.GetFiles("", "");
		//获取调用者的程序集
		var ass = new StackTrace(0).GetFrame(1).GetMethod().Module.Assembly;
		//判断程序集是否已经处理
		if (Assemblies.ContainsKey(ass.FullName))
		{
			return;
		}
		//程序集加入已处理集合
		Assemblies.Add(ass.FullName, null);
		//绑定程序集加载失败事件(这里我测试了,就算重复绑也是没关系的)
		AppDomain.CurrentDomain.AssemblyResolve += AssemblyResolve;
		//获取所有资源文件文件名
		var res = ass.GetManifestResourceNames();
		var regex = new Regex("^" + pattern.Replace(".", "\\.").Replace("*", ".*").Replace("_", ".") + "$", RegexOptions.IgnoreCase);
		foreach (var r in res)
		{
			//如果是dll,则加载
			if (regex.IsMatch(r))
			{
				try
				{
					var s = ass.GetManifestResourceStream(r);
					var bts = new byte[s.Length];
					s.Read(bts, 0, (int)s.Length);
					var da = Assembly.Load(bts);
					//判断是否已经加载
					if (LoadedDlls.ContainsKey(da.FullName))
					{
						continue;
					}
					LoadedDlls[da.FullName] = da;
				}
				catch (Exception ex)
				{
					MessageBox.Show("error2:加载dll失败\n位置:RegistDLL()!\n描述:" + ex.Message);
				}
			}
		}
	}
}

IsNull

//空字符串或者空对象
public static bool IsNull(string str)
{
	if (null == str || "" == str)
	{
		return true;
	}
	return false;
}

//表格对象为空,或者没有行
public static bool IsNull(DataTable dt)
{
	if (null == dt || dt.Rows.Count <= 0)
	{
		return true;
	}
	return false;
}

//数据集对象为空,或者没有没有,或者表中没有行
public static bool IsNull(DataSet ds)
{
	if (null == ds || null == ds.Tables || ds.Tables.Count <= 0 || IsNull(ds.Tables[0]))
	{
		return true;
	}
	return false;
}

ABP返回分页后的结果

//ABP返回分页后的结果
public static PagedResultDto<T> GetPagedResultDto<T>(IQueryable<T> query, int SkipCount, int MaxResultCount)
{
	var totals = query.Count();
	IReadOnlyList<T> list = query.Skip(SkipCount).Take(MaxResultCount).ToList();
	var result = new PagedResultDto<T>(totals, list);
	return result;
}

//ABP返回分页后的结果
public static PagedResultDto<T> GetPagedResultDto<T>(List<T> query, int SkipCount, int MaxResultCount)
{
	var totals = query.Count();
	IReadOnlyList<T> list = query.Skip(SkipCount).Take(MaxResultCount).ToList();
	var result = new PagedResultDto<T>(totals, list);
	return result;
}

把多个对象的属性赋值给一个对象,自动映射AutoMap

//把入参的属性赋值给出参, 1,所有属性必须设置{ get; set; }  2,入参有先后顺序, 如果两个入参有相同的属性, 后面的会覆盖前面的
public static T MapMulti2One<T>(params Object[] arrObj) where T : new()
{
	Type typeT = typeof(T);
	T objT = new T();

	foreach (var item in arrObj)
	{
		if (item == null)
		{
			continue;
		}
		var propertiesA = item.GetType().GetProperties();

		// 通过循环遍历所有属性,并将A对象的属性值赋值给B对象中对应的属性
		foreach (var propertyA in propertiesA)
		{
			PropertyInfo propertyT = typeT.GetProperty(propertyA.Name);
			if (propertyT != null && propertyT.PropertyType == propertyA.PropertyType)
			{
				//如果propertyT存在就赋值
				propertyT.SetValue(objT, propertyA.GetValue(item));
			}
		}
	}

	return objT;
}

使用举例1:

var query = from a in _reportInstanceRepository.GetAll()
		join b in _reportGroupRepository.GetAll() on a.ReportGroupId equals b.Id
		join c in _clientReportRepository.GetAll() on a.Id equals c.ReportId
		join d in _clientRepository.GetAll() on c.ClientId equals d.Id
		where d.Id == 4
		select MapMulti2One<ReportInstanceDto>(a, new{TemplateLength = a.Template.Length});

使用举例2:左连接

var list2 = from a in list
            join b in listBind on a.Idcard equals b.Idcard into lb
            from bind in lb.DefaultIfEmpty() //bind是左连接后的结果
            join c in listPayRecord on a.DonBloodNo equals c.DonBloodNo into pr
            from payRecode in pr.DefaultIfEmpty() //左连接
            select MapMulti2One<PayInfoDto>(a, new { bind?.BindTime, bind?.WechatUserId }, new { PayTime = payRecode?.CreationTime, payRecode?.OrderNo });

Asp.net core 读取appsetting.config

// 例如appsetting中包含属性xxxConfig:{xxx}
{
...
  "xxxConfig": {
    "BaseUrl": "http://192.168.1.1:8088/api/",
    "IsDebug": true
  },
...  
}
//1, 编写xxxConfig类与配置一致
public class xxxConfig
{
    public string BaseUrl { get; set; }
    public bool IsDebug { get; set; }
}
//2, 在Setup.cs中添加
services.Configure<xxxConfig>(_appConfiguration.GetSection("xxxConfig"));
//3, 在controller的构造函数中注入: IOptions<xxxConfig> option, 在构造函数中赋值
 _xxxConfig= option.Value;

Winform读写配置文件

简单好用,没有依赖

//配置文件路径
private string configFileName = Application.StartupPath + "\\AppConfig.xml";
//读取
DataTable dataTable2 = new DataTable();
if (File.Exists(configFileName))
{
    dataTable2.ReadXml(configFileName);
    if (dataTable2 != null && dataTable2.Rows.Count == 1)
    {
        txtServer.Text = dataTable2.Rows[0]["Server"].ToString().Trim();
        cmbLoginType.SelectedValue = dataTable2.Rows[0]["LoginType"].ToString().Trim();
        txtUser.Text = dataTable2.Rows[0]["User"].ToString().Trim();
        txtPwd.Text = dataTable2.Rows[0]["Pwd"].ToString().Trim();
        dbName = dataTable2.Rows[0]["DBName"].ToString().Trim();
    }
}
 //写入
DataTable dataTable = new DataTable();
dataTable.TableName = "ExecConfig";
dataTable.Columns.Add("Server", typeof(string));
dataTable.Columns.Add("LoginType", typeof(string));
dataTable.Columns.Add("User", typeof(string));
dataTable.Columns.Add("Pwd", typeof(string));
dataTable.Columns.Add("DBName", typeof(string));
DataRow dataRow = dataTable.NewRow();
dataRow["Server"] = txtServer.Text.Trim();
dataRow["LoginType"] = cmbLoginType.SelectedValue;
dataRow["User"] = txtUser.Text.Trim();
dataRow["Pwd"] = txtPwd.Text.Trim();
dataRow["DBName"] = cmbDB.SelectedValue;
dataTable.Rows.Add(dataRow);
if (File.Exists(configFileName))
{
    File.Delete(configFileName);
}
dataTable.WriteXml(configFileName, XmlWriteMode.WriteSchema);
MessageBox.Show("保存成功!", "提示", MessageBoxButtons.OK, MessageBoxIcon.Asterisk);

打印日志到文件

/// <summary>
/// 打印文件日志
/// </summary>
/// <param name="info">信息</param>
/// <param name="path">日志文件的路径</param>
public void WriteToLog(string info, string path = "")
{
    var logFilePath = "";
    try
    {
        //string path = "";  //  winform默认路径:  Application.StartupPath + "/Log";
        if (!Directory.Exists(path))
            Directory.CreateDirectory(path);
        logFilePath = path + "/log" + DateTime.Now.ToString("yyyMMddHH") + ".txt";
    }
    catch
    {
        //忽略日志写入错误
        return;
    }

    try
    {
        StringBuilder str = new StringBuilder();
        str.Append("Time: " + DateTime.Now.ToString("yyyyMMdd HH:mm:ss") + "\r\n");
        if (!string.IsNullOrEmpty(info)) { str.Append("info: " + info + "\r\n"); }
        str.Append("-----------------------------------------------------------\r\n");
        StreamWriter sw;
        if (!System.IO.File.Exists(logFilePath))
        {
            sw = System.IO.File.CreateText(logFilePath);
        }
        else
        {
            sw = System.IO.File.AppendText(logFilePath);
        }

        sw.Write(str.ToString());
        sw.Close();
    }
    catch
    {
        //忽略日志写入错误
    }
}

理解C#的委托Action和Func

static void Main(string[] args)
{
	ExecBatCommand(m =>
	{
		var i = m(1);
		i++;
		i = m(i);
		Console.WriteLine(i);   //请问输出什么结果??
	});
}

static void ExecBatCommand(Action<Func<int, int>> outMethod)
{
	outMethod(i => i += 2);
}

导出到Excel通用方法

 //导出到Excel通用方法
public ActionResult ExportToExcel(ExportInput input)
{
	//得到一个DataTable,例如从存储过程获取
	var dataTable = _fileDapperRepository.GetDataTableFromPro(input);
	if (dataTable.Rows.Count == 0) throw new UserFriendlyException("没有查询到数据");

	// If you are a commercial business and have
	// purchased commercial licenses use the static property
	// LicenseContext of the ExcelPackage class :
	OfficeOpenXml.ExcelPackage.LicenseContext = OfficeOpenXml.LicenseContext.Commercial;

	// If you use EPPlus in a noncommercial context
	// according to the Polyform Noncommercial license:
	OfficeOpenXml.ExcelPackage.LicenseContext = OfficeOpenXml.LicenseContext.NonCommercial;
	using (OfficeOpenXml.ExcelPackage pck = new OfficeOpenXml.ExcelPackage())
	{
		OfficeOpenXml.ExcelWorksheet ws = pck.Workbook.Worksheets.Add("页签1");
		ws.Cells["A1"].LoadFromDataTable(dataTable, true);
		//样式修改
		ws.Cells[2, 2, dataTable.Rows.Count + 1, 2].Style.Numberformat.Format = "yyyy/MM/dd";
		int[] TimeCols = { 7, 9, 11, 14, 33 };
		for (var item = 1; item < dataTable.Columns.Count; item++)
		{
			if(TimeCols.Contains(item))
				ws.Cells[2, item, dataTable.Rows.Count + 1, item].Style.Numberformat.Format = "yyyy/MM/dd HH:mm:ss";
			ws.Cells[2, item, dataTable.Rows.Count + 1, item].AutoFitColumns();//自动适应宽度
		}
		
		return File(pck.GetAsByteArray(), "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "导出数据.xlsx");
	}
}

C#音效播放PlaySound

[DllImport("winmm.dll", EntryPoint = "PlaySound")]
private static extern bool PlaySound(string pszSound, IntPtr hmod, uint fdwSound);
public const int SND_FILENAME = 0x00020000;
public const int SND_ASYNC = 0x0001;
 
//用API播放声音文件, 异步,覆盖播放(1,不会堵塞UI  2,播放第二个音效的时候,第一个音效自动停止)
void ReadSound(string WavFilePath)
{
	PlaySound(WavFilePath, IntPtr.Zero, SND_FILENAME | SND_ASYNC);      
}

微软TTS文字转语音并保存到wav文件中

public static void Speek()
{
    var synthesizer = new SpeechSynthesizer();
    synthesizer.SetOutputToDefaultAudioDevice();
    MemoryStream ms = new MemoryStream();
    synthesizer.SetOutputToWaveStream(ms);
    synthesizer.Speak("微软TTS文字转语音并保存到wav文件中");
    FileStream fs = new FileStream("C:\\a.wav", FileMode.OpenOrCreate);
    BinaryWriter w = new BinaryWriter(fs);
    w.Write(ms.ToArray());
    fs.Close();
    ms.Close();
}

设计模式之享元模式

class Program
{
	public class OtherClass
	{
		public string c = "xiangyuan";
	}

	static void Main(string[] args)
	{
		string a = "xiangyuan";
		string b = "xiangyuan";

		Console.WriteLine(object.ReferenceEquals(a, b));
		Console.WriteLine(object.ReferenceEquals(a, new OtherClass().c));

		//请问输出结果什么?  TT/TF/FT/FF   (T=Ture, F=False)
	}
}

请在评论区写出你的答案: TT/TF/FT/FF (T=Ture, F=False)

设计模式之隆过滤器

建布隆过滤器:
1, 建一个很长的二进制数组
2, 把数据经过用多个哈希算法得到的二进制值写到数组里面
用布隆过滤器
把需要检查的数据,经过相同的哈希算法得到的二进制, 在数组中匹配,全部1都匹配表示存在, 任意位不是1表示不存在

应用要点
1, 只需要判断是否存在
2, 集合的数量非常大

应用场景
垃圾邮箱黑名单

批量替换一段文本中的特殊标记字段{xxx}

//批量替换一段文本中的特殊标记字段{xxx}
public class StringReplaceDynamic
{
	public static void Demo()
	{
		XElement root = XElement.Load(@"D:\space\test\ConsoleAppNetFramework\xml\BS004.xml");
		Dictionary<string, string> a = new Dictionary<string, string>();
		a.Add("patientId", "123");
		a.Add("registerID", "321");
		var b = StringReplace(root.ToString(), a);
		Console.WriteLine(b);
	}

	public static void Demo2()
	{
		XElement root = XElement.Load(@"D:\space\test\ConsoleAppNetFramework\xml\BS004.xml");
		DataTable dt = new DataTable();
		dt.Columns.Add("patientId");
		dt.Columns.Add("registerID");
		DataRow dr = dt.NewRow();
		dr["patientId"] = "123";
		dr["registerID"] = "321";
		dt.Rows.Add(dr);
		var b = StringReplace(root.ToString(), dt);
		Console.WriteLine(b);
	}

	//用Dictionary替换{xxx}
	public static string StringReplace(string msg, Dictionary<string, string> dic)
	{
		foreach (var obj in dic)
		{
			string r = "{" + obj.Key + "}";
			msg = msg.Replace(r, obj.Value);
		}

		return msg;
	}

	//用第一行的数据替换{xxx}
	public static string StringReplace(string msg, DataTable dic)
	{

		foreach (var obj in dic.Columns)
		{
			string r = "{" + obj.ToString() + "}";
			msg = msg.Replace(r, dic.Rows[0][obj.ToString()].ToString());
		}

		return msg;
	}
}

开机自启动

string path = Application.ExecutablePath;
RegistryKey rk = Registry.CurrentUser;
RegistryKey rk2 = rk.CreateSubKey(@"Software\Microsoft\Windows\CurrentVersion\Run");
rk2.SetValue("BtmsPhoneNotify", path);
rk2.Close();
rk.Close();
// "已设置开机自启动"

SqliteHelper

public static class SqliteHelper
{
	//从配置文本中读取连接字符串
	private static string connStr = "data source=data.sqlite";

	//执行命令的方法:insert,update,delete
	//params:可变参数,目的是省略了手动构造数组的过程,直接指定对象,编译器会帮助我们构造数组,并将对象加入数组中,传递过来
	public static int ExecuteNonQuery(string sql, params SQLiteParameter[] ps)
	{
		//创建连接对象
		using (SQLiteConnection conn = new SQLiteConnection(connStr))
		{
			//创建命令对象
			SQLiteCommand cmd = new SQLiteCommand(sql, conn);
			//添加参数
			cmd.Parameters.AddRange(ps);
			//打开连接
			conn.Open();
			//执行命令,并返回受影响的行数
			return cmd.ExecuteNonQuery();
		}
	}

	//获取首行首列值的方法
	public static object ExecuteScalar(string sql, params SQLiteParameter[] ps)
	{
		using (SQLiteConnection conn = new SQLiteConnection(connStr))
		{
			SQLiteCommand cmd = new SQLiteCommand(sql, conn);
			cmd.Parameters.AddRange(ps);

			conn.Open();
			//执行命令,获取查询结果中的首行首列的值,返回
			return cmd.ExecuteScalar();
		}
	}

	//获取结果集
	public static DataTable GetDataTable(string sql, params SQLiteParameter[] ps)
	{
		using (SQLiteConnection conn = new SQLiteConnection(connStr))
		{
			//构造适配器对象
			SQLiteDataAdapter adapter = new SQLiteDataAdapter(sql, conn);
			//构造数据表,用于接收查询结果
			DataTable dt = new DataTable();
			//添加参数
			adapter.SelectCommand.Parameters.AddRange(ps);
			//执行结果
			adapter.Fill(dt);
			//返回结果集
			return dt;
		}
	}
}

sqlite数据初始化

public class SqliteDbInit
{
	//创建所有表,可以反复执行,程序启动后都执行一次
	public static void Init()
	{
		//打印类型对应的打印机
		var tablePrinterName = "create table  if not exists PrinterName (PrintType varchar(64), PrinterName varchar(512))";
		SqliteHelper.ExecuteNonQuery(tablePrinterName);

		 //是否预览
		var tableIsPreview = "create table  if not exists IsPreview (PrintType varchar(64), IsPreview int)";
		SqliteHelper.ExecuteNonQuery(tableIsPreview);
	}
}

xml转DataTable

DataSet xmlDS = new DataSet();
var stream = new StringReader(xml);
xmlDS.ReadXml(stream);
DataTable table = xmlDS.Tables[0];

xml节点转成json数组再转成DataTable

public static DataTable GetList(string data)
{
	XmlDocument doc = new XmlDocument();
	doc.LoadXml(data);//创建xml对象
	string jsonText = JsonConvert.SerializeXmlNode(doc); //xml对象转json字符串
	JObject json = JObject.Parse(jsonText); //json字符串转jion对象
	var json2 = json["result"]["item"].ToString(); //从json对象中得到json数组字符串,类似: [{},{},{}]
	return JsonConvert.DeserializeObject<DataTable>(json2); //json数据转datatable
}

Winform重启

Process.Start(Application.ExecutablePath);
Application.Exit();

Winform的弹出消息

    // 消息对话框, 2秒后自动消失
    class MkMessage : Form
    {
        private Label label1;
        private Timer timer;

        public MkMessage()
        {
            InitializeComponent();
            // 设置对话框的属性,居中,边框
            this.StartPosition = FormStartPosition.CenterScreen;
            this.FormBorderStyle = FormBorderStyle.FixedSingle;

            // 添加一个Label作为对话框的内容
            label1.Text = "这是一个非模态对话框,将在2秒后消失。";

            // 初始化Timer控件
            timer = new Timer();
            timer.Interval = 2000; // 2000毫秒后触发
            timer.Tick += Timer_Tick;
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            // 当Timer触发时关闭对话框
            this.Close();
        }

        private void Show(string title, string msg)
        {
            label1.Text = msg;
            // 显示对话框并启动计时器
            this.Show();
            timer.Start();
        }

        public static void Show(string msg)
        {
            new MkMessage().Show("", msg);
        }

        private void InitializeComponent()
        {
            this.label1 = new System.Windows.Forms.Label();
            this.SuspendLayout();
            // 
            // label1
            // 
            this.label1.Dock = System.Windows.Forms.DockStyle.Fill;
            this.label1.Font = new System.Drawing.Font("微软雅黑", 15F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(134)));
            this.label1.Location = new System.Drawing.Point(0, 0);
            this.label1.Name = "label1";
            this.label1.Size = new System.Drawing.Size(345, 116);
            this.label1.TabIndex = 0;
            this.label1.Text = "label1";
            this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
            // 
            // MkMessage
            // 
            this.ClientSize = new System.Drawing.Size(345, 116);
            this.Controls.Add(this.label1);
            this.MaximizeBox = false;
            this.MinimizeBox = false;
            this.Name = "MkMessage";
            this.ShowIcon = false;
            this.ShowInTaskbar = false;
            this.ResumeLayout(false);

        }
    }

一次只能启动一个EXE

static class Program
{
    /// <summary>
    /// 应用程序的主入口点。
    /// </summary>
    [STAThread]
    static void Main(string[] args)
    {
        Mutex instance = new Mutex(true, "ExeName", out bool createdNew);//一次只能启动一个程序
        if (args.Length > 0)
        {
            // 有参数说明是程序自动重启, 不用判断
            Application.Run(new Form5());
            instance.ReleaseMutex();
        }
        else
        {
            if (createdNew)
            {
                // 加载窗口
                Application.Run(new Form5());
                instance.ReleaseMutex();
            }
            else
            {
                var form = new AutoCloseForm("程序已运行", 2000);
                form.ShowDialog();
                Application.Exit();
                Mutex.OpenExisting("ExeName");
            }
        }
    }
}

// 自动重启EXE
Application.Exit();
Process.Start(Application.ExecutablePath, "restart");

显示几秒后自动消失的对话框

public class AutoCloseForm : Form
{
    private Timer _closeTimer;

    public AutoCloseForm(string message, int timeoutMilliseconds)
    {
        // 设置窗体的属性
        this.Size = new System.Drawing.Size(300, 100);
        this.FormBorderStyle = FormBorderStyle.FixedSingle;
        this.StartPosition = FormStartPosition.CenterScreen;
        this.ControlBox = false; // 不显示控制框

        // 添加标签来显示消息
        Label messageLabel = new Label();
        messageLabel.Text = message;
        messageLabel.Dock = DockStyle.Fill;
        messageLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
        this.Controls.Add(messageLabel);

        // 设置计时器
        _closeTimer = new Timer();
        _closeTimer.Interval = timeoutMilliseconds;
        _closeTimer.Tick += (sender, e) =>
        {
            _closeTimer.Stop();
            this.Close();
        };
        _closeTimer.Start();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值