activity结合项目应用和表结构

/**更新请假状态,启动流程实例,让启动的流程实例关联业务*/
	@Override
	public void saveStartProcess(WorkflowBean workflowBean) {
		//1:获取请假单ID,使用请假单ID,查询请假单的对象LeaveBill
		Long id = workflowBean.getId();
		LeaveBill leaveBill = leaveBillDao.findLeaveBillById(id);
		//2:更新请假单的请假状态从0变成1(初始录入-->审核中)
		leaveBill.setState(1);
		//3:使用当前对象获取到流程定义的key(对象的名称就是流程定义的key)
		String key = leaveBill.getClass().getSimpleName();
		/**
		 * 4:从Session中获取当前任务的办理人,使用流程变量设置下一个任务的办理人
			    * inputUser是流程变量的名称,
			    * 获取的办理人是流程变量的值
		 */
		Map<String, Object> variables = new HashMap<String,Object>();
		variables.put("inputUser", SessionContext.get().getName());//表示惟一用户
		/**
		 * 5:	(1)使用流程变量设置字符串(格式:LeaveBill.id的形式),通过设置,让启动的流程(流程实例)关联业务
   				(2)使用正在执行对象表中的一个字段BUSINESS_KEY(Activiti提供的一个字段),让启动的流程(流程实例)关联业务
		 */
		//格式:LeaveBill.id的形式(使用流程变量)
		String objId = key+"."+id;
		variables.put("objId", objId);
		//6:使用流程定义的key,启动流程实例,同时设置流程变量,同时向正在执行的执行对象表中的字段BUSINESS_KEY添加业务数据,同时让流程关联业务
		runtimeService.startProcessInstanceByKey(key,objId,variables);
		
	}
	
	/**2:使用当前用户名查询正在执行的任务表,获取当前任务的集合List<Task>*/
	@Override
	public List<Task> findTaskListByName(String name) {
		List<Task> list = taskService.createTaskQuery()//
					.taskAssignee(name)//指定个人任务查询
					.orderByTaskCreateTime().asc()//
					.list();
		return list;
	}
	
	/**使用任务ID,获取当前任务节点中对应的Form key中的连接的值*/
	@Override
	public String findTaskFormKeyByTaskId(String taskId) {
		TaskFormData formData = formService.getTaskFormData(taskId);
		//获取Form key的值
		String url = formData.getFormKey();
		return url;
	}
	
	/**一:使用任务ID,查找请假单ID,从而获取请假单信息*/
	@Override
	public LeaveBill findLeaveBillByTaskId(String taskId) {
		//1:使用任务ID,查询任务对象Task
		Task task = taskService.createTaskQuery()//
						.taskId(taskId)//使用任务ID查询
						.singleResult();
		//2:使用任务对象Task获取流程实例ID
		String processInstanceId = task.getProcessInstanceId();
		//3:使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
		ProcessInstance pi = runtimeService.createProcessInstanceQuery()//
						.processInstanceId(processInstanceId)//使用流程实例ID查询
						.singleResult();
		//4:使用流程实例对象获取BUSINESS_KEY
		String buniness_key = pi.getBusinessKey();
		//5:获取BUSINESS_KEY对应的主键ID,使用主键ID,查询请假单对象(LeaveBill.1)
		String id = "";
		if(StringUtils.isNotBlank(buniness_key)){
			//截取字符串,取buniness_key小数点的第2个值
			id = buniness_key.split("\\.")[1];
		}
		//查询请假单对象
		//使用hql语句:from LeaveBill o where o.id=1
		LeaveBill leaveBill = leaveBillDao.findLeaveBillById(Long.parseLong(id));
		return leaveBill;
	}
	
	/**二:已知任务ID,查询ProcessDefinitionEntiy对象,从而获取当前任务完成之后的连线名称,并放置到List<String>集合中*/
	@Override
	public List<String> findOutComeListByTaskId(String taskId) {
		//返回存放连线的名称集合
		List<String> list = new ArrayList<String>();
		//1:使用任务ID,查询任务对象
		Task task = taskService.createTaskQuery()//
					.taskId(taskId)//使用任务ID查询
					.singleResult();
		//2:获取流程定义ID
		String processDefinitionId = task.getProcessDefinitionId();
		//3:查询ProcessDefinitionEntiy对象
		ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity) repositoryService.getProcessDefinition(processDefinitionId);
		//使用任务对象Task获取流程实例ID
		String processInstanceId = task.getProcessInstanceId();
		//使用流程实例ID,查询正在执行的执行对象表,返回流程实例对象
		ProcessInstance pi = runtimeService.createProcessInstanceQuery()//
					.processInstanceId(processInstanceId)//使用流程实例ID查询
					.singleResult();
		//获取当前活动的id
		String activityId = pi.getActivityId();
		//4:获取当前的活动
		ActivityImpl activityImpl = processDefinitionEntity.findActivity(activityId);
		//5:获取当前活动完成之后连线的名称
		List<PvmTransition> pvmList = activityImpl.getOutgoingTransitions();
		if(pvmList!=null && pvmList.size()>0){
			for(PvmTransition pvm:pvmList){
				String name = (String) pvm.getProperty("name");
				if(StringUtils.isNotBlank(name)){
					list.add(name);
				}
				else{
					list.add("默认提交");
				}
			}
		}
		return list;
	}
	
	/**指定连线的名称完成任务*/
	@Override
	public void saveSubmitTask(WorkflowBean workflowBean) {
		//获取任务ID
		String taskId = workflowBean.getTaskId();
		//获取连线的名称
		String outcome = workflowBean.getOutcome();
		//批注信息
		String message = workflowBean.getComment();
		//获取请假单ID
		Long id = workflowBean.getId();
		
		/**
		 * 1:在完成之前,添加一个批注信息,向act_hi_comment表中添加数据,用于记录对当前申请人的一些审核信息
		 */
		//使用任务ID,查询任务对象,获取流程流程实例ID
		Task task = taskService.createTaskQuery()//
						.taskId(taskId)//使用任务ID查询
						.singleResult();
		//获取流程实例ID
		String processInstanceId = task.getProcessInstanceId();
		/**
		 * 注意:添加批注的时候,由于Activiti底层代码是使用:
		 * 		String userId = Authentication.getAuthenticatedUserId();
			    CommentEntity comment = new CommentEntity();
			    comment.setUserId(userId);
			  所有需要从Session中获取当前登录人,作为该任务的办理人(审核人),对应act_hi_comment表中的User_ID的字段,不过不添加审核人,该字段为null
			 所以要求,添加配置执行使用Authentication.setAuthenticatedUserId();添加当前任务的审核人
		 * */
		Authentication.setAuthenticatedUserId(SessionContext.get().getName());
		taskService.addComment(taskId, processInstanceId, message);
		/**
		 * 2:如果连线的名称是“默认提交”,那么就不需要设置,如果不是,就需要设置流程变量
		 * 在完成任务之前,设置流程变量,按照连线的名称,去完成任务
				 流程变量的名称:outcome
				 流程变量的值:连线的名称
		 */
		Map<String, Object> variables = new HashMap<String,Object>();
		if(outcome!=null && !outcome.equals("默认提交")){
			variables.put("outcome", outcome);
		}

		//3:使用任务ID,完成当前人的个人任务,同时流程变量
		taskService.complete(taskId, variables);
		//4:当任务完成之后,需要指定下一个任务的办理人(使用类)-----已经开发完成
		
		/**
		 * 5:在完成任务之后,判断流程是否结束
   			如果流程结束了,更新请假单表的状态从1变成2(审核中-->审核完成)
		 */
		ProcessInstance pi = runtimeService.createProcessInstanceQuery()//
						.processInstanceId(processInstanceId)//使用流程实例ID查询
						.singleResult();
		//流程结束了
		if(pi==null){
			//更新请假单表的状态从1变成2(审核中-->审核完成)
			LeaveBill bill = leaveBillDao.findLeaveBillById(id);
			bill.setState(2);
		}
	}
	
	/**获取批注信息,传递的是当前任务ID,获取历史任务ID对应的批注*/
	@Override
	public List<Comment> findCommentByTaskId(String taskId) {
		List<Comment> list = new ArrayList<Comment>();
		//使用当前的任务ID,查询当前流程对应的历史任务ID
		//使用当前任务ID,获取当前任务对象
		Task task = taskService.createTaskQuery()//
				.taskId(taskId)//使用任务ID查询
				.singleResult();
		//获取流程实例ID
		String processInstanceId = task.getProcessInstanceId();
//		//使用流程实例ID,查询历史任务,获取历史任务对应的每个任务ID
//		List<HistoricTaskInstance> htiList = historyService.createHistoricTaskInstanceQuery()//历史任务表查询
//						.processInstanceId(processInstanceId)//使用流程实例ID查询
//						.list();
//		//遍历集合,获取每个任务ID
//		if(htiList!=null && htiList.size()>0){
//			for(HistoricTaskInstance hti:htiList){
//				//任务ID
//				String htaskId = hti.getId();
//				//获取批注信息
//				List<Comment> taskList = taskService.getTaskComments(htaskId);//对用历史完成后的任务ID
//				list.addAll(taskList);
//			}
//		}
		list = taskService.getProcessInstanceComments(processInstanceId);
		return list;
	}
	
	/**使用请假单ID,查询历史批注信息*/
	@Override
	public List<Comment> findCommentByLeaveBillId(Long id) {
		//使用请假单ID,查询请假单对象
		LeaveBill leaveBill = leaveBillDao.findLeaveBillById(id);
		//获取对象的名称
		String objectName = leaveBill.getClass().getSimpleName();
		//组织流程表中的字段中的值
		String objId = objectName+"."+id;
		
		/**1:使用历史的流程实例查询,返回历史的流程实例对象,获取流程实例ID*/
//		HistoricProcessInstance hpi = historyService.createHistoricProcessInstanceQuery()//对应历史的流程实例表
//						.processInstanceBusinessKey(objId)//使用BusinessKey字段查询
//						.singleResult();
//		//流程实例ID
//		String processInstanceId = hpi.getId();
		/**2:使用历史的流程变量查询,返回历史的流程变量的对象,获取流程实例ID*/
		HistoricVariableInstance hvi = historyService.createHistoricVariableInstanceQuery()//对应历史的流程变量表
						.variableValueEquals("objId", objId)//使用流程变量的名称和流程变量的值查询
						.singleResult();
		//流程实例ID
		String processInstanceId = hvi.getProcessInstanceId();
		List<Comment> list = taskService.getProcessInstanceComments(processInstanceId);
		return list;
	}
	
	/**1:获取任务ID,获取任务对象,使用任务对象获取流程定义ID,查询流程定义对象*/
	@Override
	public ProcessDefinition findProcessDefinitionByTaskId(String taskId) {
		//使用任务ID,查询任务对象
		Task task = taskService.createTaskQuery()//
					.taskId(taskId)//使用任务ID查询
					.singleResult();
		//获取流程定义ID
		String processDefinitionId = task.getProcessDefinitionId();
		//查询流程定义的对象
		ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()//创建流程定义查询对象,对应表act_re_procdef 
					.processDefinitionId(processDefinitionId)//使用流程定义ID查询
					.singleResult();
		return pd;
	}
	
	/**
	 * 二:查看当前活动,获取当期活动对应的坐标x,y,width,height,将4个值存放到Map<String,Object>中
		 map集合的key:表示坐标x,y,width,height
		 map集合的value:表示坐标对应的值
	 */
	@Override
	public Map<String, Object> findCoordingByTask(String taskId) {
		//存放坐标
		Map<String, Object> map = new HashMap<String,Object>();
		//使用任务ID,查询任务对象
		Task task = taskService.createTaskQuery()//
					.taskId(taskId)//使用任务ID查询
					.singleResult();
		//获取流程定义的ID
		String processDefinitionId = task.getProcessDefinitionId();
		//获取流程定义的实体对象(对应.bpmn文件中的数据)
		ProcessDefinitionEntity processDefinitionEntity = (ProcessDefinitionEntity)repositoryService.getProcessDefinition(processDefinitionId);
		//流程实例ID
		String processInstanceId = task.getProcessInstanceId();
		//使用流程实例ID,查询正在执行的执行对象表,获取当前活动对应的流程实例对象
		ProcessInstance pi = runtimeService.createProcessInstanceQuery()//创建流程实例查询
					.processInstanceId(processInstanceId)//使用流程实例ID查询
					.singleResult();
		//获取当前活动的ID
		String activityId = pi.getActivityId();
		//获取当前活动对象
		ActivityImpl activityImpl = processDefinitionEntity.findActivity(activityId);//活动ID
		//获取坐标
		map.put("x", activityImpl.getX());
		map.put("y", activityImpl.getY());
		map.put("width", activityImpl.getWidth());
		map.put("height", activityImpl.getHeight());
		return map;
	}
}

Activiti工作流引擎数据库表结构

数据库表的命名

Acitiviti数据库中表的命名都是以ACT_开头的。第二部分是一个两个字符用例表的标识。此用例大体与服务API是匹配的。

  1. ACT_RE_*:’RE’表示repository。带此前缀的表包含的是静态信息,如,流程定义,流程的资源(图片,规则等)。
  2. ACT_RU_*:’RU’表示runtime。这是运行时的表存储着流程变量,用户任务,变量,职责(job)等运行时的数据。Activiti只存储实例执行期间的运行时数据,当流程实例结束时,将删除这些记录。这就保证了这些运行时的表小且快。
  3. ACT_ID_*:’ID’表示identity。这些表包含标识的信息,如用户,用户组,等等。
  4. ACT_HI_*:’HI’表示history。就是这些表包含着历史的相关数据,如结束的流程实例,变量,任务,等等。
  5. ACT_GE_*:普通数据,各种情况都使用的数据。

数据库表结构图

 

 

 

数据库表结构说明

  1. ACT_GE_PROPERTY:属性数据表。存储这个流程引擎级别的数据。
  1. NAME_:属性名称
  2. VALUE_:属性值
  3. REV_INT:版本号
  1. ACT_GE_BYTEARRAY:用来保存部署文件的大文本数据
  1. ID_:资源文件编号,自增长
  2. REV_INT:版本号
  3. NAME_:资源文件名称
  4. DEPLOYMENT_ID_:来自于父表ACT_RE_DEPLOYMENT的主键
  5. BYTES_:大文本类型,存储文本字节流
  1. ACT_RE_DEPLOYMENT:用来存储部署时需要持久化保存下来的信息
  1. ID_:部署编号,自增长
  2. NAME_:部署包的名称
  3. DEPLOY_TIME_:部署时间
  1. ACT_RE_PROCDEF:业务流程定义数据表
  1. ID_:流程ID,由“流程编号:流程版本号:自增长ID”组成
  2. CATEGORY_:流程命名空间(该编号就是流程文件targetNamespace的属性值)
  3. NAME_:流程名称(该编号就是流程文件process元素的name属性值)
  4. KEY_:流程编号(该编号就是流程文件process元素的id属性值)
  5. VERSION_:流程版本号(由程序控制,新增即为1,修改后依次加1来完成的)
  6. DEPLOYMENT_ID_:部署编号
  7. RESOURCE_NAME_:资源文件名称
  8. DGRM_RESOURCE_NAME_:图片资源文件名称
  9. HAS_START_FROM_KEY_:是否有Start From Key

注:此表和ACT_RE_DEPLOYMENT是多对一的关系,即,一个部署的bar包里可能包含多个流程定义文件,每个流程定义文件都会有一条记录在ACT_REPROCDEF表内,每个流程定义的数据,都会对于ACT_GE_BYTEARRAY表内的一个资源文件和PNG图片文件。和ACT_GE_BYTEARRAY的关联是通过程序用ACT_GE_BYTEARRAY.NAME与ACT_RE_PROCDEF.NAME_完成的,在数据库表结构中没有体现。

  1. ACT_ID_GROUP:用来存储用户组信息。
  1. ID_:用户组名*
  2. REV_INT:版本号
  3. NAME_:用户组描述信息*
  4. TYPE_:用户组类型
  1. ACT_ID_MEMBERSHIP:用来保存用户的分组信息
  1. USER_ID_:用户名
  2. GROUP_ID_:用户组名
  1. ACT_ID_USER:
  1. ID_:用户名
  2. REV_INT:版本号
  3. FIRST_:用户名称
  4. LAST_:用户姓氏
  5. EMAIL_:邮箱
  6. PWD_:密码
  1. ACT_RU_EXECUTION:
  1. ID_:
  2. REV_:版本号
  3. PROC_INST_ID_:流程实例编号
  4. BUSINESS_KEY_:业务编号
  5. PARENT_ID_:找到该执行实例的父级,最终会找到整个流程的执行实例
  6. PROC_DEF_ID_:流程ID
  7. SUPER_EXEC_: 引用的执行模板
  8. ACT_ID_: 节点id
  9. IS_ACTIVE_: 是否访问
  10. IS_CONCURRENT_:
  11. IS_SCOPE_: 
  1. ACT_RU_TASK:运行时任务数据表。
  1. ID_:
  2. REV_:
  3. EXECUTION_ID_: 执行实例的id
  4. PROC_INST_ID_: 流程实例的id
  5. PROC_DEF_ID_:  流程定义的id,对应act_re_procdef 的id_
  6. NAME_:  任务名称,对应 ***task 的name
  7. PARENT_TASK_ID_ : 对应父任务
  8. DESCRIPTION_:
  9. TASK_DEF_KEY_: ***task 的id
  10. OWNER_   : 发起人
  11. ASSIGNEE_: 分配到任务的人
  12. DELEGATION_ :  委托人
  13. PRIORITY_: 紧急程度
  14. CREATE_TIME_: 发起时间
  15. DUE_TIME_:审批时长
  1. ACT_RU_IDENTITYLINK:任务参与者数据表。主要存储当前节点参与者的信息。
  1. ID_:  标识
  2. REV_: 版本
  3. GROUP_ID_: 组织id
  4. TYPE_: 类型
  5. USER_ID_: 用户id
  6. TASK_ID_: 任务id
  1. ACT_RU_VARIABLE:运行时流程变量数据表。
  1. ID_:标识
  2. REV_:版本号
  3. TYPE_:数据类型
  4. NAME_:变量名
  5. EXECUTION_ID_: 执行实例id
  6. PROC_INST_ID_: 流程实例id
  7. TASK_ID_: 任务id
  8. BYTEARRAY_ID_:
  9. DOUBLE_:若数据类型为double ,保存数据在此列
  10. LONG_: 若数据类型为Long保存数据到此列
  11. TEXT_: string 保存到此列
  12. TEXT2_:
  1. ACT_HI_PROCINST:
  1. ID_ : 唯一标识
  2. PROC_INST_ID_ : 流程ID
  3. BUSINESS_KEY_ : 业务编号
  4. PROC_DEF_ID_ : 流程定义id
  5. START_TIME_ :  流程开始时间
  6. ENT__TIME :  结束时间
  7. DURATION_ : 流程经过时间
  8. START_USER_ID_ : 开启流程用户id
  9. START_ACT_ID_ : 开始节点
  10. END_ACT_ID_: 结束节点
  11. SUPER_PROCESS_INSTANCE_ID_ : 父流程流程id
  12. DELETE_REASON_ : 从运行中任务表中删除原因
  1. ACT_HI_ACTINST:
  1. ID_ : 标识
  2. PROC_DEF_ID_ :流程定义id
  3. PROC_INST_ID_ : 流程实例id
  4. EXECUTION_ID_ : 执行实例
  5. ACT_ID_ : 节点id
  6. ACT_NAME_ : 节点名称
  7. ACT_TYPE_ : 节点类型
  8. ASSIGNEE_ : 节点任务分配人
  9. START_TIME_ : 开始时间
  10. END_TIME_ : 结束时间
  11. DURATION : 经过时长
  1. ACT_HI_TASKINST:
  1. ID_ : 标识
  2. PROC_DEF_ID_ : 流程定义id
  3. TASK_DEF_KEY_ : 任务定义id
  4. PROC_INST_ID_ : 流程实例id
  5. EXECUTION_ID_ : 执行实例id
  6. PARENT_TASK_ID_ : 父任务id
  7. NAME_ : 任务名称
  8. DESCRIPTION_ : 说明
  9. OWNER_ : 拥有人(发起人)
  10. ASSIGNEE_ : 分配到任务的人
  11. START__TIME_ : 开始任务时间
  12. END_TIME_ : 结束任务时间
  13. DURATION_ : 时长
  14. DELETE_REASON_ :从运行时任务表中删除的原因
  15. PRIORITY_ : 紧急程度
  16. DUE_DATE_ :
  1. ACT_HI_DETAIL:启动流程或者在任务complete之后,记录历史流程变量
  1. ID_ : 标识
  2. TYPE_ : variableUpdate 和 formProperty 两种值
  3. PROC_INST_ID_ : 对应流程实例id
  4. EXECUTION_ID_ : 对应执行实例id
  5. TASK_ID_ : 对应任务id
  6. ACT_INST_ID : 对应节点id
  7. NAME_ : 历史流程变量名称,或者表单属性的名称
  8. VAR_TYPE_ : 定义类型
  9. REV_ : 版本
  10. TIME_ : 导入时间
  11. BYTEARRAY_ID_
  12. DOUBLE_ : 如果定义的变量或者表单属性的类型为double,他的值存在这里
  13. LONG_ : 如果定义的变量或者表单属性的类型为LONG ,他的值存在这里
  14. TEXT_ :  如果定义的变量或者表单属性的类型为string,值存在这里
  15. TEXT2_:
  1. ACT_HI_COMMENT 意见表
  1. ID_ :标识
  2. TYPE_ : 意见记录类型 为comment 时 为处理意见
  3. TIME_ : 记录时间
  4. USER_ID_ :
  5. TASK_ID_ : 对应任务的id
  6. PROC_INST_ID_ : 对应的流程实例的id
  7. ACTION_ : 为AddComment 时为处理意见
  8. MESSAGE_ : 处理意见
  9. FULL_MSG_ :

结论及总结

  1. 流程文件部署主要涉及到3个表,分别是:ACT_GE_BYTEARRAY、ACT_RE_DEPLOYMENT、ACT_RE_PROCDEF。主要完成“部署包”-->“流程定义文件”-->“所有包内文件”的解析部署关系。从表结构中可以看出,流程定义的元素需要每次从数据库加载并解析,因为流程定义的元素没有转化成数据库表来完成,当然流程元素解析后是放在缓存中的,具体的还需要后面详细研究。
  2. 流程定义中的java类文件不保存在数据库里 。
  3. 组织机构的管理相对较弱,如果要纳入单点登录体系内还需要改造完成,具体改造方法有待研究。
  4. 运行时对象的执行与数据库记录之间的关系需要继续研究
  5. 历史数据的保存及作用需要继续研究。

 

 

 

Activiti使用Mybatis3做持久化工作,可以在配置中设置流程引擎启动时创建表。

Activiti使用到的表都是ACT_开头的。

ACT_RE_*:流程定义存储。

ACT_RU_*:流程执行记录,记录流程启动到结束的所有动作,流程结束后会清除相关记录。

ACT_ID_*:用户记录,流程中使用到的用户和组。

ACT_HI_*:流程执行的历史记录。

ACT_GE_*:通用数据及设置。

使用到的表:

ACT_GE_BYTEARRAY:流程部署的数据。

ACT_GE_PROPERTY:通用设置。

ACT_HI_ACTINST:流程活动的实例。

ACT_HI_ATTACHMENT:

ACT_HI_COMMENT:

ACT_HI_DETAIL:

ACT_HI_PROCINST:流程实例。

ACT_HI_TASKINST:任务实例。

ACT_ID_GROUP:用户组。

ACT_ID_INFO:

ACT_ID_MEMBERSHIP:

ACT_ID_USER:用户。

ACT_RE_DEPLOYMENT:部署记录。

ACT_RE_PROCDEF:流程定义。

ACT_RU_EXECUTION:流程执行记录。

ACT_RU_IDENTITYLINK:

ACT_RU_JOB:

ACT_RU_TASK:执行的任务记录。

ACT_RU_VARIABLE:执行中的变量记录。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值