springboot整合activiti5-登录与第一个流程

本文介绍了如何在SpringBoot应用中整合Activiti5,实现用户登录功能,并详细展示了如何创建和配置BPMN流程。用户登录后,能够启动流程、填写表单,然后进行审批操作,整个过程涉及Activiti的身份验证、表单处理和任务查询。流程定义文件中,所有userTask的代理人被设定为admin,从而简化了流程操作。
摘要由CSDN通过智能技术生成

系列文章目录(springboot整合activiti5)

用户登录

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<html lang="en">
<head>
<title>Activiti学习</title>
<style type="text/css">
<!--
body {
	margin-left: 0px;
	margin-top: 0px;
	margin-right: 0px;
	margin-bottom: 0px;
}
.inputsize{ width:120px; height:18px; }
-->
</style>
<link href="<%=basePath%>css/css.css" rel="stylesheet" type="text/css" />
</head>

<body>
<form action="<%=basePath%>login/save" method="post" name="fom" id="fom">
<table width="100%" border="0" cellspacing="0" cellpadding="0">
  <tr>
    <td height="147" background="<%=basePath%>images/top02.gif"><img src="<%=basePath%>images/top03.gif" width="776" height="147" /></td>
  </tr>
</table>
<table width="562" border="0" align="center" cellpadding="0" cellspacing="0" class="right-table03">
  <tr>
    <td width="221"><table width="95%" border="0" cellpadding="0" cellspacing="0" class="login-text01">
      
      <tr>
        <td><table width="100%" border="0" cellpadding="0" cellspacing="0" class="login-text01">
          <tr>
            <td align="center"><img src="<%=basePath%>images/ico13.gif" width="107" height="97" /></td>
          </tr>
          <tr>
            <td height="40" align="center">&nbsp;</td>
          </tr>
          
        </table></td>
        <td><img src="<%=basePath%>images/line01.gif" width="5" height="292" /></td>
      </tr>
    </table></td>
    <td><table width="100%" border="0" cellspacing="0" cellpadding="0">
      <tr>
        <td width="31%" height="35" class="login-text02">用户名&nbsp;<br /></td>
        <td width="69%"><input name="username" type="text" class="inputsize"/></td>
      </tr>
      <tr>
        <td height="35" class="login-text02">密码&nbsp;<br /></td>
        <td><input name="password" type="password"  class="inputsize" /></td>
      </tr>
      
      <tr>
        <td height="35">&nbsp;</td>
        <td><input name="Submit2" type="submit" class="right-button01" value="确认登录" onClick="window.location='index.html'" />
          <input name="Submit232" type="submit" class="right-button02" value="重置" /></td>
      </tr>
    </table></td>
  </tr>
</table>
</form>
</body>
</html>
package com.xquant.platform.test.activiti.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.activiti.engine.IdentityService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping(value = "/login")
public class LoginController {
    
    @Autowired
    private IdentityService identityService;
    
    @RequestMapping(value = "/")
    public String add() {
        return "login";
    }
    
    @RequestMapping(value = "/out")
    public String out(HttpSession session) {
    	session.removeAttribute("userId");
    	return "login";
    }
    
    @RequestMapping(value = "/save",method=RequestMethod.POST)
    public String save(@RequestParam(value="username") String username, @RequestParam(value="password") String password, HttpSession session,HttpServletRequest request) {
    	session.removeAttribute("userId");
    	
    	boolean canPass = identityService.checkPassword(username, password);
    	
    	if (canPass){
    		session.setAttribute("userId", username);
    	}else{
    		return "redirect:/login/";
    	}
    	
    	return "redirect:/form/list";
    }
    
    
}

启动之后,用户可以通过http://localhost:8080/login/进行登录(用户必须在ACT_ID_USER表中已经存在)
在这里插入图片描述

BPMN以及第一个流程

创建一个流程定义文件reimbursement.bpmn。对应文件路径如下图所示
在这里插入图片描述
此处将每个userTask修改了代理人为同一个admin,然后通过admin发起的话,所有的流程都可以由admin来操作了。

<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath" targetNamespace="http://www.zioer.com/reimbursement">
  <process id="reimbursement" name="费用报销" isExecutable="true">
    <documentation>公司费用报销简易流程</documentation>
    <startEvent id="startevent1" name="Start" activiti:initiator="startUserId">
      <extensionElements>
        <activiti:formProperty id="fee" name="费用" type="long" required="true"></activiti:formProperty>
        <activiti:formProperty id="note" name="说明" type="string"></activiti:formProperty>
      </extensionElements>
    </startEvent>
<!--    <userTask id="departmentApprove" name="部门领导审批" activiti:assignee="lee">-->
    <userTask id="departmentApprove" name="部门领导审批" activiti:assignee="admin">
      <extensionElements>
        <activiti:formProperty id="fee" name="费用" type="long" writable="false"></activiti:formProperty>
        <activiti:formProperty id="note" name="说明" type="string" writable="false"></activiti:formProperty>
        <activiti:formProperty id="bmyj" name="部门意见" type="string"></activiti:formProperty>
      </extensionElements>
    </userTask>
    <sequenceFlow id="flow1" sourceRef="startevent1" targetRef="departmentApprove"></sequenceFlow>
<!--    <userTask id="reimburseApprove" name="财务部门审批" activiti:assignee="lobby">-->
    <userTask id="reimburseApprove" name="财务部门审批" activiti:assignee="admin">
      <extensionElements>
        <activiti:formProperty id="fee" name="费用" type="long" writable="false"></activiti:formProperty>
        <activiti:formProperty id="refee" name="核实费用" type="long" required="true"></activiti:formProperty>
        <activiti:formProperty id="note" name="说明" type="string" writable="false"></activiti:formProperty>
        <activiti:formProperty id="bmyj" name="部门意见" type="string" writable="false"></activiti:formProperty>
        <activiti:formProperty id="bzhu" name="备注" type="string"></activiti:formProperty>
      </extensionElements>
    </userTask>
    <sequenceFlow id="flow2" sourceRef="departmentApprove" targetRef="reimburseApprove"></sequenceFlow>
    <userTask id="usertask1" name="申请人确认" activiti:assignee="${startUserId}">
      <extensionElements>
        <activiti:formProperty id="fee" name="费用" type="long" writable="false"></activiti:formProperty>
        <activiti:formProperty id="refee" name="核实费用" type="long" writable="false"></activiti:formProperty>
        <activiti:formProperty id="note" name="说明" type="string" writable="false"></activiti:formProperty>
        <activiti:formProperty id="bmyj" name="部门意见" type="string" writable="false"></activiti:formProperty>
        <activiti:formProperty id="bzhu" name="备注" type="string" writable="false"></activiti:formProperty>
      </extensionElements>
    </userTask>
    <sequenceFlow id="flow3" sourceRef="reimburseApprove" targetRef="usertask1"></sequenceFlow>
    <endEvent id="endevent2" name="End"></endEvent>
    <sequenceFlow id="flow5" sourceRef="usertask1" targetRef="endevent2"></sequenceFlow>
  </process>
  <bpmndi:BPMNDiagram id="BPMNDiagram_reimbursement">
    <bpmndi:BPMNPlane bpmnElement="reimbursement" id="BPMNPlane_reimbursement">
      <bpmndi:BPMNShape bpmnElement="startevent1" id="BPMNShape_startevent1">
        <omgdc:Bounds height="35.0" width="35.0" x="80.0" y="160.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="departmentApprove" id="BPMNShape_departmentApprove">
        <omgdc:Bounds height="55.0" width="105.0" x="160.0" y="150.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="reimburseApprove" id="BPMNShape_reimburseApprove">
        <omgdc:Bounds height="55.0" width="105.0" x="310.0" y="150.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="usertask1" id="BPMNShape_usertask1">
        <omgdc:Bounds height="55.0" width="105.0" x="460.0" y="150.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="endevent2" id="BPMNShape_endevent2">
        <omgdc:Bounds height="35.0" width="35.0" x="610.0" y="160.0"></omgdc:Bounds>
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="flow1" id="BPMNEdge_flow1">
        <omgdi:waypoint x="115.0" y="177.0"></omgdi:waypoint>
        <omgdi:waypoint x="160.0" y="177.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow2" id="BPMNEdge_flow2">
        <omgdi:waypoint x="265.0" y="177.0"></omgdi:waypoint>
        <omgdi:waypoint x="310.0" y="177.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow3" id="BPMNEdge_flow3">
        <omgdi:waypoint x="415.0" y="177.0"></omgdi:waypoint>
        <omgdi:waypoint x="460.0" y="177.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="flow5" id="BPMNEdge_flow5">
        <omgdi:waypoint x="565.0" y="177.0"></omgdi:waypoint>
        <omgdi:waypoint x="610.0" y="177.0"></omgdi:waypoint>
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>
</definitions>

在这里插入图片描述

首先点击新增流程
在这里插入图片描述

    @RequestMapping(value = "/add")
    public String add(Model model, HttpSession session) {
        // 首先是安全认证 如果用户没有登录 则返回登录页面
        if (session.getAttribute("userId") == null) {
            return "redirect:/login/";
        }
        // 启动指定流程 通过latestVersion指定只获取最新版本
        // 因为用户在实际使用中,可能会对同一个流程修改后再进行部署 这样就会存在多个版本
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey("reimbursement").latestVersion().singleResult();
        // 表示启动指定流程的实例 id的格式如reimbursement:1:37508
        StartFormData startFormData = formService.getStartFormData(processDefinition.getId());
        // 获取流程实例的FormProperty 通过activiti:formProperty定义的
        List<FormProperty> formProperties = startFormData.getFormProperties();

        model.addAttribute("list", formProperties);
        model.addAttribute("formData", startFormData);

        return "reimbursement_start";
    }

然后进入到reimbursement_start.jsp当中,这里list当中包含需要填写的参数,而这些参数在前面流程定义中由activiti:formProperty来指定的。
在这里插入图片描述

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<html lang="en">
<head>
    <title>费用报销-新增</title>
    <link rel="stylesheet" rev="stylesheet" href="<%=basePath%>css/style.css" type="text/css" media="all" />
	<script type="text/javascript" src="<%=basePath%>js/My97DatePicker/WdatePicker.js"></script>
	<script language=JavaScript>
		function save(){
		   document.getElementById("form").submit();
		}
	</script>
</head>
<body>
<form action="start/save" method="post" name="form" id="form">
<div class="MainDiv">
<table width="60%" border="0" cellpadding="0" cellspacing="0" class="CContent">
  <tr>
      <th class="tablestyle_title" >费用报销-新增</th>
  </tr>
  <tr>
    <td class="CPanel">
		<table border="0" cellpadding="0" cellspacing="0" style="width:100%">
            <TR>
                <TD width="100%">
                    <fieldset style="height:100%;">
                    <legend>内容填写</legend>
                          <table border="0" cellpadding="2" cellspacing="1" style="width:100%">
                         
                          <c:forEach items="${list}" var="var" varStatus="vs">
		                  <tr>
		                  	<c:if test="${ var.getType().getName() =='string' || var.getType().getName() =='long'}">
		                  	<td nowrap align="right" width="13%">${var.getName()}</td>
		                  	<td><input type='text' id='${var.getId()}' name='${var.getId()}' value='${var.getValue()}' /></td>
						                      
							</c:if>
		                  </tr>
		                  </c:forEach>
                          </table>
                    </fieldset>			
                </TD>
            </TR>
		</TABLE>
	 </td>
  </tr>
  <tr>
    <TD colspan="2" align="center" height="50px">
        <input type="button" name="Submit" value="保存" class="button" onclick="save();"/>    
        <input type="button" name="Submit2" value="返回" class="button" onclick="window.history.go(-1);"/>
    </TD>
  </tr>
</table>
</div>
</form>
</body>
</html>

jsp中会遍历FormProperty列表并进行渲染,渲染之后的结果如下所示
在这里插入图片描述
用户填写,并保存
在这里插入图片描述

/**
 * 提交启动流程
 */
@RequestMapping(value = "/start/save")
public String saveStartForm(Model model, HttpServletRequest request, HttpSession session) {
    String userId = session.getAttribute("userId") == null ? null : session.getAttribute("userId").toString();
    if (userId == null) {
        return "redirect:/login/";
    }
    // 用于获取页面传递的值
    Map formProperties = pageData(request);
    ProcessInstance processInstance = null;
    ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
            .processDefinitionKey("reimbursement").latestVersion().singleResult();
    String processDefinitionId = processDefinition.getId();
    try {
        // 设置流程发起人
        identityService.setAuthenticatedUserId(userId);
        // 启动Activiti内置Form流程
        processInstance = formService.submitStartFormData(processDefinitionId, formProperties);
    } finally {
        identityService.setAuthenticatedUserId(null);
    }
    // 重定向 到列表页面
    return "redirect:/form/list";
}

启动任务,并提交,这里比较关键的是设置提交人以及提交的参数
在这里插入图片描述
然后查询当前用户待处理的任务,由于我们将代理人都设置为了admin。所以可以查出刚才提交的任务

@RequestMapping(value = "/list")
public String list(Model model, HttpSession session) {
    String userId = session.getAttribute("userId") == null ? null : session.getAttribute("userId").toString();
    if (userId == null) {
        return "redirect:/login/";
    }
    List<Task> tasks = new ArrayList<Task>();

    //获得当前用户的待办以及分配的任务
    tasks = taskService.createTaskQuery().taskCandidateOrAssigned(userId).active().orderByTaskId().desc().list();

    model.addAttribute("list", tasks);

    return "reimbursement_list";
}

在这里插入图片描述
将任务通过reimbursement_list来进行渲染

<%@ page language="java" import="java.util.*" contentType="text/html; charset=utf8" %> 
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme() + "://"
			+ request.getServerName() + ":" + request.getServerPort()
			+ path + "/";
%>
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf8" />
<title>Zioer-Activiti示例</title>
<link rel="stylesheet" rev="stylesheet" href="<%=basePath%>css/style.css" type="text/css" media="all" />
<script language=JavaScript>
function link(){
   document.location.href="add";
}
function link2(){
   document.location.href="<%=basePath%>/login/out";
}
</script>
</head>

<body class="ContentBody">
<form action="add" method="post" name="fom" id="fom">
<div class="MainDiv">
<table width="60%" border="0" cellpadding="0" cellspacing="0" class="CContent">
  <tr>
      <th class="tablestyle_title" >Activiti示例-待办工作</th>
  </tr>
  <tr>
    <td class="CPanel">
		<table id="subtree1" style="DISPLAY: " width="100%" border="0" cellspacing="0" cellpadding="0">
        <tr>
          <td><table width="95%" border="0" align="center" cellpadding="0" cellspacing="0">
          	 <tr>
               <td height="20">
               	<span class="newfont07">
	              <input name="Submit1" type="button" class="right-button08" value="新增流程" onclick="link();"/>
	            </span>  
	            <span class="newfont07">
	              <input name="Submit1" type="button" class="right-button08" value="退出" onclick="link2();"/>
	            </span>             
	           </td>
          	 </tr>
              <tr>
                <td height="40" class="font42">
				<table width="100%" border="0" cellpadding="4" cellspacing="1" bgcolor="#FFFFEE" class="newfont03">
				 <tr class="CTitle" >
                    	<td height="22" colspan="5" align="center" style="font-size:16px">当前用户办理工作列表</td>
                  </tr>
                  <tr bgcolor="#EEEEEE">
				    <td width="10%" height="30">任务ID</td>
					<td width="20%">当前节点</td>
                    <td width="20%">办理人</td>
                    <td width="36%">创建时间</td>
					<td width="17%">操作</td>
                  </tr>
                  <c:forEach items="${list}" var="var" varStatus="vs">
                  <tr  <c:if test="${vs.count%2==0}">bgcolor="#AAAABB"</c:if> align="left" >
				    <td >${var.id}</td>
				    <td  height="30">${var.name}</td>
					<td >${var.assignee}</td>                    
					<td ><fmt:formatDate value="${var.createTime}" type="both"/></td>                    
					<td ><a href="<%=basePath%>form/startform/${var.id}">办理</a></td>
                  </tr>
                  </c:forEach>
            </table></td>
        </tr>
      </table>
      </td>
  </tr>
</table>
	 </td>
  </tr>
  
</table>
</div>
</form>
</body>
</html>

在这里插入图片描述
点击任务的操作按钮(办理),会将任务ID传送到后台,获取对应的表格信息

/**
 * 初始化启动流程,读取启动流程的表单字段来渲染start form
 */
@RequestMapping(value = "/startform/{taskId}")
public String StartTaskForm(@PathVariable("taskId") String taskId, Model model, HttpSession session) throws Exception {
    String userId = session.getAttribute("userId") == null ? null : session.getAttribute("userId").toString();
    if (userId == null) {
        return "redirect:/login/";
    }
    // 获取指定任务的taskFormData taskId会随着任务进行而变化
    TaskFormData taskFormData = formService.getTaskFormData(taskId);
    // 然后再获取FormProperty
    List<FormProperty> formProperties = taskFormData.getFormProperties();
    String startUserId = (String) taskService.getVariable(taskId, "startUserId");
    model.addAttribute("formData", taskFormData);
    model.addAttribute("startUserId", startUserId);
    model.addAttribute("list", formProperties);

    return "reimbursement_edit";
}

在这里插入图片描述
接下来再通过jsp页面(reimbursement_edit)渲染

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt"%>
<%
	String path = request.getContextPath();
	String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<html lang="en">
<head>
    <title>费用报销-审批</title>
    <link rel="stylesheet" rev="stylesheet" href="<%=basePath%>css/style.css" type="text/css" media="all" />
	<script type="text/javascript" src="<%=basePath%>js/My97DatePicker/WdatePicker.js"></script>
	<script language=JavaScript>
		function save(){
		   document.getElementById("form").submit();
		}
	</script>
</head>
<body>
<form action="save/${ formData.getTask().getId()}" method="post" name="form" id="form">
<div class="MainDiv">
<table width="60%" border="0" cellpadding="0" cellspacing="0" class="CContent">
  <tr>
      <th class="tablestyle_title" >费用报销-审批</th>
  </tr>
  <tr>
    <td class="CPanel">
		<table border="0" cellpadding="0" cellspacing="0" style="width:100%">
            <TR>
                <TD width="100%">
                    <fieldset style="height:100%;">
                    <legend>内容填写</legend>
                          <table border="0" cellpadding="2" cellspacing="1" style="width:100%">
                          <tr>
                          	<td nowrap align="right" width="13%">申请人</td>
                          	<td>${startUserId}</td>
                          </tr>
                          <c:forEach items="${list}" var="var" varStatus="vs">
		                  <tr>
		                  	<c:if test="${ var.getType().getName() =='string' || var.getType().getName() =='long' || var.getType().getName() ==null}">
		                  		<c:choose>
			                  		<c:when test="${ var.isWritable()}">  
			                  			<td nowrap align="right" width="13%">${var.getName()}</td>
			                  			<td><input type='text' id='${var.getId()}' name='${var.getId()}' value='${var.getValue()}' /></td>
			                  		</c:when>
			                  		<c:otherwise>
			                  			<td nowrap align="right" width="13%">${var.getName()}</td>
			                  			<td>${var.getValue()}</td>
			                  		</c:otherwise>
			                  	</c:choose>
							</c:if>
		                  </tr>
		                  </c:forEach>
                          </table>
                    </fieldset>			
                </TD>
            </TR>
		</TABLE>
	 </td>
  </tr>
  <tr>
    <TD colspan="2" align="center" height="50px">
        <input type="button" name="Submit" value="保存" class="button" onclick="save();"/>     
        <input type="button" name="Submit2" value="返回" class="button" onclick="window.history.go(-1);"/>
    </TD>
  </tr>
</table>
</div>
</form>
</body>
</html>

注意区分可写与不可写,这些都是在流程定义时定义好的
在这里插入图片描述
在这里插入图片描述
输入部门意见之后保存,又会进入任务提交
在这里插入图片描述
然后又是列表需要处理的任务
在这里插入图片描述
接下来又是财务审批,点击办理,将任务ID(42548)传递到后台查询表格参数并渲染
在这里插入图片描述
在这里插入图片描述
填写参数完成之后,再保存,又会进入到任务完成流程(当前任务Id=42548),
在这里插入图片描述
审批完成之后又会进入到当前用户待处理流程列表,可以看到任务ID已经由42548变为了42554了。
在这里插入图片描述
继续点击办理,然后提交,查询任务列表…,直到流程结束。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lang20150928

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值