数据层的接口设计

 接口设计:

package org.forever.dao;

import java.util.List;

import org.forever.pagination.PageInfo;


/**
 * 通用dao接口
 * @author 陈均
 *
 */
public interface IGeneralDao {

	/**
	 * 创建实体到数据库
	 * @param entity
	 * @throws Exception
	 */
	public void create(Object entity)throws Exception;
	
	/**
	 * 根据条件删除实体信息(排除in条件的操作)<br/>
	 * 例子:<br/>
	 * List<Condition> conditions = Arrays.asList(
				new Condition("id",new Long(2) ,Operation.EQ)
			);<br/>
		User user = new User();<br/>
		user.setConditions(conditions);<br/>
		删除单个实体:<br/>
		User user = new User(new Long(22));<br/>
		generalDao.delete(user);<br/>
	 */
	public int delete(PageInfo entity)throws Exception;
	
	/**
	 * 删除单个实体信息
	 * 批量删除实体信息,该方法只适用于in操作的<br/>
	 * 考虑性能的原因,所以单独写的一个方法,实现是基于sql实现的,<br/>
	 * 没有单独解析sql字段,实体字段必须和数据库字段名字一样<br/>
	 * 例子:<br/>
	 * 批量删除:<br/>
	 * List<Long> ids = new ArrayList<Long>();<br/>
		for (int i = 47000; i < 147000; i++) {<br/>
			ids.add(new Long(i));<br/>
		}<br/>
		List<Condition> conditions = Arrays.asList(new Condition("id", ids,<br/>
				Operation.IN));<br/>
		User user = new User();<br/>
		user.setConditions(conditions);<br/>
		int count = generalDao.batchDelete(user);<br/>
		删除单个实体:<br/>
		User user = new User(new Long(22));<br/>
		generalDao.batchDelete(user);<br/>
	 */
	public int batchDelete(PageInfo entity)throws Exception;
	
	/**
	 * 批量或者单个更新实体信息(排除in条件的操作)<br/>
	 * 例子:<br/>
	 * 对单个实体更新(可对版本进行控制):<br/>
	 *  User user = new User(new Long(94));<br/>
		user = (User) generalDao.query(user).get(0);<br/>
		Random random = new Random();<br/>
		user.setCname("陈均"+random.nextInt());<br/>
		generalDao.update(user);<br/>
		批量更新(不能控制版本):<br/>
		List<Condition> conditions = Arrays.asList(new Condition("id",<br/>
				new Object[] { new Long(147000), new Long(157000) },<br/>
				Operation.BETWEEN));<br/>
		User user = new User();<br/>
		user.setCname("陈均");<br/>
		user.setConditions(conditions);<br/>
		int count = generalDao.update(user);<br/>
	 * 
	 */
	public int update(PageInfo entity)throws Exception;
	
	/**
	 * 批量更新实体信息,适用于in操作的<br/>
	 * 考虑性能的原因,所以单独写的一个方法,实现是基于sql实现的<br/>
	 * * 没有单独解析sql字段,实体字段必须和数据库字段名字一样<br/>
	 * 这种批量更新不考虑版本控制<br/>
	 * 例子:<br/>
	 * List<Long> ids = new ArrayList<Long>();<br/>
		for (int i = 147000; i < 157000; i++) {<br/>
			ids.add(new Long(i));<br/>
		}<br/>
		List<Condition> conditions = Arrays.asList(new Condition("id", ids,<br/>
				Operation.IN));<br/>
		User user = new User();<br/>
		user.setCname("陈均来了");<br/>
		user.setConditions(conditions);<br/>
		int count = generalDao.batchUpdate(user);<br/>
	 */
	public int batchUpdate(PageInfo entity)throws Exception;
	
	/**
	 * 根据id查询单个对象<br/>
	 * User user = new User(new Long(95));<br/>
	 * user = (User) generalDao.query(user).get(0);<br/>
	 * 
	 */
	public Object queryEntity(PageInfo entity)throws Exception;
	
	/**
	 * * 根据任意条件查询多条信息<br/>
	 * SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");<br/>
		Date startTime = dateFormat.parse("2010-02-01");<br/>
		Date endTime = dateFormat.parse("2010-03-02");<br/>
		List<Condition> conditions = Arrays.asList(<br/>
				new Condition("stuGrade","一年级", Operation.EQ),<br/>
				new Condition("stuAge", 12, Operation.GE),<br/>
				new Condition("stuAge", 19, Operation.LE), <br/>
				new Condition("stuClass","二班", Operation.EQ),<br/>
				new Condition("stuName", "%stor%",Operation.LIKE), <br/>
				new Condition("stuSex", Arrays.asList("男", "女"), Operation.IN),<br/>
				new Condition("stuTime", new Object[] { startTime, endTime },Operation.BETWEEN),<br/> 
				new Condition("stuAge",new Object[] { 14, 18 }, Operation.BETWEEN));<br/>
		List<Order> orders = Arrays.asList(<br/>
				new Order("stuName", OrderType.DESC), <br/>
				new Order("stuAge",OrderType.DESC));<br/>
		Student student = new Student();<br/>
		student.setConditions(conditions);<br/>
		student.setOrders(orders);<br/>
		List<?> list = generalDao.query(student);<br/>
	 */
	public List<?> queryList(PageInfo entity)throws Exception; 
	
	/**
	 * 外置命名查询
	 */
	public Object query(String queryName,Object... params)throws Exception;
	
	
}

 

hibernate实现设计:

package org.forever.sales.dao.hibernateImpl;

import java.lang.reflect.Field;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.forever.dao.IGeneralDao;
import org.forever.pagination.Condition;
import org.forever.pagination.Operation;
import org.forever.pagination.Order;
import org.forever.pagination.OrderType;
import org.forever.pagination.PageInfo;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.stereotype.Repository;

@Repository("generalDao")
public class GeneralDaoImpl implements IGeneralDao {

	private static final String SET = "SET";
	private static final String UPDATE = "UPDATE";
	private static final String EMPTY_STRING = "";
	private static final String CONDITION_SQL = "#condition_sql#";
	private static final String DELETE = "DELETE";
	private static final String RIGTH_BRACKETS = ")";
	private static final String LEFT_BRACKETS = "(";
	private static final String _1_1 = "1=1";
	private static final String WHERE = "WHERE";
	private static final String FROM = "FROM";
	private static final String SPACE = " ";
	private static final String MODE = "mode";
	private static Log log = LogFactory.getLog(GeneralDaoImpl.class);

	public static final String ENTITY_NAME = "#entityName#";
	public static final String CONDITION_HQL = "#condition_hql#";
	public static final String FIELDS = "#fields#";
	public static final String TABLE_NAME = "#tableName#";
	public static final String ORDER_HQL = "#order_hql#";
	public static final String SELECT = "SELECT";
	public static final String COUNT = "COUNT";

	private String hqlCountTemplate;
	private String hqlTemplate;
	private String deleteTemplate;
	private String updateTemplate;
	private static Map<String,String> tabMapCache = new HashMap<String, String>();

	public GeneralDaoImpl() {
		// hqlCountTemplate =
		// "SELECT COUNT(mode) FROM #entityName# mode WHERE 1=1 #condition_hql#";
		StringBuffer sb = new StringBuffer();
		sb.append(SELECT).append(SPACE).append(COUNT).append(LEFT_BRACKETS)
				.append(MODE).append(RIGTH_BRACKETS).append(SPACE).append(FROM)
				.append(SPACE).append(ENTITY_NAME).append(SPACE).append(MODE)
				.append(SPACE).append(WHERE).append(SPACE).append(_1_1).append(
						SPACE).append(CONDITION_HQL);
		hqlCountTemplate = sb.toString();
		sb = new StringBuffer();
		sb.append(SELECT).append(SPACE).append(MODE).append(SPACE).append(FROM)
				.append(SPACE).append(ENTITY_NAME).append(SPACE).append(MODE)
				.append(SPACE).append(WHERE).append(SPACE).append(_1_1).append(
						SPACE).append(CONDITION_HQL);
		// hqlTemplate =
		// "SELECT mode FROM #entityName# mode WHERE 1=1 #condition_hql# #order_hql#";
		hqlTemplate = sb.toString();
		sb = new StringBuffer();
		sb.append(DELETE).append(SPACE).append(FROM).append(SPACE).append(
				TABLE_NAME).append(SPACE).append(WHERE).append(SPACE).append(
				_1_1).append(SPACE).append(CONDITION_SQL);
		// deleteTemplate = "DELETE FROM #tableName# WHERE 1=1 #condition_sql#";
		deleteTemplate = sb.toString();
		// updateTemplate =
		// "UPDATE #entityName# mode SET #fields# WHERE 1=1 #condition_hql# ";
		sb = new StringBuffer();
		sb.append(UPDATE).append(SPACE).append(ENTITY_NAME).append(SPACE)
				.append(MODE).append(SPACE).append(SET).append(SPACE).append(
						FIELDS).append(SPACE).append(WHERE).append(SPACE)
				.append(_1_1).append(SPACE).append(CONDITION_HQL);
		updateTemplate = sb.toString();

	}

	@Resource
	private HibernateTemplate hibernateTemplate;

	public void create(Object entity) throws Exception {
		log.info("create " + entity.getClass());
		hibernateTemplate.persist(entity);
	}

	public int delete(final PageInfo entity) throws Exception {
		return hibernateTemplate.execute(new HibernateCallback<Integer>() {
			public Integer doInHibernate(Session session)
					throws HibernateException, SQLException {
				String deleteTemplate = "DELETE #entityName# mode WHERE 1=1 #condition_hql#";
				List<Object> values = new ArrayList<Object>();
				String condition_hql = preConditionHQL(entity, values);
				if (EMPTY_STRING.equals(condition_hql)) {
					session.delete(entity);
					return 1;
				}
				String delete = deleteTemplate.replaceAll(CONDITION_HQL,
						condition_hql).replaceAll(ENTITY_NAME,
						entity.getClass().getSimpleName());
				Query query = session.createQuery(delete);
				setQueryParameter(query, values);
				return query.executeUpdate();
			}
		});
	}

	// 面向单个对象的分页写法,对于连表分页不适用
	public List<?> query(final PageInfo entity) throws Exception {
		return hibernateTemplate.executeFind(new HibernateCallback<List<?>>() {
			public List<?> doInHibernate(Session session)
					throws HibernateException, SQLException {
				return executeHQL(entity, session);
				// return executeQBC(entity, session);
			}
		});
	}

	public int update(final PageInfo entity) throws Exception {
		return hibernateTemplate.execute(new HibernateCallback<Integer>() {
			public Integer doInHibernate(Session session)
					throws HibernateException, SQLException {

				List<Object> values = new ArrayList<Object>();
				String fields = preField(entity, values);
				String condition_hql = preConditionHQL(entity, values);
				if (EMPTY_STRING.equals(condition_hql)) {
					hibernateTemplate.merge(entity);
					return 1;
				}
				String update = updateTemplate.replaceAll(CONDITION_HQL,
						condition_hql).replaceAll(FIELDS, fields).replaceAll(
						ENTITY_NAME, entity.getClass().getSimpleName());
				System.out.println("update = " + update);
				Query query = session.createQuery(update);
				setQueryParameter(query, values);
				return query.executeUpdate();
			}
		});
	}

	public String getTableName(Object entity){
		String className = entity.getClass().getName();
		SAXReader reader = new SAXReader();
		String tableName = tabMapCache.get(className);
		if(tableName == null){
			String entityConfiXml = className.replaceAll("\\.", "/")+".hbm.xml";
			Document doc;
			try {
				doc = reader.read(GeneralDaoImpl.class.getClassLoader().getResourceAsStream(entityConfiXml));
				List<?> list = doc.selectNodes("/hibernate-mapping/class[@name='"+className+"']");
				if(list.size()==1){
					Element element = (Element) list.get(0);
					tableName = element.attributeValue("table");
					tabMapCache.put(className, tableName);
				}else{
					throw new RuntimeException("没有找到类" + className + "对应的配置文件xml");
				}
			} catch (DocumentException e) {
				throw new RuntimeException(e);
			}
		}
		return tableName;
	}
	
	public int batchDelete(final PageInfo entity) throws Exception {
		return hibernateTemplate.execute(new HibernateCallback<Integer>() {
			public Integer doInHibernate(Session session)
					throws HibernateException, SQLException {
				String tableName = getTableName(entity);
				List<Object> values = new ArrayList<Object>();
				String condition_sql = preConditionSQL(entity, values);
				if (EMPTY_STRING.equals(condition_sql)) {
					session.delete(entity);
					return 1;
				}
				String delete = deleteTemplate.replaceAll(CONDITION_SQL,
						condition_sql).replaceAll(TABLE_NAME, tableName);
				log.info("delete = " + delete);
				Query query = session.createSQLQuery(delete);
				setQueryParameter(query, values);
				return query.executeUpdate();
			}
		});
	}

	public int batchUpdate(final PageInfo entity) throws Exception {
		return hibernateTemplate.execute(new HibernateCallback<Integer>() {

			public Integer doInHibernate(Session session)
					throws HibernateException, SQLException {
				String updateTemplate = new String(
						"UPDATE #tableName# SET #fields# WHERE 1=1 #condition_sql#");
				List<Object> values = new ArrayList<Object>();
				String tableName = getTableName(entity);
				String fields = preField(entity, values);
				String condition_sql = preConditionSQL(entity, values);
				if (EMPTY_STRING.equals(condition_sql)) {
					hibernateTemplate.update(entity);
					return 1;
				}
				String update = updateTemplate.replaceAll(CONDITION_HQL,
						condition_sql).replaceAll(FIELDS, fields).replaceAll(
						TABLE_NAME, tableName);
				System.out.println("update = " + update);
				Query query = session.createSQLQuery(update);
				setQueryParameter(query, values);
				return query.executeUpdate();
			}
		});
	}

	/**
	 * 处理字段语句hql,用于更新用
	 */
	public String preField(PageInfo entity, List<Object> values) {
		StringBuffer fields = new StringBuffer();
		Class<?> c = entity.getClass();
		for (Field field : c.getDeclaredFields()) {
			String fieldName = field.getName();
			try {
				field.setAccessible(true);
				Object value = field.get(entity);
				if (value != null) {
					fields.append(fieldName + "=?,");
					values.add(value);
				}
			} catch (Exception e) {
				e.printStackTrace();
				throw new RuntimeException(e);
			}
		}
		if (fields.length() > 0) {
			fields.replace(fields.length() - 1, fields.length(), EMPTY_STRING);
		}
		return fields.toString();
	}

	/**
	 * 处理排序解析hql
	 */
	public String preOrder(PageInfo entity) {
		StringBuffer c = new StringBuffer();
		List<Order> orders = entity.getOrders();
		if (orders.size() > 0) {
			c.append(" ORDER BY ");
		}
		for (Order order : orders) {
			String propertyName = order.getPropertyName();
			OrderType orderType = order.getOrderType();
			c.append("mode." + propertyName + orderType + ",");
		}
		if (orders.size() > 0) {
			c.replace(c.length() - 1, c.length(), EMPTY_STRING);
		}
		return c.toString();
	}

	/**
	 * 条件解析SQL该方法作用于批量更新和批量删除使用
	 */
	public String preConditionSQL(PageInfo entity, List<Object> param) {
		StringBuffer c = new StringBuffer();
		for (Condition condition : entity.getConditions()) {
			String propertyName = condition.getPropertyName();
			Object value = condition.getPropertyValue();
			Operation operation = condition.getOperation();
			switch (operation) {
			case LIKE:
				c.append(" AND " + propertyName + operation + "?");
				param.add(value);
				break;
			case BETWEEN:
				Object[] params = (Object[]) value;
				c.append(" AND " + propertyName + operation + "? AND ?");
				param.add(params[0]);
				param.add(params[1]);
				break;
			case IN:
				Collection<?> values = (Collection<?>) value;
				c.append(" AND " + propertyName + operation + LEFT_BRACKETS);
				for (Object object : values) {
					c.append("?,");
					param.add(object);
				}
				c.replace(c.length() - 1, c.length(), EMPTY_STRING);
				c.append(RIGTH_BRACKETS);
				break;
			case EQ:
				c.append(" AND " + propertyName + operation + "?");
				param.add(value);
				break;
			case GE:
				c.append(" AND " + propertyName + operation + "?");
				param.add(value);
				break;
			case GT:
				c.append(" AND " + propertyName + operation + "?");
				param.add(value);
				break;
			case LE:
				c.append(" AND " + propertyName + operation + "?");
				param.add(value);
				break;
			case LT:
				c.append(" AND " + propertyName + operation + "?");
				param.add(value);
				break;
			case NE:
				c.append(" AND " + propertyName + operation + "?");
				param.add(value);
				break;
			}
		}
		return c.toString();
	}

	/**
	 * 条件解析hql
	 */
	public String preConditionHQL(PageInfo entity, List<Object> param) {
		StringBuffer c = new StringBuffer();
		for (Condition condition : entity.getConditions()) {
			String propertyName = condition.getPropertyName();
			Object value = condition.getPropertyValue();
			Operation operation = condition.getOperation();
			switch (operation) {
			case LIKE:
				c.append(" AND " + propertyName + operation + "?");
				param.add(value);
				break;
			case BETWEEN:
				Object[] params = (Object[]) value;
				c.append(" AND " + propertyName + operation + "? AND ?");
				param.add(params[0]);
				param.add(params[1]);
				break;
			case IN:
				c.append(" AND " + propertyName + operation + LEFT_BRACKETS);
				Class<?> clazz = value.getClass();
				if (clazz.isArray()) {
					for (Object object : (Object[]) value) {
						c.append("?,");
						param.add(object);
					}
				} else if (value instanceof Collection<?>) {
					for (Object object : (Collection<?>) value) {
						c.append("?,");
						param.add(object);
					}
				}
				c.replace(c.length() - 1, c.length(), EMPTY_STRING);
				c.append(RIGTH_BRACKETS);
				break;
			case EQ:
				c.append(" AND " + propertyName + operation + "?");
				param.add(value);
				break;
			case GE:
				c.append(" AND " + propertyName + operation + "?");
				param.add(value);
				break;
			case GT:
				c.append(" AND " + propertyName + operation + "?");
				param.add(value);
				break;
			case LE:
				c.append(" AND " + propertyName + operation + "?");
				param.add(value);
				break;
			case LT:
				c.append(" AND " + propertyName + operation + "?");
				param.add(value);
				break;
			case NE:
				c.append(" AND " + propertyName + operation + "?");
				param.add(value);
				break;
			}
		}
		return c.toString();
	}

	public List<?> executeHQL(PageInfo entity, Session session) {
		List<Object> values = new ArrayList<Object>();
		String condition_hql = preConditionHQL(entity, values);
		String order_hql = preOrder(entity);
		String hqlCount = hqlCountTemplate.replaceAll(CONDITION_HQL,
				condition_hql).replaceAll(ENTITY_NAME,
				entity.getClass().getSimpleName());
		String hql = hqlTemplate.replaceAll(CONDITION_HQL, condition_hql)
				.replaceAll(ORDER_HQL, order_hql).replaceAll(ENTITY_NAME,
						entity.getClass().getSimpleName());
		log.info("hqlCount=" + hqlCount);
		log.info("hql=" + hql);
		Query query = session.createQuery(hqlCount);
		setQueryParameter(query, values);
		Object uqResult = query.uniqueResult();
		entity.setTotalItems(Integer.parseInt(uqResult.toString()));
		setTotalPage(entity);
		query = session.createQuery(hql);
		setQueryParameter(query, values);
		return query.setFirstResult(
				(entity.getCurrentPage() - 1) * entity.getPageSize())
				.setMaxResults(entity.getPageSize()).list();
	}

	/**
	 * 设置分页语句参数值
	 */
	public void setQueryParameter(Query query, List<Object> values) {
		for (int i = 0; i < values.size(); i++) {
			query.setParameter(i, values.get(i));
		}
	}

	/**
	 * QBC形式的解析
	 */
	public Object executeQBC(PageInfo entity, Session session) {
		Criteria qbc = session.createCriteria(entity.getClass());
		qbc.setProjection(Projections.rowCount());
		preCondition(qbc, entity.getConditions());
		Object uqResult = qbc.uniqueResult();
		entity.setTotalItems(Integer.parseInt(uqResult.toString()));
		setTotalPage(entity);
		qbc.setProjection(null);
		preOrder(qbc, entity.getOrders());
		return qbc.setFirstResult(
				(entity.getCurrentPage() - 1) * entity.getPageSize())
				.setMaxResults(entity.getPageSize()).list();
	}

	/**
	 * 设置总页数
	 * */
	public void setTotalPage(PageInfo pageInfo) {
		pageInfo
				.setTotalPage(pageInfo.getTotalItems() % pageInfo.getPageSize() == 0 ? pageInfo
						.getTotalItems()
						/ pageInfo.getPageSize()
						: pageInfo.getTotalItems() / pageInfo.getPageSize() + 1);
	}

	/**
	 * 处理排序qbc
	 */
	public void preOrder(Criteria qbc, List<Order> orders) {
		for (Order order : orders) {
			String propertyName = order.getPropertyName();
			switch (order.getOrderType()) {
			case ASC:
				qbc.addOrder(org.hibernate.criterion.Order.asc(propertyName));
				break;
			case DESC:
				qbc.addOrder(org.hibernate.criterion.Order.desc(propertyName));
				break;
			}
		}
	}

	/**
	 * 处理条件qbc
	 */
	public void preCondition(Criteria qbc, List<Condition> conditions) {
		for (Condition condition : conditions) {
			String propertyName = condition.getPropertyName();
			Object value = condition.getPropertyValue();
			switch (condition.getOperation()) {
			case LIKE:
				qbc.add(Restrictions.like(propertyName, value.toString(),
						MatchMode.ANYWHERE));
				break;
			case BETWEEN:
				Object[] params = (Object[]) value;
				qbc.add(Restrictions
						.between(propertyName, params[0], params[1]));
				break;
			case IN:
				Collection<?> values = (Collection<?>) value;
				qbc.add(Restrictions.in(propertyName, values));
				break;
			case EQ:
				qbc.add(Restrictions.eq(propertyName, value));
				break;
			case GE:
				qbc.add(Restrictions.ge(propertyName, value));
				break;
			case GT:
				qbc.add(Restrictions.gt(propertyName, value));
				break;
			case LE:
				qbc.add(Restrictions.le(propertyName, value));
				break;
			case LT:
				qbc.add(Restrictions.lt(propertyName, value));
				break;
			case NE:
				qbc.add(Restrictions.ne(propertyName, value));
				break;
			}
		}
	}

	public Object query(String queryName, Object... values) throws Exception {
		return hibernateTemplate.findByNamedQuery(queryName, values);
	}

	@Override
	public Object queryEntity(PageInfo entity) throws Exception {
		try {
			Class<?> c = entity.getClass();
			Field field = c.getDeclaredField("id");
			field.setAccessible(true);
			Integer id = Integer.parseInt(field.get(entity).toString());
			return hibernateTemplate.get(c, id);
		} catch (Exception e) {
			e.printStackTrace();
			return null;
		}
	}

	@Override
	public List<?> queryList(final PageInfo entity) throws Exception {
		return hibernateTemplate.executeFind(new HibernateCallback<List<?>>() {
			public List<?> doInHibernate(Session session)
					throws HibernateException, SQLException {
				return executeHQL(entity, session);
			}
		});
	}
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值