jenkins 一键式部署的工具

一键式部署的工具

1、安装
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

C:\ProgramData\Jenkins.jenkins\secrets\initialAdminPassword安装完成!

注册账号:

注册的账户信息:

840643859@qq.comndk

与il2cpp作用:

ndk的作用: 把代码编译成il2cpp,底层c++

il2cpp的特点: 打包更慢,但代码运行更快,效率更高

访问其他电脑的jenkins:

把这个路径的 localhost,改成要访问的电脑的ip,再加 冒号 8080 。

全局配置:

在这里插入图片描述

插件安装:

在这里插入图片描述

有一个失败了,不知道要紧不?
在这里插入图片描述

在全局工具配置中,加入多个版本的unity.

在这里插入图片描述

重启jenkins慢的问题:

出现这个情况时,是因为Jenkins的默认更新镜像用的国外的镜像,把更新镜像改成国内的即可。

首先打开Jenkins的安装目录, 比如我是安装在 D:\Program Files (x86)\Jenkins 这个目录,打开这个目录,然后打开hudson.model.UpdateCenter.xml 这个配置文件,

将https://updates.jenkins.io/update-center.json 修改成http://mirror.xmission.com/jenkins/updates/update-center.json ,
然后打开windows服务管理器,重启Jenkins服务,再重新刷新Jenkins启动页面即可

新建任务

定时构建:

在这里插入图片描述

比如设置,每15分钟执行一次,如下:

在这里插入图片描述

执行unity命令、windows命令

在这里插入图片描述

构建完成后,邮件提醒:

在这里插入图片描述

归档:将打包出来的东西,存储起来

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

Jenkins部署svn更新:

先拉项目,再具体的项目配置中,输入要执行的指令。

通常来说, 都是先清理,再还原,再更新。。 再执行打包等。

在这里插入图片描述

执行更新时, 需要提供 账号密码

然后点这里,”开始构建“:
在这里插入图片描述

发现报错了: (需要在jenkins的环境变量中,配置svn)
在这里插入图片描述

为jenkins 配置svn 环境变量
在这里插入图片描述

再测试:svn执行就正常了。
在这里插入图片描述

Jenkins执行 Unity函数在unity写一个编辑器的静态方法:
在这里插入图片描述

Unity需要执行的指令:
-projectpath %WorkPath% -quit -batchmode -executeMethod TestEditor.JenkinsTest -logFile "D:\logtxt"如下:
https://docs.unity3d.com/cn/2020.2/Manual/CommandLineArguments.html
在这里插入图片描述

改指令为unity提供的固定写法。

指令说明:
-projectpath 后面接unity项目的位置
-quit 表示执行完,会退出unity
-batchmode 表示不打开unity软件,在后台静默执行,
-executeMethod 后面接Unity中的静态类.方法名, 表示要执行的函数,
-logFile 后接txt文件,unity将写入执行过程中产生的log信息

如下:
在这里插入图片描述

在Jenkins配置Unity需要执行的指令:
在这里插入图片描述

可以先测试在终端执行的效果。

测试完成可以看到,时间已经改到了。

注意!需要关掉Unity,才能使用后台执行。 因为每个unity工程同一时间只能开启一个实例。

再测试在Jenkins中的执行:执行中: 有一点点久。执行完成: (第14条)查看log信息的入口:log信息的开始部分:最后部分有Success. (如果脚本有错误或其他问题,会Failed),准备工作,测试完成!Jenkins执行Unity打应用包先查看打资源包与打应用包的流程:先打ab资源:打ab——将xml转为二进制:xml是游戏的数据配置文件。先看下,为什么要xml转成二进制。转的方法:先获取目录下所有 .xml再转二进制:
* 读取并记录资源的依赖信息,并进行添加。(处理依赖,也是为了减少资源冗余)

* 再添加prefab本体。


* 再删除.manifest文件。 (因为这里是自己手动配置的,不用manifest管理, 不用是因为manifest管理会有冗余)


* 再对以上信息(二进制、依赖资源、prefab本体),setABname, 标记assetBundle名字。

assetImporter.assetBundleName = name;
* 最后调用ab资源的打包的API:

AssetBundleManifest manifest = BuildPipeline.BuildAssetBundles(m_BunleTargetPath, BuildAssetBundleOptions.ChunkBasedCompression, EditorUserBuildSettings.activeBuildTarget);
* 打资源完成后,将资源拷贝到对应的目录,以用于之后打包时的需要。

* 打应用包:

BuildPipeline.BuildPlayer(FindEnableEditorrScenes(), savePath, EditorUserBuildSettings.activeBuildTarget, BuildOptions.None);savePath:是打出的包要存放的路径
* 最后再删除ab包, 因为下次还要重新打。

至此,打资源及打包的流程就过了。

现在打包的入口,配置到jinKens中。再开始构建。

构建失败了,因为Unity没关。 不能开两个实例。

把这个关了,再试下。在构建的过程中,可以点控制台,看到不断有信息在输出。SUCCESS 成功了:

查看目录路径,可看到包已经打出来了。

但这样只能存在打包机上。还未完成。

应该把这个包压缩成zip格式,并传到ftp,供其他人通过ftp随时下载。

使用rar软件,用命令行,将打出的win包,压缩zip

Winrar.exe a -r -ep1 “输出路径.rar” "需要压缩的文件夹路径"比如我的rar在此:开始测试在终端压缩:压缩测试成功:压缩成功了, 但如果是自动化处理,包名就不能写死,因为包名是每次打包时根据时间生成的不同数字。思路:可以在打包时,将包名写入文本文件。打包完后,再读这个文本里的名字,再调用压缩命令。先添加windows批处理命令。增加指令:第一行:进入d盘(因为buildname.txt文件在f盘)第二行:读取 buildname.txt 里的版本号第三行:进入f盘(因为rar软件在f盘)第四行:调用rar软件进行压缩。对应路径:上面有用到之前说到过的, set name=%%i 赋值, %name% 取值这里有个坑,要注意,如果在vs改了代码,不能直接去jinkens执行,要先打开unity,进行编译。报错了:又失败了,调的API的问题,换一下:再打包一次:踩坑! 在修改环境变量后,需要重启jenkins,才能生效。重启的方法:访问此路径,之后点击确认。有个奇怪的问题出现了,我修改的项目的路径WorkPath,但无论怎么打包,都是打的没改之前的那个。配置的E盘:运行的却是D盘:很怪,查一下:但是上面的svn却可以正确引用到:尝试多次,还是不行,再试试加双引号的方式:还是不行:可以看到,无论怎么设置 %WorkPath"都读不到,且前面还会变成 ”工作空间“的路径值。如果不设置”工作空间“,则路径也会变成jinkens的默认路径值。可能是workPath命名被缓存了,或者与系统变量有冲突, 我都改成WorkPath1试下。还是不行。然后看了下参数的说明:原来,他这里的projectpath的p, 要小写。而unity的api中的p,是大写。坑爹!难怪一直读不到 projectPath改好了,再试一下。还是报错了:再重启试一下:还是有问题。又试了几次,也不行。再看下网上别人怎么弄的。发现有个人是用 取 变 量 值 的 。 但 教 程 上 一 直 是 取变量值的。但教程上一直是%xxx%取值的,且教程上是对的。但现在看来教程的版本可能跟我的不同了。于是我也用 取变量。但还是报错了。原来语法要改成这样,只有这样,才不会报错。看下我失败的尝试。新的报错:它说这个类没有。但其实是有的。测试了下,还是不行。上面行了,其实是因为,还是用到了D盘里面的项目了。最后再改了路径,现在是真的用到E盘的正确路径了。踩坑!unity编辑器指令,需要用 $符号,且特定方式取值。它说,此处不应有?是什么意思可能是因为这个文件是在e盘,而之前我是进入的f盘。改成e试下:还是不行:原来这里是必须加分号(😉 的, 在教程里,只是用了空格,也通过了。!加好,再试一下!还是不行。在终端测试下,也确实不行。踩坑,再检查一次,发现是这里少了个"in"改好,再试一次:现在报winRar的错误了:改成大写试一下:还是不行, 试一下从终端跑下:可能是要先进f盘, 再cd踩坑,windows终端上,要cd之前,一定要先进入盘符。终于成功了。再看包体,已经自动压缩成rar了。归档:对归档存量的设置,如下:超过30天的将被删除, 最多保留30个,超过后,前面的也将被删除归档成品,选这里:然后在这里设置:它就会在打包完成后,将成品拷贝到目标位置去。到这里,PC打包完成。补充:Unity_Windows平台相关知识:https://docs.unity3d.com/cn/2019.4/Manual/WindowsStandaloneBinaries.html成包的各部分含义说明:如下,是unity文档的说明:IL2CPP:IL2CPP的含义:( Intermediate Language To C++)使用 IL2CPP 构建项目时,Unity 会在创建本机二进制文件之前将脚本和程序集内的 IL 代码转换为 C++

再讲Jenkins安卓打包

* 先新建项目,

再转为安卓平台
* 配置安卓所需插件:

检查构建安卓时,所需的工具配置: sdk, jdk , ndk如sdk:SDK为第三方开发者提供特定的软件包、软件框架、硬件平台、操作系统等创建应用软件开发工具的集合,提供应用程序接口API。SDK工程师为辅助开发某类软件的相关文档、范例和工具的集合,使用SDK可以提高开发效率,更简单的接入某个功能。一个产品想实现某个功能,可以找到相关的SDK,工程师直接接入SDK,就不用再重新开发了jdk:(开发JAVA程序的开发包,JDK里面有JAVA的运行环境(JRE)),ndk:(构建il2cpp时需要,mono包不需要)
* 再将jenkins配置项,与之前win的一样弄,拷贝过来,

* 再把WorkPath项目路径之类的,改成安卓项目的。


* 再把归档的文件,改成: .apk


* 第1次测试打包,失败了:


* 定位错误的方法:

打开日志,按error寻找错误: 发现没有再按失败“failure"找错误,发现有7个,但都不是错误的提示:按 "not found"寻找错误, 因为构建过程中,常会因为构建所需要的东西没提供到,所以报这个错:比如下方:但上面这个其实不是根本性的错误,在unity官方论坛,有技术人员回复如下:百度查一下: 什么鬼都没有,竟然没查到符合的。 百度怎么还不凉凉?谷歌查一下:很多符合的。在第二条中找到有用的信息了:所以,再按"exception"寻找错误:就是说,安卓应用,没有进行签名的意思。win包不用签名,但安卓应用包需要。安卓签名的作用: 加密包体。生成一个keystore,可以保护应用包,对其数据加密。使其难以被破解或逆向解包。签名需要秘钥,如何生成安卓秘钥:以下是生成所需指令:keytool -genkey -alias android.keystore -keyalg RSA -validity 36500 -keystore RealFrame.keystore指令的含义:指令生成过程:需要输入密码、个人信息之类的。这里就生成了秘钥:再到Unity_Android平台的发布配置处,添加刚才的秘钥:并输入刚才加的新密码:再测试,手动在unity打包:Build Android中…包打出来了:再测试下,jenkins打包:报错了:Unable to locate Android SDK.说定位不到sdk:但我在编辑器中是已经设置了的。在网上查了下,说如果没安装到原生的位置,则要在环境变量里设置。加好再测试,已经没有刚才的报错了。但还是失败了:Failed to load signer "signer #1"因为安卓平台那边还有个设置:默认是 debug, 这样就没应用到之前加的那个签名。需要改成: 选择到刚才加的秘钥。再测试直接在unity打包: 成功了:重启unity,再进入,发现密码消失了, 所以在这里手动,是没用的,unity不会保存。需要每次打包时动态加,如下:在打包的代码中,打包之前,动态的设置。 参数信息、密码信息等。还是失败了, 但不是之前的那个报错,说明问题更近一步了。检查log, 没有error, 没有fail, 也没有not found之类的。可能的错误,我这里是调用的BuildPC, 但目前我是打安卓包, 所以可能参数不对。改下这里调用unity的配置:改成 BuildApp.BuildAndroid,再试一下:失败了,看下报错,好像是打包过程中,有的字符串处理异常。这个不好排查,先在unity执行打包试下。发现在unity可以打成功:那换个接口试下,改成这个接口: 因为刚才在编辑器测试,也是用这个接口的:jenkins这里命令改一下:CMD 参数错误:发现这里少了一个横杠,加上:再测试: 还有错误:检查之前的,发现这里的命令,前面就是没有“-”的, 再加回来:再把这个去掉,因为可能不适用这个命令:经历了12次失败,终于成功了。相比之前win的出包,经理了50多次失败,已经习惯了。自动打出的包:在Jenkins中,增加选项参数:增加安卓渠道:构建的时候,就可以下拉选择了:和打包PC一样,接下来:jenkins传递参数、unity接收参数、unity设置参数
* 将渠道参数,加入到打包的指令中,进行传递参数。

* 再在unity中,改代码,接收参数, 以获取渠道为例:

string[] parameters = Environment.GetCommandLineArgs();if (str.StartsWith(“Place”))
* 并在unity设置应用参数。 (如下,以渠道、版本号等举例)

string suffix = "_";        
if (setting.Place != Place.None)        {            //代表了渠道包            string symbol = PlayerSettings.GetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android);            PlayerSettings.SetScriptingDefineSymbolsForGroup(BuildTargetGroup.Android, symbol + ";" + setting.Place.ToString());            suffix += setting.Place.ToString();        }        if (!string.IsNullOrEmpty(setting.Version))        {            PlayerSettings.bundleVersion = setting.Version;            suffix += setting.Version;        }        if (!string.IsNullOrEmpty(setting.Build))        {            PlayerSettings.Android.bundleVersionCode = int.Parse(setting.Build);            suffix += "_" + setting.Build;        }        if (!string.IsNullOrEmpty(setting.Name))        {            PlayerSettings.productName = setting.Name;            //PlayerSettings.applicationIdentifier = "com.TTT." + setting.Name;        }        if (setting.MulRendering)        {            PlayerSettings.MTRendering = true;            suffix += "_MTR";        }        else        {            PlayerSettings.MTRendering = false;        }        if (setting.IL2CPP)        {            PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, ScriptingImplementation.IL2CPP);            suffix += "_IL2CPP";        }        else        {            PlayerSettings.SetScriptingBackend(BuildTargetGroup.Android, ScriptingImplementation.Mono2x);        }        if (setting.Debug)        {            EditorUserBuildSettings.development = true;            EditorUserBuildSettings.connectProfiler = true;            suffix += "_Debug";        }        else        {            EditorUserBuildSettings.development = false;        }        return suffix;然后调用unity函数,打包:    public static void BuildAndroid()    {        //打ab包        BundleEditor.Build();        PlayerSettings.Android.keystorePass = "sikiocean";        PlayerSettings.Android.keyaliasPass = "sikiocean";        PlayerSettings.Android.keyaliasName = "android.keystore";        PlayerSettings.Android.keystoreName = Application.dataPath.Replace("/Assets", "") + "/realfram.keystore";        BuildSetting buildSetting = GetAndoridBuildSetting();        string suffix = SetAndroidSetting(buildSetting);        //生成可执行程序        string abPath = Application.dataPath + "/../AssetBundle/" + EditorUserBuildSettings.activeBuildTarget.ToString() + "/";        //清空生成的文件夹        DeleteDir(m_AndroidPath);        Copy(abPath, Application.streamingAssetsPath);        string savePath = m_AndroidPath + m_AppName + "_Andorid" + suffix + string.Format("_{0:yyyy_MM_dd_HH_mm}.apk", DateTime.Now);        BuildPipeline.BuildPlayer(FindEnableEditorrScenes(), savePath, EditorUserBuildSettings.activeBuildTarget, BuildOptions.None);        DeleteDir(Application.streamingAssetsPath);    }设置完这些,再来测试下打包:有错:看代码:应该是这个build参数,没有传对。所以转不了整型。原来是这里没有加Build参数,所以传了空。至于这个Bundle Version有什么含义:可以看这里:可以理解为,Version是大版本号, Bundle Version Code是小版本号,所以一般是小数,0.1之类的。在jenkins配好 BundleVersion,再测试:失败:还是一样的错误,再看代码,这里是要转整数类型的,传个0.1是浮点类型,肯定有问题。改成1再试下。成功了。至此,打包安卓完成!

开始打包IOS流程

打包IOS比较复杂主要流程:需要先打出Xcode工程, 然后按需修改xcode工程的参数,再用xocde工程编译,并打出 ipa的应用包。
* 先在pc导出xcode工程如下:

* 安装这个软件:

iOS Project Builder for Unity Installer
* 安装软件后,有两个文件夹,拷贝其中全部的两个文件至U盘:

* 再把导出的xcode工程,也拷贝至U盘,如下:


* 申请appleID,苹果有效期一年的开发者账号(如无),一年699块

申请的网址: https://developer.apple.com
* 申请开发者证书(如无)

点击“证书”:点击“添加”填写描述“随意填”,但Bundle ID “和unity中设置的Bundle ID 相同再点”注册“,提交这个页面:到这一步,就注册完成了:然后,请求证书,导入钥匙串访问。钥匙串在mac app应用这里进:点这里”请求证书“输入邮件信息等,再点“继续”:设置”证书的名字“与存的位置 :钥匙串文件创建好了,如下:再创建开发者证书:(需要用到上面的钥匙串)创建开发者证书:选这个,再点继续:再下一步的页面, 再点”继续“:选择”钥匙串文件“:再点”Continue"继续:然后即可下载证书:下载后,直接点击证书,自动跳转到添加证书的界面:再弹窗界面用户名密码:证书就被添加到“钥匙串”中了:再创建用于发布的证书:按刚才的流程,点"Continue”, 一直到 选择钥匙串文件下载后点击,添加到“钥匙串访问”中:分别之前那创建两个证书对应的描述文件:选中:再选中 bundle id: (之前创建的)再选择证书,(之前创建的):再点继续,选择设备:填写设备名与UDID:udid是手机等终端的唯一ID,可用蒲公英 进行查询。再点继续:显示了设备的信息:一直点下一步 ,就完成了,在设备列表中可以看到刚才添加的设备了:然后重新在刚才创建 描述文件的流程里来一次,按同样的方式,到这一步:填写配置信息, 取个新名字。再点“继续”,就生成了描述文件:有了这个描述文件,xcode才能编译。双击该文件:就会打开xcode了。再创建Distribution(发布)文件:同样的步骤:distribution发布文件,用于发布到appStore,是可以给所有appstore用户下载的,所以不用注册设备。之前Development开发文件,是用于注册特定的设备用于调试的。 未在列表中注册的设备,不能安装此应用。Xcode编译:把之前u盘的ios导出工程,与工具的文件,拷贝到苹果机。工具软件的作用:可以把苹果机上配置信息,如钥匙串、证书、等等,都保存下来,以用于在PC上,在jenkins中使用。双击xcode工程打开:在打开的工程中添加苹果账号:如下:再选择刚才的账号:会显示证书和描述文件已经识别到。再点这里,真机调试,即编译:第一次编译比较慢,后面比较快。编译时,显示有个错误,点击它:显示了错误信息:Command /bin/sh/ failed with exit 126选择到Unity-iphone: 先删除到 Shell里的内容, 再编译,显示Build成功了:但无法安装到iphone6,显示目标手机太低,而xcode要求的版本比较高。修改调低这里的 “Deployment Target“部署的目标”:再运行即可。上面那个问题,是mac系统,及xcode版本过低, 需要升级, 之后再编译和打包。导出和编译正常后,再执行之前工具是的这个文件:(拷贝MAC配置文件)到这个界面,输入自定义密码,(之后从PC执行另一个指令时,需要用到)使用工具,从mac导出资料完成:如下: 再把文件拷贝到winPC执行此文件出现命令行:解压ios的sdk,把mac上的信息,Develop环境,工程文件等,解压出来。提示成功:
查看结果,在软件安装位置出现的 SDK,即是需要的从mac上弄过来的工程文件:

ios工程的sdk界面发下:

双击打开钥匙串工具:

界面如下:

选中到Development:

使用此工具打包执行:

选择要打包的项目: (unity导出的xcode项目)

于是开始编译打包了:

完成后,在所选工程的目录下,即会生成ios可安装的 .ipa应用包

使用 iTools安装:

选择刚才的安装包:

验证失败问题:

可能是设备没有越狱的原因。
是证书问题,需要在工具软件重新导入下证书,如下:
先下载证书

再Import刚才的证书:

使用PP助手进行安装,在提示”设备未越狱“时,仍可点击后面的尝试安装,可安装成功。

如果要再次打包,需要清空以下四个文件:

也可以点这里清理:

在UnityEditor使用此代码,可以在打包完成后,自动设置需要的参数。
PostProcessBuild,会在Build完成后自动回调。

Ios Builder 的命令行
build.cmd “F:\Work\Teach\RealFram_IOS\BuildTarget\IOS\RealFram_IOS_0.1_0_MTR_Dynamic_2019_02_18_09_38” -xcname “Unity-iPhone” -xcconf “Release” -archs “armv7 arm64” -type crt -multicore -pngcrush -ipa -strip bitcode -identity “iPhone Developer haiyang feng (NDR72C36VV).cer:AppleIncRootCertificate.cer:AppleWWDRCA.cer:private_key00.key:ocean” -provision “RealFram (QFNVK65JTJ).mobileprovision”

搭建ios的jenkins打包:

调用Unity打包函数:

-projectpath %WorkPath% -quit -batchmode -executeMethod BuildApp.BuildIOS Version= V e r s i o n B u i l d = Version Build= VersionBuild=Build Name= N a m e M u l R e n d e r i n g = Name MulRendering= NameMulRendering=MulRendering DynamicBatching=$DynamicBatching -logFile "D:\log.txt"

unity ios 接收并设置参数:

    if (!string.IsNullOrEmpty(setting.Version))
    {
        PlayerSettings.bundleVersion = setting.Version;
        suffix += setting.Version;
    }
    if (!string.IsNullOrEmpty(setting.Build))
    {
        PlayerSettings.iOS.buildNumber = setting.Build;
        suffix += "_" + setting.Build;
    }
    if (!string.IsNullOrEmpty(setting.Name))
    {
        PlayerSettings.productName = setting.Name;
        //PlayerSettings.applicationIdentifier = "com.TTT." + setting.Name;
    }


    if (setting.MulRendering)
    {
        PlayerSettings.MTRendering = true;
        suffix += "_MTR";
    }
    else
    {
        PlayerSettings.MTRendering = false;
    }


    if (setting.DynamicBatching)
    {
        suffix += "_Dynamic";
    }

unity ios打包函数,打出xcode工程:

public static void BuildIOS()
{
    //打ab包
    BundleEditor.Build();
    BuildSetting buildSetting = GetIOSBuildSetting();
    string suffix = SetIOSSetting(buildSetting);


    //生成可执行程序
    string abPath = Application.dataPath + "/../AssetBundle/" + EditorUserBuildSettings.activeBuildTarget.ToString() + "/";
    //清空生成的文件夹
    DeleteDir(m_IOSPath);
    Copy(abPath, Application.streamingAssetsPath);
    string name = m_AppName + "_IOS" + suffix + string.Format("_{0:yyyy_MM_dd_HH_mm}", DateTime.Now);
    string savePath = m_IOSPath + name;
    BuildPipeline.BuildPlayer(FindEnableEditorrScenes(), savePath, EditorUserBuildSettings.activeBuildTarget, BuildOptions.None);
    DeleteDir(Application.streamingAssetsPath);
    WriteBuildName(name);
}

调用iosBuild,打出ipa包

"%WorkPath%\BuildTarget\IOS%name%" -xcname "Unity-iPhone" -xcconf "Release" -archs "armv7 arm64" -type crt -multicore -pngcrush -ipa -strip bitcode -identity "iPhone Developer haiyang feng (NDR72C36VV).cer:AppleIncRootCertificate.cer:AppleWWDRCA.cer:private_key00.key:ocean" -provision "RealFram (QFNVK65JTJ).mobileprovision"

打包完了,压缩归档:
将打好的ipa移动到目标位置,进行归档,并改名。

move “ipaPath” “DirPath”

查看调用软件时,传递的参数:
Proecess Monitor

2022年4月6日20:23:31

后续添加:
打包完后,邮箱提醒
热更新部署

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值