使用Sqlsugar实现项目

本文详细介绍了如何在C#项目中利用Sqlsugar库进行数据库操作,涵盖了连接配置、实体映射、CRUD操作以及 LINQ 查询的使用方法,旨在提升开发效率并简化数据访问层的代码。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using xktComm.Common;
using xktComm.DataConvert;
using xktNodeSettings;
using xktNodeSettings.Node.Modbus;
using xktNodeSettings.Node.Variable;

namespace CMStestMdbus
{
    public partial class FrmMain : Form
    {
        public FrmMain()
        {
            InitializeComponent();
            modbusTCPList = ModbusTCPCFG.LoadXmlFile(settingPath);
            SqlSugarService.SetConnectionStr("Data Source=" + Application.StartupPath + str2);
            InitialInfo();
            Communication();

        }

        string str2 = "\\config\\ZgTestData.db;Pooling=true;FailIfMissing=false";
        //配置文件路径
        private string settingPath = Application.StartupPath + "\\settings\\settingsss.xml";

        InsertDataSQLite objInsert = new InsertDataSQLite(10000);//10秒1次

        private string ipAddress = "127.0.0.1";
        private int port = 502;

        //private CancellationTokenSource cts = new CancellationTokenSource();



        /// <summary>
        /// 通讯函数
        /// </summary>
        private void Communication()
        {
            if (modbusTCPList.Count>0)//有modbusTCP节点
            {
                foreach (var dev in modbusTCPList) //遍历modbusTCP节点
                {
                    if (dev.IsActive)
                    {
                        dev.cts = new CancellationTokenSource();
                        Task.Run(async () =>
                        {
                            while (!dev.cts.IsCancellationRequested)//默认是false
                            {
                                if (dev.IsConnected) //放解析
                                {
                                    CommonMethods.TCPisConnected = dev.IsConnected;
                                    foreach (var gp in dev.ModbusTCPGroupList)//遍历每一个组
                                    {
                                        if (gp.IsActive)
                                        {
                                            //读取数据
                                            byte[] res = null;
                                            //线圈存储区
                                            if (gp.StoreArea == ModbusStoreArea.输入线圈 || gp.StoreArea == ModbusStoreArea.输出线圈)
                                            {
                                                //读取
                                                switch (gp.StoreArea)
                                                {
                                                    case ModbusStoreArea.输出线圈:
                                                        res = dev.modtcp.ReadOutputStatus(gp.Start, gp.Length);
                                                        break;
                                                    case ModbusStoreArea.输入线圈:
                                                        res = dev.modtcp.ReadInputStatus(gp.Start, gp.Length);
                                                        break;
                                                    default:
                                                        break;
                                                }

                                                //解析

                                                //判断
                                                if (res != null && res.Length == ((gp.Length % 8 == 0) ? gp.Length / 8 : gp.Length / 8 + 1))
                                                {
                                                    dev.ErrorTimes = 0;
                                                    foreach (var variable in gp.varList)
                                                    {
                                                        //验证地址并解析地址
                                                        if (VerifyModbusAddress(true, variable.VarAddress, out int start, out int offset))
                                                        {
                                                            //真正的地址偏移
                                                            start -= gp.Start;

                                                            switch (variable.VarType)
                                                            {
                                                                case DataType.Bool:
                                                                    variable.Value = BitLib.GetBitArrayFromByteArray(res, false)[start];
                                                                    break;
                                                                default:
                                                                    break;
                                                            }

                                                            //包含就替换,否则就添加
                                                            if (CommonMethods.CurrentPLCValue.ContainsKey(variable.Name))
                                                            {
                                                                CommonMethods.CurrentPLCValue[variable.Name] = variable.Value;
                                                            }
                                                            else
                                                            {
                                                                CommonMethods.CurrentPLCValue.Add(variable.Name, variable.Value);
                                                            }

                                                        }
                                                    }

                                                }
                                                else
                                                {
                                                    dev.ErrorTimes++;

                                                    if (dev.ErrorTimes >= dev.MaxErrorTimes)
                                                    {
                                                        dev.IsConnected = false;
                                                        CommonMethods.TCPisConnected = dev.IsConnected;
                                                    }

                                                }
                                            }

                                            //寄存器存储区
                                            else
                                            {
                                                //读取
                                                switch (gp.StoreArea)
                                                {
                                                    case ModbusStoreArea.保持寄存器:
                                                        res = dev.modtcp.ReadKeepReg(gp.Start, gp.Length);
                                                        break;
                                                    case ModbusStoreArea.输入寄存器:
                                                        res = dev.modtcp.ReadInputReg(gp.Start, gp.Length);
                                                        break;
                                                    default:
                                                        break;
                                                }


                                                if (res != null && res.Length == gp.Length * 2)
                                                {
                                                    dev.ErrorTimes = 0;
                                                    foreach (var variable in gp.varList)
                                                    {
                                                        if (VerifyModbusAddress(false, variable.VarAddress, out int start, out int offset))
                                                        {
                                                            start -= gp.Start;

                                                            start *= 2;

                                                            //2开始读取50个寄存器

                                                            //第一个变量地址是2,索引是0   (2-2)*2
                                                            //第二个变量地址是4,索引是4   (4-2)*2
                                                            //第三个变量地址是6,索引是8   (6-2)*2


                                                            switch (variable.VarType)
                                                            {
                                                                case DataType.Bool:
                                                                    variable.Value = BitLib.GetBitFrom2ByteArray(res, start, offset, true);
                                                                    break;
                                                                case DataType.Byte:
                                                                    variable.Value = ByteLib.GetByteFromByteArray(res, start);
                                                                    break;
                                                                case DataType.Short:
                                                                    variable.Value = ShortLib.GetShortFromByteArray(res, start, dev.DataFormat);
                                                                    break;
                                                                case DataType.UShort:
                                                                    variable.Value = UShortLib.GetUShortFromByteArray(res, start, dev.DataFormat);
                                                                    break;
                                                                case DataType.Int:
                                                                    variable.Value = IntLib.GetIntFromByteArray(res, start, dev.DataFormat);
                                                                    break;
                                                                case DataType.UInt:
                                                                    variable.Value = UIntLib.GetUIntFromByteArray(res, start, dev.DataFormat);
                                                                    break;
                                                                case DataType.Float:
                                                                    variable.Value = FloatLib.GetFloatFromByteArray(res, start, dev.DataFormat);
                                                                    break;
                                                                case DataType.Double:
                                                                    variable.Value = DoubleLib.GetDoubleFromByteArray(res, start, dev.DataFormat);
                                                                    break;
                                                                case DataType.Long:
                                                                    variable.Value = LongLib.GetLongFromByteArray(res, start, dev.DataFormat);
                                                                    break;
                                                                case DataType.ULong:
                                                                    variable.Value = ULongLib.GetULongFromByteArray(res, start, dev.DataFormat);
                                                                    break;
                                                                case DataType.String:
                                                                    variable.Value = StringLib.GetStringFromByteArray(res, start, offset, Encoding.ASCII);
                                                                    break;
                                                                case DataType.ByteArray:
                                                                    variable.Value = StringLib.GetHexStringFromByteArray(res, start, offset);
                                                                    break;
                                                                case DataType.HexString:
                                                                    variable.Value = StringLib.GetHexStringFromByteArray(res, start, offset);
                                                                    break;
                                                                default:
                                                                    break;
                                                            }

                                                            //包含就替换,否则就添加
                                                            if (CommonMethods.CurrentPLCValue.ContainsKey(variable.Name))
                                                            {
                                                                CommonMethods.CurrentPLCValue[variable.Name] = variable.Value;
                                                            }
                                                            else
                                                            {
                                                                CommonMethods.CurrentPLCValue.Add(variable.Name, variable.Value);
                                                            }
                                                        }
                                                    }
                                                }

                                                else
                                                {
                                                    dev.ErrorTimes++;

                                                    if (dev.ErrorTimes >= dev.MaxErrorTimes)
                                                    {
                                                        dev.IsConnected = false;
                                                        CommonMethods.TCPisConnected = dev.IsConnected;
                                                    }
                                                }
                                            }


                                        }
                                    }
                                }
                                else
                                {
                                    //延时
                                    if (!dev.FirstConnect)//如果不是第一次连接
                                    {
                                        await Task.Delay(dev.ReConnectTime);
                                        dev.modtcp?.DisConnect();
                                    }
                                    //第一次 连接
                                    //初始化通信对象
                                    dev.modtcp = new xktComm.Modbus.ModbusTcp();
                                    dev.modtcp.ConnectTimeOut = dev.ConnectTimeOut;//设置连接超时时间
                                    //设置间隔时间
                                    await Task.Delay(200);
                                    //设置数据格式
                                    dev.modtcp.DataFormat = dev.DataFormat;
                                    //建立连接
                                    dev.IsConnected = dev.modtcp.Connect(dev.ServerURL, dev.Port);
                                    dev.FirstConnect = false;
                                    await Task.Delay(200);
                                }
                            }
                        }, dev.cts.Token);
                    }
                }
            }
        }

        /// <summary>
        /// 验证IP地址函数
        /// </summary>
        /// <param name="isBit"></param>
        /// <param name="address"></param>
        /// <param name="start"></param>
        /// <param name="offset"></param>
        /// <returns></returns>
        private bool VerifyModbusAddress(bool isBit, string address, out int start, out int offset)
        {
            if (isBit)
            {
                offset = 0;
                return int.TryParse(address, out start);
            }
            else
            {
                if (address.Contains('.'))
                {
                    string[] result = address.Split('.');
                    if (result.Length == 2)
                    {
                        bool val = true;
                        int res = 0;
                        val = val && int.TryParse(result[0], out res);
                        start = res;
                        val = val && int.TryParse(result[1], out res);
                        offset = res;
                        return val;
                    }
                    else
                    {
                        start = 0;
                        offset = 0;
                        return false;
                    }
                }
                else
                {
                    offset = 0;
                    return int.TryParse(address, out start);
                }
            }
        }

        //设备集合
        private List<NodeModbusTCP> modbusTCPList = new List<NodeModbusTCP>();



        /// <summary>
        /// 初始化相关信息
        /// </summary>
        private void InitialInfo()
        {

            foreach (var dev in modbusTCPList)
            {
                foreach (var gp in dev.ModbusTCPGroupList)
                {
                    foreach (var variable in gp.varList)
                    {
                        if (variable.Config.ArchiveEnable)//API改变
                        {
                            CommonMethods.storeVariList.Add(variable);
                        }
                    }
                }
            }

            //自己修改IP地址
            //modbusTCPList[0].ServerURL = ipAddress;

            //....


        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using xktNodeSettings.Node.Modbus;
using xktNodeSettings.Node.Variable;

namespace CMStestMdbus
{
    public class CommonMethods
    {
        public static Dictionary<string, object> CurrentPLCValue = new Dictionary<string, object>();
        //TCP标志位
        public static bool TCPisConnected = false;

        public static int CacheCount = 600;//10分钟, 每秒一条

        //归档变量集合
        public static List<ModbusTCPVariable> storeVariList = new List<ModbusTCPVariable>();

        public static List<List<Cache>> CacheList = new List<List<Cache>>();
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;


namespace CMStestMdbus
{
    public class InsertDataSQLite
    {
        System.Timers.Timer t;

        int count = 0;

        public InsertDataSQLite(int Interval)
        {
            t = new System.Timers.Timer(Interval);
            t.Elapsed += T_Elapsed;
            t.AutoReset = true;
            t.Enabled = true;
            t.Start();
            count = CommonMethods.CacheCount; //默认为600
        }

        private void T_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (CommonMethods.TCPisConnected)
            {
                InsertActualData();
                //if (DateTime.Now.Minute == 0 && DateTime.Now.Second == 0)//整点插入
                //{
                //    InsertDataHourReport();
                //}
            }
        }
        #region 插入实时数据

        
        private void InsertActualData()
        {
            if (CommonMethods.CurrentPLCValue != null && CommonMethods.CurrentPLCValue.Count > 0)
            {
                List<ActualData> actualDataArray = new List<ActualData>();
                
                List<Cache> cache = new List<Cache>();
                ////FileVarModbusList归档变量集合 在commonMethos里创建的静态变量, 归档, 意味着存入数据库
                //归档信息在json里面存储

                foreach (var item in CommonMethods.storeVariList)//FileVarModbusList归档集合
                {
                    string varName = item.Name;//modbus 点表中的名称
                    string remark = item.Description; //注释
                    double value = 0.0;
                    if (!CommonMethods.CurrentPLCValue.ContainsKey(varName))
                    {
                        value = 0.0;

                    }
                    else
                    {
                        value = Convert.ToDouble(CommonMethods.CurrentPLCValue[varName]);
                    }
                    DateTime dt = DateTime.Now;
                    actualDataArray.Add(new ActualData()
                    {
                        InsertTime = dt,
                        VarName = varName,
                        Value = Convert.ToString(value),
                        Remark = remark
                    });

                    cache.Add(new Cache()
                    {
                        InsertTime = dt,
                        VarName = varName,
                        Value = value.ToString(),
                        Remark = remark
                    });
                }

                var count = new ActualDataProvider().Insert(actualDataArray); //向数据库插入数据

                if (CommonMethods.CacheList.Count <= count)//如果Count小于600
                {
                    CommonMethods.CacheList.Add(cache);//集合的集合
                }
                else
                {
                    CommonMethods.CacheList.RemoveAt(0);//把第一个去掉
                    CommonMethods.CacheList.Add(cache);//继续添加
                }


            }
        }
        #endregion

        #region 插入小时数据
        //private void InsertDataHourReport()
        //{
        //    List<string> array = new List<string>();
        //    foreach (var item in CommonMethods.storeVariList)
        //    {//ModbusTCPVariable
        //        double value = 0.0;
        //        if (CommonMethods.CurrentPLCValue.ContainsKey(item.Name))
        //        {
        //            string Res = CommonMethods.CurrentPLCValue[item.Name].ToString();//键值对,a[key]为值,存了19个变量的值
        //            if (Res.Length == 0)
        //            {
        //                value = 0.0;

        //            }
        //            else
        //            {
        //                value = Convert.ToDouble(Res);
        //            }
        //            array.Add(value.ToString("f1"));
        //        }

        //        ReportData reportData = new ReportData()
        //        {
        //            J0GCB11CF101 = array[0],
        //            J0GCB12CF101 = array[1],
        //            J0GCB13CF101 = array[2],
        //            J0GCB20CT101 = array[3],
        //            J0GCB20CQ101_NTU = array[4],
        //            J0GCB10CP102 = array[5],
        //            J0GCB21CF101 = array[6],
        //            J0GCB21CF102 = array[7],
        //            J0GCB21CP101 = array[8],
        //            J0GCB21CP102 = array[9],
        //            J0GCB21CQ101_NTU = array[10],
        //            J0GCB22CF101 = array[11],
        //            J0GCB22CF102 = array[12],
        //            J0GCB22CP101 = array[13],
        //            J0GCB22CP102 = array[14],
        //            J0GCB22CQ101_NTU = array[15],
        //            J0GCB31CL101 = array[16],
        //            J0GCB32CL101 = array[17],
        //            J0GCB50CP101 = array[18]
        //        };

        //        var conut = new ReportDataProvider().Insert(reportData);
        //    }
        //}
        #endregion
    }
}

<?xml version="1.0" encoding="utf-8"?>
<Settings>
  <NodeClass Name="PLC设备" Description="各种PLC控制设备">
    <DeviceNode Name="西门子PLC" Description="空压系统1#PLC" DeviceType="30" ConnectTimeOut="2000" ReConnectTime="5000" CreateTime="2022/1/26 5:03:03" InstallationDate="2022/1/26 5:03:03" IsActive="True" MaxErrorTimes="1" KeyWay="VarName" UseAlarmCheck="False" IpAddress="192.168.10.134" Port="502" PlcType="S71500" Rack="0" Slot="0" IsUseMultiRead="false" />
  </NodeClass>
  <NodeClass Name="OPC客户端" Description="OPC相关客户端" />
  <NodeClass Name="Modbus客户端" Description="Modbus相关客户端">
    <ModbusNode Name="Modbus TCP Client" Description="1#软水系统" ModbusType="2000" ConnectTimeOut="2000" CreateTime="2022/1/26 7:01:52" ReConnectTime="5000" InstallationDate="2022/1/26 7:01:52" IsActive="True" MaxErrorTimes="1" KeyWay="VarName" UseAlarmCheck="False" ServerURL="192.168.10.134" Port="502" DataFormat="ABCD">
      <ModbusGroup Name="读_读写" Description="Float_Bool" Type="ModbusTCP" StoreArea="保持寄存器" Length="15" Start="0" SlaveID="1" IsActive="true">
        <Variable Name="J0GCB11CF101" Description="1号活性碳过滤器入口水流量" Type="ModbusTCP" VarAddress="0" Scale="1" Offset="0" Start="0" AccessProperty="只读" AlarmEnable="False" ArchiveEnable="True" SetLimitEnable="False" ArchivePeriod="2" VarType="Float" />
        <Variable Name="J0GCB12CF101" Description="2号活性碳过滤器入口水流量" Type="ModbusTCP" VarAddress="2" Scale="1" Offset="0" Start="0" AccessProperty="只读" AlarmEnable="False" ArchiveEnable="True" SetLimitEnable="False" ArchivePeriod="2" VarType="Float" />
        <Variable Name="J0GCB13CF101" Description="3号活性碳过滤器入口水流量" Type="ModbusTCP" VarAddress="4" Scale="1" Offset="0" Start="0" AccessProperty="只读" AlarmEnable="False" ArchiveEnable="True" SetLimitEnable="False" ArchivePeriod="2" VarType="Float" />
        <Variable Name="J0GCB20CT101" Description="超滤装置进口母管水温度" Type="ModbusTCP" VarAddress="6" Scale="1" Offset="0" Start="0" AccessProperty="只读" AlarmEnable="False" ArchiveEnable="True" SetLimitEnable="False" ArchivePeriod="2" VarType="Float" />
        <Variable Name="J0GCB20CQ101_NTU" Description="超滤装置进口母管浊度" Type="ModbusTCP" VarAddress="8" Scale="1" Offset="0" Start="0" AccessProperty="只读" AlarmEnable="False" ArchiveEnable="True" SetLimitEnable="False" ArchivePeriod="2" VarType="Float" />
        <Variable Name="J0GCB31CL101" Description="1号超滤出水水箱水位" Type="ModbusTCP" VarAddress="10" Scale="1" Offset="0" Start="0" AccessProperty="读写" AlarmEnable="False" ArchiveEnable="True" SetLimitEnable="False" ArchivePeriod="2" VarType="UShort" />
        <Variable Name="J0GCB32CL101" Description="2号超滤出水水箱水位" Type="ModbusTCP" VarAddress="11" Scale="1" Offset="0" Start="0" AccessProperty="读写" AlarmEnable="False" ArchiveEnable="True" SetLimitEnable="False" ArchivePeriod="2" VarType="UShort" />
        <Variable Name="J0GCB50CP101" Description="超滤出水升压泵出口母管压" Type="ModbusTCP" VarAddress="12" Scale="1" Offset="0" Start="0" AccessProperty="读写" AlarmEnable="False" ArchiveEnable="True" SetLimitEnable="False" ArchivePeriod="2" VarType="UShort" />
        <Variable Name="J0GCB60CF101" Description="超滤反洗水泵出口母管流量" Type="ModbusTCP" VarAddress="13" Scale="1" Offset="0" Start="0" AccessProperty="读写" AlarmEnable="False" ArchiveEnable="True" SetLimitEnable="False" ArchivePeriod="2" VarType="UShort" />
        <Variable Name="J0GCB60CP101" Description="超滤反洗水泵出口母管压力" Type="ModbusTCP" VarAddress="14" Scale="1" Offset="0" Start="0" AccessProperty="读写" AlarmEnable="False" ArchiveEnable="True" SetLimitEnable="False" ArchivePeriod="2" VarType="UShort" />
      </ModbusGroup>
    </ModbusNode>
  </NodeClass>
  <NodeClass Name="Custom客户端" Description="Custom相关客户端" />
  <NodeClass Name="服务器" Description="自定义服务器" />
</Settings>
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CMStestMdbus
{
    public class ActualDataProvider
    {
        public int Delete(ActualData t)
        {
            if (t == null) return 0;
            ActualData model = SqlSugarHelper.db.Queryable<ActualData>().Where(item => item.Id == t.Id).First();
            if (model == null) return 0;

            int count = SqlSugarHelper.db.Deleteable<ActualData>(model).ExecuteCommand();
            return count;
        }

        //public int Insert(ActualData t)
        //{
        //    int count = SqlSugarHelper.db.Insertable<ActualData>(t).ExecuteCommand();
        //    return count;
        //}

        /// <summary>
        /// 批量插入
        /// </summary>
        /// <param name="t"></param>
        /// <returns></returns>
        /// 
        public int Insert(List<ActualData> t)
        {
            if (t == null) return 0;
            ////if (t.Value == null) return 0;
            ////方法1
            //foreach (var item in t.ToArray())
            //{
            //    SqlSugarHelper.db.Insertable<ActualData>(item).ExecuteCommand();
            //}
            ////方法2
            int count = SqlSugarHelper.db.Insertable(t.ToArray()).ExecuteCommand();//官方推荐的
            return count;
        }


        public List<ActualData> Select()
        {
            DateTime day = DateTime.Now.AddDays(-1);
            return SqlSugarHelper.db.Queryable<ActualData>().Where(u => SqlFunc.Between(u.InsertTime, day,DateTime.Now)).ToList();
            //return SqlSugarHelper.db.Queryable<ActualData>().ToList();
        }

        public int Update(ActualData t)
        {
            if (t == null) return 0;
            ActualData model = SqlSugarHelper.db.Queryable<ActualData>().Where(item => item.Id == t.Id).First();
            if (model == null) return 0;
            model.Value = t.Value;
            model.Remark = t.Remark;
            model.VarName = t.VarName;
            int count = SqlSugarHelper.db.Updateable<ActualData>(model).ExecuteCommand();
            return count;
        }
    }
}

using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CMStestMdbus
{
    public class SqlSugarHelper
    {
        public static string ConnectionString = string.Empty; //必填, 数据库连接字符串
        public static SqlSugarClient db
        {
            get => new SqlSugarClient(new ConnectionConfig()
            {
                ConnectionString = ConnectionString,
                DbType = SqlSugar.DbType.Sqlite,         //必填, 数据库类型
                IsAutoCloseConnection = true,       //默认false, 时候知道关闭数据库连接, 设置为true无需使用using或者Close操作
                InitKeyType = InitKeyType.SystemTable    //默认SystemTable, 字段信息读取, 如:该属性是不是主键,是不是标识列等等信息
            });
        }
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CMStestMdbus
{
    public class SqlSugarService
    {
        /// <summary>
        /// 设置连接字符串
        /// </summary>
        /// <param name="ConnectionStr"></param>
        public static void SetConnectionStr(string ConnectionStr)
        {
            SqlSugarHelper.ConnectionString = ConnectionStr;
        }
    }
}

//SqlSugarService.SetConnectionStr("Data Source=" + Application.StartupPath + "\\Config\\ScadaData.db;Pooling=true;FailIfMissing=false");

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

潘诺西亚的火山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值