AX 2009 报表替换Excel显示

本文探讨了报表系统中遇到的问题,即显示的数值过长导致使用##替代,打印困难,以及定宽定高的不便。通过引入Excel模板文件,实现了数据填充与自由调整,解决了客户使用的痛点。文章详细介绍了开发过程中的关键步骤,包括报表类的调用、参数接收以及Excel数据填充逻辑,旨在提升报表系统的灵活性和用户体验。

报表替换Excel显示

报表还是不够灵活,不够好用。特别是当项目内容过多,显示的数值过长时候都会用##替代,而打印不出来。

而且定宽,定高,不方便调整。我们开发的辛苦,客户用的也不舒服。

双方都不爽,最后弄了个Excel,算是解决了这个问题。

先要弄个Excel模板文件,然后把数据内容逐个填充到单元格即可,客户爱拖爱调,那就是他的事了。

续上篇,调用接收参数报表类

public   class  ReportRun extends ObjectRun
{
    
int                          g_Year;
    PurchYearCollectTable       g_PurchYCTable;
    Array                       g_Arr;
    SysExcelApplication         excel;
    SysExcelWorkbooks           books;
    SysExcelWorkbook            book;
    SysExcelWorksheets          sheets;
    SysExcelWorksheet           sheet;
    SysExcelCells               cells;
    SysExcelCell                cell;
    SysExcelRange               columns;
    SysExcelRange               column;
    COM                         range,row;
    FileName                    fileName;
    
int                          rowNum;
    real                        pricesCount;

    
private   void  excelHead()
    {
      container       m_Title;
      ;
      cell 
=  cells.item( 1 , 5 );

      m_Title 
=   this .read(cell);

      cell.value(strfmt(conpeek (m_Title,
1 ),g_Year));
    }

    
private   void  excelTable()
    {
      PurchYearCollect                m_PurchYearC;
    
int                                  i,arrLeng,rowN,rowR;
    ;

    rowN 
=   4 ;

    arrLeng 
=  g_Arr.lastIndex()  +   1 ;

    
for (i  =   1 ;i < arrLeng;i ++ )
    {
        m_PurchYearC 
=  g_Arr.value(i);

        
if (rowN  >   5 )
        {
            range 
=  sheet.range(strfmt( " A%1:N%1 " ,rowN - 1 )).comObject();
            row 
=  range.EntireRow();
            row.copy();
            row.insert();
        }

        cell 
=  cells.item(rowN, 1 );
        cell.value(m_PurchYearC.getItemName());

        cell 
=  cells.item(rowN, 2 );
        cell.value(m_PurchYearC.getPrice(
1 ));

        cell 
=  cells.item(rowN, 3 );
        cell.value(m_PurchYearC.getPrice(
2 ));

        cell 
=  cells.item(rowN, 4 );
        cell.value(m_PurchYearC.getPrice(
3 ));

        cell 
=  cells.item(rowN, 5 );
        cell.value(m_PurchYearC.getPrice(
4 ));

        cell 
=  cells.item(rowN, 6 );
        cell.value(m_PurchYearC.getPrice(
5 ));

        cell 
=  cells.item(rowN, 7 );
        cell.value(m_PurchYearC.getPrice(
6 ));

        cell 
=  cells.item(rowN, 8 );
        cell.value(m_PurchYearC.getPrice(
7 ));

        cell 
=  cells.item(rowN, 9 );
        cell.value(m_PurchYearC.getPrice(
8 ));

        cell 
=  cells.item(rowN, 10 );
        cell.value(m_PurchYearC.getPrice(
9 ));

        cell 
=  cells.item(rowN, 11 );
        cell.value(m_PurchYearC.getPrice(
10 ));

        cell 
=  cells.item(rowN, 12 );
        cell.value(m_PurchYearC.getPrice(
11 ));

        cell 
=  cells.item(rowN, 13 );
        cell.value(m_PurchYearC.getPrice(
12 ));

        rowN
++ ;
    }

    rowN 
+=   3 ;
    rowR 
=  rowN  +   1 ;

    
for (i  =   1 ;i < arrLeng;i ++ )
    {
        m_PurchYearC 
=  g_Arr.value(i);

        
if (rowN  >  rowR)
        {
            range 
=  sheet.range(strfmt( " A%1:M%1 " ,rowN - 1 )).comObject();
            row 
=  range.EntireRow();
            row.copy();
            row.insert();
        }

        cell 
=  cells.item(rowN, 1 );
        cell.value(m_PurchYearC.getItemName());

        cell 
=  cells.item(rowN, 2 );
        cell.value(m_PurchYearC.getRate(
1 ));

        cell 
=  cells.item(rowN, 3 );
        cell.value(m_PurchYearC.getRate(
2 ));

        cell 
=  cells.item(rowN, 4 );
        cell.value(m_PurchYearC.getRate(
3 ));

        cell 
=  cells.item(rowN, 5 );
        cell.value(m_PurchYearC.getRate(
4 ));

        cell 
=  cells.item(rowN, 6 );
        cell.value(m_PurchYearC.getRate(
5 ));

        cell 
=  cells.item(rowN, 7 );
        cell.value(m_PurchYearC.getRate(
6 ));

        cell 
=  cells.item(rowN, 8 );
        cell.value(m_PurchYearC.getRate(
7 ));

        cell 
=  cells.item(rowN, 9 );
        cell.value(m_PurchYearC.getRate(
8 ));

        cell 
=  cells.item(rowN, 10 );
        cell.value(m_PurchYearC.getRate(
9 ));

        cell 
=  cells.item(rowN, 11 );
        cell.value(m_PurchYearC.getRate(
10 ));

        cell 
=  cells.item(rowN, 12 );
        cell.value(m_PurchYearC.getRate(
11 ));

        cell 
=  cells.item(rowN, 13 );
        cell.value(m_PurchYearC.getRate(
12 ));

        rowN
++ ;
    }

    excel.visible(
true );
    }

    Container read(SysExcelCell sysExcelCell)
    {
    container       line;
    
int              intvalue;
    real            realvalue;
    ;
    
switch  (sysExcelCell.value().variantType())
    {
        
case  COMVariantType::VT_EMPTY:
            line 
+=   0 ;
            
break ;

        
case  COMVariantType::VT_I1:
            line 
+=  sysExcelCell.value(). char ();
            
break ;

        
case  COMVariantType::VT_I2:
            line 
+=  sysExcelCell.value(). short ();
            
break ;

        
case  COMVariantType::VT_I4:
            intValue 
=  sysExcelCell.value(). int ();
            
if  (intValue  ==   0 )
            {
                intValue 
=  sysExcelCell.value(). long ();
            }
            line 
+=  intValue;
            
break ;

        
case  COMVariantType::VT_UI1:
            line 
+=  sysExcelCell.value(). byte ();
            
break ;

        
case  COMVariantType::VT_UI2:
            line 
+=  sysExcelCell.value().uShort();
            
break ;

        
case  COMVariantType::VT_UI4:
            intValue 
=  sysExceLCell.value().uInt();
            
if  (intValue  ==   0 )
            {
                intValue 
=  sysExcelCell.value().uLong();
            }
            line 
+=  intValue;
            
break ;

        
case  COMVariantType::VT_R4 :
            realValue 
=  sysExcelCell.value(). float ();
            line 
+=  realValue;
            
break ;

        
case  COMVariantType::VT_R8 :
            realValue 
=  sysExcelCell.value(). double ();
            line 
+=  realValue;
            
break ;

        
case  COMVariantType::VT_DECIMAL :
            realValue 
=  sysExcelCell.value(). decimal ();
            line 
+=  realValue;
            
break ;

        
case  COMVariantType::VT_BSTR :
            line 
+=  SysExcelCell.value().bstr();
            
break ;
        
default :
            
throw  error(strfmt( " @SYS26908 " , sysExcelCell.value().variantType()));
    }

    
return   line;
    }

    
private   void  excelImport()
    {
    FilenameOpen                m_file;
    VendParameters              m_parameter;
    ;

    select firstonly m_parameter;
    m_file 
=  m_parameter.PurchYearPrintModel;

    excel 
=  SysExcelApplication::construct();

    books 
=  excel.workbooks();

    
if ( ! books.open(m_file))
        
return ;

    
if ( ! books.count())
    {
        info(
" no content! " );
        
return ;
    }

    book 
=  books.item( 1 );
    sheets 
=  book.worksheets();
    sheet 
=  sheets.itemFromNum( 1 );
    cells 
=  sheet.cells();

    }

    
private   void  PriceRedution()
    {
    PurchLineView           m_PurchLineView;
    utcDateTime                 m_BeginTime,m_EndTime,m_BeginYear;
    Date                        m_BeginDate,m_EndDate;
    
int                          i,r;
    real                        downPrice,total,lastPrice;
    str                         m_ItemName;
    MainTypeTable           m_MainType;
    PurchYearCollect        m_PurchYear;
    PurchLine                   m_PurchLine,t_PurchLine;
    real                        tmpPrice,tmpQty;
    ;

    g_Arr 
=   new  Array(Types::Class);

    r 
=   1 ;

    m_BeginYear 
=  DateTimeUtil::newDateTime(mkdate( 1 , 1 ,g_Year), 0 );

    
while  select m_MainType
    {
        m_PurchYear 
=   new  PurchYearCollect();

        m_PurchYear.setItemName(m_MainType.Name);

        m_BeginDate 
=  mkdate( 1 , 1 ,g_Year);
        m_EndDate 
=  mkdate( 31 , 1 ,g_Year);

        
for (i  =   1 ;i < 13 ;i ++ )
        {
            m_BeginTime 
=  DateTimeUtil::newDateTime(m_BeginDate, 0 );
            m_EndTime 
=  DateTimeUtil::newDateTime(m_EndDate, 3600 * 24 - 1 );

            total 
=   0 ;
            downPrice 
=   0 ;

            
// while select sum(PurchQty) from m_PurchLineView
            
// group by m_PurchLineView.MainTypeId
            
// where m_PurchLineView.createdDateTime1 > m_BeginTime && m_PurchLineView.createdDateTime1 < m_EndTime
            
// && m_PurchLineView.MainTypeId == m_MainType.MainTypeId
            
// {
                 while  select m_PurchLineView
                
where  m_PurchLineView.createdDateTime  >  m_BeginTime  &&  m_PurchLineView.createdDateTime  <  m_EndTime
                
&&  m_PurchLineView.MainTypeId  ==  m_MainType.MainTypeId
                {
                    lastPrice 
=   0 ;

                    
while  select firstonly t_PurchLine order by t_PurchLine.createdDateTime desc  where  t_PurchLine.ItemId  ==  m_PurchLineView.ItemId
                    
&&  t_PurchLine.createdDateTime  <  m_BeginYear
                    {
                        lastPrice 
=  t_PurchLine.PurchPrice;
                    }

                    
if ( ! t_PurchLine)
                    {
                        
while  select firstonly t_PurchLine order by t_PurchLine.createdDateTime asc
                        
where  t_PurchLine.ItemId  ==  m_PurchLineView.ItemId
                        {
                            lastPrice 
=  t_PurchLine.PurchPrice;
                        }
                    }

                    tmpPrice 
=  m_PurchLineView.PurchPrice;
                    tmpQty 
=  m_PurchLineView.PurchQty;

                    downPrice 
+=  (tmpPrice  -  lastPrice)  *  tmpQty;
                    total 
+=  tmpPrice  *  tmpQty;
                }
            
// }

            m_BeginDate 
=  nextmth(m_BeginDate);
            m_EndDate 
=  endmth(nextmth(m_EndDate));

            m_PurchYear.setPrice(i,downPrice);
            m_PurchYear.setTotal(i,total);
        }

        g_Arr.value(r,m_PurchYear);

        
++ r;
     }
    }

    
public  boolean fetch()
    {
    ;
    
this .excelImport();
    
this .excelHead();
    
this .excelTable();

    
throw   "" ;   // 出处抛出空异常,报表最后就不显示

    
return   false ;
    }

    
public   void  init()
    {
     PurYearClass        m_PurYear;
    ;

    super();

    m_PurYear 
=  element.args().caller();

    g_Year 
=  m_PurYear.getYear();

    Title.text(strfmt(
" @SYS1303 " ,g_Year));

      
this .PriceRedution();   // 技术月降价汇总的方法
    }
}

 

 

转载于:https://www.cnblogs.com/Kurodo/archive/2011/08/15/2139750.html

import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns import os from matplotlib.gridspec import GridSpec from matplotlib.ticker import PercentFormatter # -------------------------- 全局配置 -------------------------- plt.rcParams["font.family"] = ["SimHei", "WenQuanYi Micro Hei", "Heiti TC"] # 中文字体 plt.rcParams["axes.unicode_minus"] = False # 正确显示负号 plt. rcParams['font.sans–serif']=['Kaiti'] sns.set_theme(style="whitegrid", palette="deep") # 可视化主题 output_dir = "F:/大数据/第6章/杜邦分析图表" # 输出路径 os.makedirs(output_dir, exist_ok=True) # 自动创建目录 # -------------------------- 日期处理函数 -------------------------- def extract_year(date_value): """从'截止日期'列提取年份(支持多种格式)""" date_str = str(date_value).strip() if "-" in date_str: return int(date_str.split("-")[0]) # 处理2023-12-31 if "年" in date_str: return int(date_str.split("年")[0]) # 处理2023年12月 if len(date_str)>=4 and date_str.isdigit(): return int(date_str[:4]) # 处理20231231 return np.nan # -------------------------- 数据读取与清洗 -------------------------- def load_financial_data(file_path): """加载财务报表并处理日期列(返回带年份列的清洗后DataFrame)""" df = pd.read_excel(file_path, sheet_name=0) # 强制读取第一个工作表 if not isinstance(df, pd.DataFrame): raise TypeError(f"文件{file_path}读取失败,返回类型为{type(df)}") # 查找"截止日期"列(关键列) date_cols = [col for col in df.columns if "截止日期" in col] if not date_cols: raise ValueError(f"文件{file_path}未找到'截止日期'列") # 提取年份并清洗数据 df["年份"] = df[date_cols[0]].apply(extract_year) df = df[df["年份"].notna()].astype({"年份": int}) # 过滤无效年份并转换为整数 return df.sort_values("年份").reset_index(drop=True) # 按年份排序 # -------------------------- 列名动态匹配 -------------------------- def find_column(df, keywords): """动态匹配DataFrame中的列名(支持模糊匹配)""" for col in df.columns: if any(keyword in col for keyword in keywords): return col raise ValueError(f"未找到包含以下关键词的列: {keywords}") # -------------------------- 杜邦分析核心逻辑 -------------------------- def dupont_analysis(balance_sheet_path, income_statement_path): # 加载并清洗数据 bs = load_financial_data(balance_sheet_path) # 资产负债表 is_ = load_financial_data(income_statement_path) # 利润表 # 匹配关键列(支持列名变体) bs_assets = find_column(bs, ["资产总计", "资产合计"]) # 资产总计列 bs_equity = find_column(bs, ["所有者权益", "股东权益", "净资产"]) # 所有者权益列 is_revenue = find_column(is_, ["营业收入", "营业总收入"]) # 营业收入列 is_net_profit = find_column(is_, ["净利润", "归属于母公司", "归母净利润"]) # 净利润列 # 按年份合并数据(仅保留共同年份) merged_df = bs[["年份", bs_assets, bs_equity]].merge( is_[["年份", is_revenue, is_net_profit]], on="年份", how="inner" ) merged_df.columns = ["年份", "资产总计", "所有者权益合计", "营业收入", "净利润"] # 统一列名 # 计算杜邦指标 merged_df["ROE"] = (merged_df["净利润"] / merged_df["所有者权益合计"]) * 100 # 净资产收益率 merged_df["净利润率"] = (merged_df["净利润"] / merged_df["营业收入"]) * 100 # 净利润率 merged_df["平均总资产"] = (merged_df["资产总计"] + merged_df["资产总计"].shift(1)) / 2 # 平均总资产 merged_df["总资产周转率"] = merged_df["营业收入"] / merged_df["平均总资产"].fillna(merged_df["资产总计"]) # 总资产周转率(处理首年) merged_df["权益乘数"] = merged_df["资产总计"] / merged_df["所有者权益合计"] # 权益乘数 merged_df["ROA"] = merged_df["净利润率"] * merged_df["总资产周转率"] # 总资产收益率 return merged_df.dropna() # 过滤缺失值 # -------------------------- 可视化输出(完整标题版) -------------------------- def visualize_duPont(df): # -------------------- 图表1:杜邦核心指标趋势图 -------------------- fig = plt.figure(figsize=(16, 10), constrained_layout=True) fig.suptitle("格力电器杜邦分析核心指标趋势(2018-2023)", fontsize=18, fontweight="bold", y=1.02) # 主标题 gs = GridSpec(2, 2, figure=fig, height_ratios=[2, 1]) # 子图布局(2行2列) # ROE趋势(主指标) ax1 = fig.add_subplot(gs[0, :]) sns.lineplot(data=df, x="年份", y="ROE", marker="o", linewidth=3, color="#E64B35", ax=ax1) ax1.set_title("核心指标:净资产收益率(ROE)", fontsize=16, pad=15) # 子标题 ax1.set_ylabel("ROE(%)", fontsize=14) ax1.yaxis.set_major_formatter(PercentFormatter()) ax1.grid(linestyle="--", alpha=0.6) # 添加数据标签 for x, y in df[["年份", "ROE"]].values: ax1.text(x, y+0.5, f"{y:.1f}%", ha="center", fontsize=12, color="#E64B35") # 净利润率趋势(柱状图) ax2 = fig.add_subplot(gs[1, 0]) sns.barplot(data=df, x="年份", y="净利润率", color="#4DBBD5", ax=ax2) ax2.set_title("盈利能力:净利润率", fontsize=15, pad=12) # 子标题 ax2.set_ylabel("净利润率(%)", fontsize=13) ax2.yaxis.set_major_formatter(PercentFormatter()) # 添加柱状图标签 for p in ax2.patches: height = p.get_height() ax2.text(p.get_x()+p.get_width()/2., height+0.2, f"{height:.1f}%", ha="center", fontsize=11) # 总资产周转率趋势(线图) ax3 = fig.add_subplot(gs[1, 1]) sns.lineplot(data=df, x="年份", y="总资产周转率", marker="s", linewidth=2.5, color="#3C5488", ax=ax3) ax3.set_title("运营效率:总资产周转率", fontsize=15, pad=12) # 子标题 ax3.set_ylabel("周转率(次)", fontsize=13) ax3.grid(linestyle="--", alpha=0.6) # 添加趋势阴影 ax3.fill_between(df["年份"], df["总资产周转率"], alpha=0.1, color="#3C5488") plt.savefig(f"{output_dir}/杜邦核心指标趋势图.png", dpi=300, bbox_inches="tight") # 保存时自动调整边界 # -------------------- 图表2:ROE分解瀑布图 -------------------- fig, ax = plt.subplots(figsize=(14, 8)) base_year = df["年份"].min() current_year = df["年份"].max() ax.set_title(f"{base_year}年-{current_year}年ROE驱动因素分解", fontsize=18, fontweight="bold", pad=20) # 主标题 # 计算各因素贡献值 base = df[df["年份"] == base_year].iloc[0] current = df[df["年份"] == current_year].iloc[0] factors = [ ("基年ROE", base["ROE"], "#3C5488"), ("净利润率贡献", base["ROE"]*(current["净利润率"]/base["净利润率"]-1), "#4DBBD5"), ("总资产周转率贡献", base["ROE"]*(current["净利润率"]/base["净利润率"])*(current["总资产周转率"]/base["总资产周转率"]-1), "#00A087"), ("权益乘数贡献", base["ROE"]*(current["净利润率"]/base["净利润率"])*(current["总资产周转率"]/base["总资产周转率"])*(current["权益乘数"]/base["权益乘数"]-1), "#F39B7F"), ("当前年ROE", current["ROE"], "#E64B35"), ] # 绘制瀑布图 y_pos = np.arange(len(factors)) bars = ax.barh(y_pos, [f[1] for f in factors], color=[f[2] for f in factors], edgecolor="black", height=0.6) ax.set_yticks(y_pos) ax.set_yticklabels([f[0] for f in factors], fontsize=12) ax.set_xlabel("ROE贡献值(%)", fontsize=14) ax.xaxis.set_major_formatter(PercentFormatter()) # 添加数值标签 for bar, (name, value, _) in zip(bars, factors): ax.text(bar.get_width()+0.5 if value>0 else bar.get_width()-1.5, bar.get_y()+bar.get_height()/2, f"{value:.1f}%", va="center", fontsize=11) plt.savefig(f"{output_dir}/ROE驱动因素分解图.png", dpi=300, bbox_inches="tight") # -------------------- 图表3:指标相关性热力图 -------------------- fig, ax = plt.subplots(figsize=(12, 9)) ax.set_title("杜邦分析核心指标相关性矩阵", fontsize=18, fontweight="bold", pad=25) # 主标题 corr = df[["ROE", "净利润率", "总资产周转率", "权益乘数", "ROA"]].corr().round(2) # 绘制热力图 sns.heatmap(corr, annot=True, cmap="coolwarm", vmin=-1, vmax=1, linewidths=0.5, annot_kws={"fontsize": 14}, square=True, cbar_kws={"shrink": 0.8, "label": "相关系数(r)"}) ax.set_xticklabels(ax.get_xticklabels(), rotation=45, ha="right", fontsize=12) ax.set_yticklabels(ax.get_yticklabels(), rotation=0, fontsize=12) plt.savefig(f"{output_dir}/指标相关性热力图.png", dpi=300, bbox_inches="tight") print(f"所有完整图表已保存至:{output_dir}") # -------------------------- 主程序 -------------------------- if __name__ == "__main__": try: # 替换为实际文件路径(确保Excel文件包含"截止日期"列) balance_sheet_path = "F:/大数据/第6章/格力资产负债表.xlsx" income_statement_path = "F:/大数据/第6章/格力利润表.xlsx" # 执行杜邦分析 analysis_df = dupont_analysis(balance_sheet_path, income_statement_path) # 保存分析结果(保留2位小数) analysis_df.round(2).to_excel(f"{output_dir}/格力杜邦分析数据.xlsx", index=False) print(f"分析数据已保存至:{output_dir}/格力杜邦分析数据.xlsx") # 生成完整图表 visualize_duPont(analysis_df) except Exception as e: print(f"程序运行错误: {str(e)}") 运行代码
06-13
标题SpringBoot智能在线预约挂号系统研究AI更换标题第1章引言介绍智能在线预约挂号系统的研究背景、意义、国内外研究现状及论文创新点。1.1研究背景与意义阐述智能在线预约挂号系统对提升医疗服务效率的重要性。1.2国内外研究现状分析国内外智能在线预约挂号系统的研究与应用情况。1.3研究方法及创新点概述本文采用的技术路线、研究方法及主要创新点。第2章相关理论总结智能在线预约挂号系统相关理论,包括系统架构、开发技术等。2.1系统架构设计理论介绍系统架构设计的基本原则和常用方法。2.2SpringBoot开发框架理论阐述SpringBoot框架的特点、优势及其在系统开发中的应用。2.3数据库设计与管理理论介绍数据库设计原则、数据模型及数据库管理系统。2.4网络安全与数据保护理论讨论网络安全威胁、数据保护技术及其在系统中的应用。第3章SpringBoot智能在线预约挂号系统设计详细介绍系统的设计方案,包括功能模块划分、数据库设计等。3.1系统功能模块设计划分系统功能模块,如用户管理、挂号管理、医生排班等。3.2数据库设计与实现设计数据库表结构,确定字段类型、主键及外键关系。3.3用户界面设计设计用户友好的界面,提升用户体验。3.4系统安全设计阐述系统安全策略,包括用户认证、数据加密等。第4章系统实现与测试介绍系统的实现过程,包括编码、测试及优化等。4.1系统编码实现采用SpringBoot框架进行系统编码实现。4.2系统测试方法介绍系统测试的方法、步骤及测试用例设计。4.3系统性能测试与分析对系统进行性能测试,分析测试结果并提出优化建议。4.4系统优化与改进根据测试结果对系统进行优化和改进,提升系统性能。第5章研究结果呈现系统实现后的效果,包括功能实现、性能提升等。5.1系统功能实现效果展示系统各功能模块的实现效果,如挂号成功界面等。5.2系统性能提升效果对比优化前后的系统性能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值