JCO java connector

package pack.java.jsf.connsapserver;

import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import com.sap.conn.jco.AbapException;
import com.sap.conn.jco.JCoAttributes;
import com.sap.conn.jco.JCoDestination;
import com.sap.conn.jco.JCoDestinationManager;
import com.sap.conn.jco.JCoException;
import com.sap.conn.jco.JCoField;
import com.sap.conn.jco.JCoFunction;
import com.sap.conn.jco.JCoListMetaData;
import com.sap.conn.jco.JCoMetaData;
import com.sap.conn.jco.JCoStructure;
import com.sap.conn.jco.JCoTable;
import com.sap.conn.jco.ext.DestinationDataProvider;

/**
 * Java调用SAP服务类;
 * @author Administrator
 *
 */
public class CallSapService
{
    private static String ABAP_AS_POOLED = "ABAP_AS_WITH_POOL";
    private JCoDestination destination=null;
    JCoFunction  function = null;
    static
    {
        Properties connectProperties = new Properties();
        connectProperties.setProperty(DestinationDataProvider.JCO_ASHOST, "192.168.12.2");
        connectProperties.setProperty(DestinationDataProvider.JCO_SYSNR,  "00");
        connectProperties.setProperty(DestinationDataProvider.JCO_CLIENT, "800");
        connectProperties.setProperty(DestinationDataProvider.JCO_USER,   "admin");
        connectProperties.setProperty(DestinationDataProvider.JCO_PASSWD, "testUser");
        connectProperties.setProperty(DestinationDataProvider.JCO_LANG,   "en");
        //设置连接池容量;
        connectProperties.setProperty(DestinationDataProvider.JCO_POOL_CAPACITY, "3");
        //最多的连接数;
        connectProperties.setProperty(DestinationDataProvider.JCO_PEAK_LIMIT,    "10");
        //创建数据文件;使用连接池文件;
        createDataFile(ABAP_AS_POOLED, "jcoDestination", connectProperties);
    }
    
    /**
     * 创建数据文件;
     * @param name
     * @param suffix
     * @param properties
     */
    private static void createDataFile(String name, String suffix, Properties properties)
    {
    	//文件名+.jcoDestination后缀;
        File cfg = new File(name+"."+suffix);
        //如果文件不存在;
        if(!cfg.exists())
        {
            try
            {
                FileOutputStream fos = new FileOutputStream(cfg, false);
                //在属性文件存放;
                properties.store(fos, "This Is Config File !");
                fos.close();
            }
            catch (Exception e)
            {
                throw new RuntimeException("Unable to create the destination file " + cfg.getName(), e);
            }
        }
    }
    
    /***
     * 使用连接池获得连接对象;
     * @throws JCoException
     */
    public String connectUsingPool() throws JCoException
    {
    	//通过连接池获得连接;
        JCoDestination destination = JCoDestinationManager.getDestination(ABAP_AS_POOLED);
        destination.ping();
        //获得SAP系统中所有的属性值;
        JCoAttributes jCoAttributes=destination.getAttributes();
        System.out.println("Attributes:");
        System.out.println(jCoAttributes.toString());
        return jCoAttributes.toString();
    }
    
    /**
     * 简单的调用Function Module.
     * @throws JCoException
     */
    public String simpleCall(String functionName,Map<String,Object> importMap,List<String> exportList) throws JCoException
    {
    	//通过ABAP_AS_POOLED(连接池)来获得JCoDestination对象;
        JCoDestination destination = JCoDestinationManager.getDestination(ABAP_AS_POOLED);
        //通过destination目的地对象,取回function module. 返回一个function 对象;"ZHT_HELLOWORLD_FUNCTION"
        JCoFunction function = destination.getRepository().getFunction(functionName);
        if(function == null)
        	//抛出运行时异常;方法,不存在SAP系统中.
            throw new RuntimeException("BAPI_COMPANYCODE_GETLIST not found in SAP.");
        
        //为function module中的导入的参数设置值;REQUTEXT ->代表导入参数名称,->Hello SAP代表导入参数的值;
        if(importMap!=null && importMap.size()>0){
        	for(Iterator<String> iterator=importMap.keySet().iterator();iterator.hasNext();){
        		String key=iterator.next();
        		//为Function Module中的import导入参数,设置值;
        		function.getImportParameterList().setValue(key,importMap.get(key));
            }
        }
        try
        {
        	//执行此destination目的地对象;
            function.execute(destination);
        }
        catch(AbapException e)
        {
            System.out.println(e.toString());
            return null;
        }
        String result=null;
        //调用Function Module中 Export 返回参数(Z_RESULT).;进行输出;
    	for(String exportName:exportList){
    		//输出;
    		result=function.getExportParameterList().getString(exportName);
        }
    	return result;
    }
    
    /**
     * 调用SAP系统中Function Module结构Structure;
     * @throws JCoException
     */
    public Map<String,String> workWithStructure(String functionName,String structure) throws JCoException
    {
    	Map<String,String> map =new HashMap<String, String>();
    	//通过ABAP_AS_POOLED(连接池)来获得JCoDestination对象;
        JCoDestination destination = JCoDestinationManager.getDestination(ABAP_AS_POOLED);
        //通过destination目的地对象,取回function module. 返回一个function 对象;"RFC_SYSTEM_INFO"
        JCoFunction function = destination.getRepository().getFunction(functionName);
        if(function == null){
            throw new RuntimeException("function not found in SAP.");
        }			
        try
        {
        	//远程执行function module.
            function.execute(destination);
        }
        catch(AbapException e)
        {
            throw new RuntimeException(e);
        }
        //根据export中的结构名获得Structure.-->"RFCSI_EXPORT"
        JCoStructure exportStructure = function.getExportParameterList().getStructure(structure);
        //destination.getAttributes().getSystemID() -> RD1系统标识符;
        System.out.println("System info for " + destination.getAttributes().getSystemID() + ":\n");
        //exportStructure.getMetaData() 获得对应的Structure中的,Structure Name;
        for(int i = 0; i < exportStructure.getMetaData().getFieldCount(); i++) 
        {
        	//为Structure设置值;
        	exportStructure.setValue("NAME", "Admin");
        	exportStructure.setValue("AGE", 20);
        	exportStructure.setValue("ADDRESS", "湖南株洲");
        	exportStructure.setValue("PHONE", "95599");
        	
        	//获取Structre 中Field Name的名称 和值;
            System.out.println(exportStructure.getMetaData().getName(i) + ":\t" + exportStructure.getString(i));
        }
        //JCo still supports the JCoFields, but direct access via getXX is more efficient as field iterator
        for(JCoField field : exportStructure)
        {
        	//输出Structture中field name 和 field value;
            map.put(field.getName(),field.getString());
        }
        return map;
    }

    /**
     * 调用SAP系统中Function Module的Table->InnerTable;
     * 方法一;
     * @throws JCoException
     */
    public List<String> workWithTable() throws JCoException
    {
    	List<String>  list= new ArrayList<String>();
        JCoDestination destination = JCoDestinationManager.getDestination(ABAP_AS_POOLED);
        //根据Function Module中的返回 Table Name,获得JCOFunction对象;-->BAPI_COMPANYCODE_GETLIST;
        JCoFunction function = destination.getRepository().getFunction("BAPI_COMPANYCODE_GETLIST");
        if(function == null){
            throw new RuntimeException("BAPI_COMPANYCODE_GETLIST not found in SAP.");
        }
        try
        {
        	//远程执行;
            function.execute(destination);
        }
        catch(AbapException e)
        {
            System.out.println(e.toString());
            return null;
        }
        
        //获得结构Structure;
        JCoStructure returnStructure = function.getExportParameterList().getStructure("RETURN");
        //判断type不等于空;
        if (! (returnStructure.getString("TYPE").equals("")||returnStructure.getString("TYPE").equals("S"))  )   
        {
           throw new RuntimeException(returnStructure.getString("MESSAGE"));
        }
        //获得Function Module->Tables中的->COMPANYCODE_LIST 内表;
        JCoTable codes = function.getTableParameterList().getTable("COMPANYCODE_LIST");
        //循环内表里面所以的行;
        for (int i = 0; i < codes.getNumRows(); i++) 
        {
        	//为行设置值;
            codes.setRow(i);
            //输出内表中的COMP_CODE 字段 和 COMP_Name字段的值;
            System.out.println(codes.getString("COMP_CODE") + '\t' + codes.getString("COMP_NAME"));
        }
        
        //循环为
        codes.firstRow();
        for (int i = 0; i < codes.getNumRows(); i++, codes.nextRow()) 
        {
        	//获得 名称为:BAPI_COMPANYCODE_GETDETAIL的function;
            function = destination.getRepository().getFunction("BAPI_COMPANYCODE_GETDETAIL");
            if (function == null) 
                throw new RuntimeException("BAPI_COMPANYCODE_GETDETAIL not found in SAP.");
            
            //为BAPI_COMPANYCODE_GETDETAIL->Import->COMPANYCODEID 字段设置值;
            function.getImportParameterList().setValue("COMPANYCODEID", codes.getString("COMP_CODE"));
            //为BAPI_COMPANYCODE_GETDETAIL->Export->COMPANYCODEID 字段设置值;
            function.getExportParameterList().setActive("COMPANYCODE_ADDRESS",false);
            
            try
            {
            	//远程执行;
                function.execute(destination);
            }
            catch (AbapException e)
            {
                System.out.println(e.toString());
                return null;
            }
            //获得 名称为:BAPI_COMPANYCODE_GETDETAIL Function中->Export->RETURN Field 的Structure;
            returnStructure = function.getExportParameterList().getStructure("RETURN");
            //判断是否是系统类型;S,W,"";
            if (! (returnStructure.getString("TYPE").equals("") ||
                   returnStructure.getString("TYPE").equals("S") ||
                   returnStructure.getString("TYPE").equals("W")) ) 
            {
                throw new RuntimeException(returnStructure.getString("MESSAGE"));
            }
           
            //获得名称为 COMPANYCODE_DETAIL 的Structure 结构;
            JCoStructure detail = function.getExportParameterList().getStructure("COMPANYCODE_DETAIL");
            //输出结构中的字段;
            System.out.println(detail.getString("COMP_CODE") + '\t' +
                               detail.getString("COUNTRY") + '\t' +
                               detail.getString("CITY"));
            list.add(detail.getString("COMP_CODE") + '\t' +detail.getString("COUNTRY") + '\t' +detail.getString("CITY"));
        }
        return list;
    }
    
    /**
     * 处理内表,方法二;
     * @return
     * @throws JCoException 
     */
    public List<String> workInnerTable() throws JCoException{
    	List<String> list=new ArrayList<String>();
    	JCoDestination jcoDestination=JCoDestinationManager.getDestination(ABAP_AS_POOLED);
    	JCoFunction function = jcoDestination.getRepository().getFunction("ZHT_INNERTABLE_FUNCTION");
    	if(function == null){
    		throw new RuntimeException("方法不存在SAP系统中.");
    	}
    	function.execute(jcoDestination);
    	function.getImportParameterList().setValue("PARAMETER", "SELECT");
    	String str=function.getExportParameterList().getString("ZHOBBIES");
    	System.out.println("我的爱好是:"+str);
    	JCoTable table = function.getTableParameterList().getTable("Z_DATA_LISTTABLE");
    	JCoTable infoTable=function.getTableParameterList().getTable("Z_DATA_INFOTABLE");
    	System.out.println("循环输出第一个(Z_DATA_LISTTABLE)内表:");
    	infoTable.deleteAllRows();
    	for(int i = 0 ;i <table.getNumRows();i++){
    		table.setRow(i);
    		System.out.println(table.getRow()+"\t"+table.getString("NAME")+"\t"+table.getString("AGE")+"\t"+table.getString("ADDRESS")+"\t"+table.getString("PHONE"));
    		list.add(table.getString("NAME")+"  \t"+table.getString("AGE")+"  \t"+table.getString("ADDRESS")+"  \t"+table.getString("PHONE"));
    	}
    	System.out.println("\n循环输出第二个(Z_DATA_INFOTABLE)内表:");
    	for(int i = 0 ;i<infoTable.getNumRows();i++){
    		infoTable.setRow(i);
    		System.out.println(infoTable.getRow()+"\t"+infoTable.getString("NAME")+"\t"+infoTable.getString("AGE")+"\t"+infoTable.getString("ADDRESS")+"\t"+infoTable.getString("PHONE"));
    	}
    	function.execute(jcoDestination);
    	return list;
    }
    
    /***
     * 插入数据到SAP系统中;
     * @param functionName
     * @param tableName
     * @param dataList
     * @return
     * @throws JCoException
     */
    public List<Object[]> insertDATATOSAPTable(String functionName,String tableName,List<Object[]> dataList) throws JCoException{
    	//获得连接池连接;返回目标对象;
    	JCoDestination destination=JCoDestinationManager.getDestination(ABAP_AS_POOLED);
    	//从目标对象中的仓库,获得function Info.
    	JCoFunction function=destination.getRepository().getFunctionTemplate(functionName).getFunction();
    	if(function == null){
    		throw new RuntimeException(functionName+"此方法不存在SAP系统中!!");
    	}
    	function.execute(destination);
    	function.getImportParameterList().setValue("PARAMETER", "INSERT");
    	JCoTable infoTable =function.getTableParameterList().getTable(tableName);
    	if(infoTable == null){
    		throw new RuntimeException(tableName+"内表不存在"+functionName+"中!!");
    	}
    	int i=0;
    	for (Object[] obj :dataList) {
			infoTable.insertRow(i);
			infoTable.setValue("NAME", obj[0]);
    		infoTable.setValue("AGE", obj[1]!=null?Integer.parseInt(obj[1].toString()):obj[1]);
    		infoTable.setValue("ADDRESS", obj[2]);
    		infoTable.setValue("PHONE", obj[3]);
    		i+=1;
		}
    	function.execute(destination);
    	return dataList;
    }
    
    /**
     * 查询SAP透明表中的所有数据;
     * @param functionName
     * @param tableName
     * @return
     * @throws JCoException
     */
    public List<Object[]> searchSAPDATA(String functionName,String tableName) throws JCoException{
    	JCoDestination destination=JCoDestinationManager.getDestination(ABAP_AS_POOLED);
    	JCoFunction function=destination.getRepository().getFunctionTemplate(functionName).getFunction();
    	if(function == null){
    		throw new RuntimeException(functionName+"function not find in SAP!!");
    	}
    	List<Object[]> list=new ArrayList<Object[]>();
    	JCoTable table=function.getTableParameterList().getTable(tableName);
    	function.getImportParameterList().setValue("PARAMETER", "SELECT");
    	try{
    	  //远程执行;
          function.execute(destination);
        }catch(AbapException e){
            System.out.println(e.toString());
            return null;
        }
    	if(table!=null){
    		//获得内表中的所有记录;
    		for(int i=0;i<table.getNumRows();i++){
    			Object[] obj=new Object[4];
    			table.setRow(i);
    			//获得内表中所有的字段;
    			for(int x=0;x<table.getNumColumns();x++){
    				obj[x]=table.getString(table.getMetaData().getName(x));
    			}
    			list.add(obj);
    		}
    	}
    	return list;
    }
    
    
    /***
     * 整合import,export,table中设置值和取值的方法;
     * @param functionName
     * @throws JCoException 
     */
    public Object[] getBapi_All_Data(String functionName,Map<String,List<Object>> setImportMap,Map<String,Map<String,List<Object>>> setBapiTable) throws JCoException{
    	//通过连接池获得连接;
        this.destination = JCoDestinationManager.getDestination(ABAP_AS_POOLED);

        //获得JcoFunction;
        this.function=destination.getRepository().getFunctionTemplate(functionName).getFunction();
        if(function == null){
    		throw new RuntimeException(functionName+" function module not find in sap!");
    	}
        
        //初始化返回值数组;
    	Object[] objArray =new Object[2];
    	
    	//第一步,设置Import值;
    	this.setBapiImport(setImportMap);
    	
    	//第二步,设置Table的值;
    	this.setBapiTable(setBapiTable);
    	
    	//第三步远程执行;
    	function.execute(destination);
    	
    	//第四步,取出table的值;
    	Map<String,List<Object>> bapiTableMap=this.getBapiTable();
    	objArray[0] = bapiTableMap;
    	
    	//第五步,取出export的值;
    	Map<String,List<Object>> bapiExportMap = this.getBapiExport();
    	objArray[1] = bapiExportMap;
    	
    	return objArray;
    }
    
    
    /***
     * 调用BAPI_PR_CREATE Function Module Data,import;
     * @return
     * @throws JCoException 
     */
    public void setBapiImport(Map<String,List<Object>> map) throws JCoException{
    	JCoListMetaData metaDataList=this.function.getImportParameterList().getListMetaData();
    	if(metaDataList!=null && metaDataList.getFieldCount()>0){
    		//get PRHEADER,PRHEADERX,TESTRUN Name.
	    	for(int i=0;i<metaDataList.getFieldCount();i++){
	    		int type = metaDataList.getType(metaDataList.getName(i));
	    		JCoStructure structure = null;
	    		if(type!=0){
	    			structure = this.function.getImportParameterList().getStructure(i);
	    		}
	    		String FiledName = metaDataList.getName(i);
	    		System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~FiledName~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
	    		if(map!=null && map.size()>0){
		    		for(Iterator<String> iterator=map.keySet().iterator();iterator.hasNext();){
		    			String key = iterator.next();
		    			List<Object> listValue=map.get(key);
		    			if(FiledName.equals(key)){
			    			try {
								if(structure!=null && structure.getFieldCount()>0){
									for(int  x = 0;x<listValue.size();x++){
										System.out.println(structure.getMetaData().getName(x)+","+listValue.get(x));
										structure.setValue(structure.getMetaData().getName(x),listValue.get(x));
									}
								}
								if(type==0){
									System.out.println(FiledName+","+listValue.get(0));
									function.getImportParameterList().setValue(metaDataList.getName(i),listValue.get(0));
								}
							} catch (Exception e) {
								function.getImportParameterList().setValue(metaDataList.getName(i),listValue.get(0));
					    		String str=function.getImportParameterList().getString(metaDataList.getName(i));
					    		System.out.println(metaDataList.getName(i)+":"+str+"\n");
							}
		    			}
		    		}
	    		}
	    		System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~END~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n\n");
	    	}
    	}
    }
    
    /***
     * 调用ZBAPI_PR_CREATE Function Module Data,export;
     * @return 
     * @throws JCoException 
     * @throws JCoException 
     */
    public Map<String,List<Object>> getBapiExport() throws JCoException{
    	Map<String,List<Object>> map =new HashMap<String,List<Object>>();
    	JCoListMetaData listMetaData=this.function.getExportParameterList().getListMetaData();
    	if(listMetaData != null && listMetaData.getFieldCount()>0){
    		for(int i = 0;i<listMetaData.getFieldCount();i++){
    			//获得Export里面Filed的字段的名称;
    			String filedName=listMetaData.getName(i);
    			System.out.println("Export里面的字段名称:"+filedName);
    			JCoStructure structure = null;
    			try {
    				int type=listMetaData.getType(filedName);
    				if(type!=0){
    					structure = this.function.getExportParameterList().getStructure(filedName);
    				}
					
					if(structure!=null && structure.getFieldCount()>0){
						List<Object> list=new ArrayList<Object>();
						for(int x=0;x<structure.getFieldCount();x++){
							
							list.add(structure.getValue(x));
						}
						map.put(filedName, list);
					}
					
					if(type==0){
						List<Object> list=new ArrayList<Object>();
						String export_result=this.function.getExportParameterList().getString(filedName);
						list.add(export_result);
						map.put(filedName, list);
					}
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
					List<Object> list=new ArrayList<Object>();
					String str=this.function.getExportParameterList().getString(filedName);
					list.add(str);
					map.put(filedName, list);
				}
    		}
    	}
    	return map;
    }
    
    /***
     * 调用ZBAPI_PR_CREATE Function Module Data,Table;设置值;
     * @return 
     * @throws JCoException 
     */
    public Map<String,Map<String,List<Object>>> setBapiTable(Map<String,Map<String,List<Object>>> tableMap){
    	JCoListMetaData listMetaData=this.function.getTableParameterList().getListMetaData();
    	if(listMetaData!=null && listMetaData.getFieldCount()>0){
    		//要设置值的 table名称有:PRITEM,PRITEMX,PRACCOUNT,PRACCOUNTX,
    		for(int i = 0;i<listMetaData.getFieldCount();i++){
    			//获得所有tableName;
    			String tableName=listMetaData.getName(i);
    			JCoTable table=this.function.getTableParameterList().getTable(tableName);
    			if(table!=null && table.getFieldCount()>0){
    				//System.out.println("表的名称:"+tableName+"字段总数:"+table.getFieldCount());
    				for(Iterator<String> iterator=tableMap.keySet().iterator();iterator.hasNext();){
    					String key=iterator.next();
    					Map<String,List<Object>> map2=tableMap.get(key);
    					int y = 0;
    					for(Iterator<String> iterator2=map2.keySet().iterator();iterator2.hasNext();){
    						String key2=iterator2.next();
    						List<Object> listObjValue=map2.get(key2);
    						if(key2!=null && !"".equals(key2)){
    							//example:PRITEM1,PRITEM2 -> PRITEM,PRITEM,PRITEMX1,PRITEMX2->PRITEMX,PRITEMX.
    							key2=key2.substring(0, key2.length()-1);
    						}
    						if(listObjValue!=null && listObjValue.size()>0 && tableName.equals(key2)){
    							System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"+tableName+"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
    							table.insertRow(y);
    							for(int x = 0 ;x<listObjValue.size();x++){
    								//set value.
    								table.setValue(table.getMetaData().getName(x),listObjValue.get(x));
    								System.out.println("y:"+y+","+table.getMetaData().getName(x)+":"+listObjValue.get(x));
    							}
        						y +=1;
        						System.out.println("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~"+tableName+"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");
    						}
    					}
    				}
    			}
    		}
    	}
    	return tableMap;
    }
    
    /***
     * 调用ZBAPI_PR_CREATE Function Module Data,export;
     * @return 
     * @throws JCoException 
     */
    public Map<String,List<Object>> getBapiTable(){
    	Map<String,List<Object>> map = new HashMap<String, List<Object>>();
    	JCoListMetaData listMetaData=this.function.getTableParameterList().getListMetaData();
    	if(listMetaData!=null && listMetaData.getFieldCount()>0){
    		//要设置值的 table名称:PRITEM,PRITEMX,PRACCOUNT,PRACCOUNTX,
    		for(int i = 0;i<listMetaData.getFieldCount();i++){
    			//获得所有tableName;
    			String tableName=listMetaData.getName(i);
    			JCoTable table=this.function.getTableParameterList().getTable(tableName);
    			
    			if(table!=null && table.getFieldCount()>0){
    				List<Object> list=new ArrayList<Object>();
    				System.out.println("表的名称:"+tableName+"字段总数:"+table.getFieldCount());
    				
    				if(tableName .equals("RETURN")){
    					for(int x = 0;x<table.getNumRows();x++){
        					table.setRow(x);
        					//table.getMetaData().getName(x)获得字段名;getValue()      //获得值;
        					//table.setValue(table.getMetaData().getName(x),121212);  //设置值;
        					list.add(table.getValue("MESSAGE"));
        				}
    				}
    				map.put(tableName, list);
    			}
    		}
    	}
    	return map;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值