sql count中加条件

本文介绍了一种在SQL中使用case when语句结合count函数的高级用法,通过判断特定条件来统计不同情况的数量,这是一种不同于常规where和having子句的计数方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一般的,我们会在where, 或者 having中加条件,count中只是某个字段

今天看到另外一种写法,不知道性能怎么样

select 

count(  case    when xxx>10 and yyy<99 then bbb else null end)  cm1,

count(  case    when xxx>20 and yyy<1 then ccc  else null end)  cm2

from xxxx

基本原理是 使用了数据库count(null)时返回的是0 count(非null)返回是1

@Flink SQL

转载于:https://www.cnblogs.com/yszzu/p/10430483.html

报错Caused By: javax.faces.view.facelets.FaceletException: 检验数据查询错误,{endDate=2025-07-24, notLikeKey=T11, startDate=2025-07-18},EJBException: You have attempted to set a parameter value using a name of locator that does not exist in the query string SELECT c.id, c.orgCode, c.locator, c.empNo, c.empName, c.itemCode, c.itemDesc, c.useableQty, c.unUseableQty, c.demageQty, c.offQty, c.signNo, c.checkoutEmpNo, c.checkoutEmpName, c.checkOutDate, c.finishedEmpNo, c.finishedEmpName, c.finishedDate, c.payMoney, c.overTime, MAX(n.oldToolGatherNo), c.requestNo, c.erpState FROM ToolCheckEntity c LEFT JOIN ToolNoteRequestEntity n ON n.requestNo = c.requestNo WHERE c.state='已完成' AND c.locator not like :notLikeKey AND (c.checkOutDate BETWEEN :startDate AND :endDate) group by c.id, c.orgCode, c.locator, c.empNo, c.empName,, // 构建动态查询语句 private String buildDynamicQuery(Map<String, String> queryMap, Map<String, String> sortMap, boolean isCountQuery) { StringBuilder sqlBuilder = new StringBuilder(); if (isCountQuery) { // sqlBuilder.append("SELECT COUNT(c) "); sqlBuilder.append("SELECT COUNT(DISTINCT c.id) FROM ToolCheckEntity c "); } else { sqlBuilder.append("SELECT c.id, c.orgCode, c.locator, c.empNo, c.empName, "); sqlBuilder.append("c.itemCode, c.itemDesc, c.useableQty, c.unUseableQty, c.demageQty, "); sqlBuilder.append("c.offQty, c.signNo, c.checkoutEmpNo, c.checkoutEmpName, c.checkOutDate, "); sqlBuilder.append("c.finishedEmpNo, c.finishedEmpName, c.finishedDate, c.payMoney, c.overTime, "); sqlBuilder.append("MAX(n.oldToolGatherNo), c.requestNo, c.erpState "); sqlBuilder.append("FROM ToolCheckEntity c LEFT JOIN ToolNoteRequestEntity n ON n.requestNo = c.requestNo "); } // StringBuilder sqlBuilder = new StringBuilder(); // sqlBuilder.append("SELECT c.id, c.orgCode, c.locator, c.empNo, c.empName, "); // sqlBuilder.append("c.itemCode, c.itemDesc, c.useableQty, c.unUseableQty, c.demageQty, "); // sqlBuilder.append("c.offQty, c.signNo, c.checkoutEmpNo, c.checkoutEmpName, c.checkOutDate, "); // sqlBuilder.append("c.finishedEmpNo, c.finishedEmpName, c.finishedDate, c.payMoney, c.overTime, "); // sqlBuilder.append("n.oldToolGatherNo, c.requestNo, c.erpState "); // sqlBuilder.append("FROM ToolCheckEntity c LEFT JOIN ToolNoteRequestEntity n ON n.requestNo = c.requestNo "); sqlBuilder.append("WHERE "); sqlBuilder.append(" c.state='已完成' "); // 添WHERE条件 if (queryMap != null && !queryMap.isEmpty()) { // boolean firstCondition = true; // 处理orgCode条件 if (queryMap.containsKey("orgCode") && StringUtils.isNotBlank(queryMap.get("orgCode"))) { sqlBuilder.append(" AND c.orgCode = :orgCode "); } // 处理locator条件 if (queryMap.containsKey("locator") && StringUtils.isNotBlank(queryMap.get("locator"))) { sqlBuilder.append(" AND c.locator = :locator "); } // 处理empNo条件 if (queryMap.containsKey("empNo") && StringUtils.isNotBlank(queryMap.get("empNo"))) { sqlBuilder.append(" AND c.empNo = :empNo "); } if (queryMap.containsKey("itemCode") && StringUtils.isNotBlank(queryMap.get("itemCode"))) { sqlBuilder.append(" AND c.itemCode like :itemCode "); } if (queryMap.containsKey("notLikeKey") && StringUtils.isNotBlank(queryMap.get("notLikeKey"))) { sqlBuilder.append(" AND c.locator not like :notLikeKey "); } if (queryMap.containsKey("startDate") && StringUtils.isNotBlank(queryMap.get("startDate"))&& queryMap.containsKey("endDate") && StringUtils.isNotBlank(queryMap.get("endDate")) ){ // 添日期范围条件 - 根据业务需求选择合适的日期字段 sqlBuilder.append(" AND (c.checkOutDate BETWEEN :startDate AND :endDate) "); } } if (!isCountQuery) { String groupByStr=" group by c.id, c.orgCode, c.locator, c.empNo, c.empName, \n" + " c.itemCode, c.itemDesc, c.useableQty, c.unUseableQty, \n" + " c.demageQty, c.offQty, c.signNo, c.checkoutEmpNo, \n" + " c.checkoutEmpName, c.checkOutDate, c.finishedEmpNo, \n" + " c.finishedEmpName, c.finishedDate, c.payMoney, \n" + " c.overTime, c.requestNo, c.erpState"; sqlBuilder.append(groupByStr); String orderBy=" order by c.checkOutDate desc"; sqlBuilder.append(orderBy); } return sqlBuilder.toString(); } // 设置查询参数 private void setQueryParameters(Query query, Map<String, String> queryMap) { if (queryMap == null) return; if (queryMap.containsKey("orgCode") && StringUtils.isNotBlank(queryMap.get("orgCode"))) { query.setParameter("orgCode", queryMap.get("orgCode")); } if (queryMap.containsKey("locator") && StringUtils.isNotBlank(queryMap.get("locator"))) { query.setParameter("locator", queryMap.get("locator")); } if (queryMap.containsKey("empNo") && StringUtils.isNotBlank(queryMap.get("empNo"))) { query.setParameter("empNo", queryMap.get("empNo")); } if (queryMap.containsKey("itemCode") && StringUtils.isNotBlank(queryMap.get("itemCode"))) { query.setParameter("itemCode", "%"+queryMap.get("itemCode")+"%"); } if (queryMap.containsKey("notLikeKey") && StringUtils.isNotBlank(queryMap.get("notLikeKey"))) { query.setParameter("locator", queryMap.get("notLikeKey")+"%"); } if (queryMap.containsKey("startDate") && StringUtils.isNotBlank(queryMap.get("startDate"))&& queryMap.containsKey("endDate") && StringUtils.isNotBlank(queryMap.get("endDate")) ) { try { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm"); // 定义日期格式 Date startDate = sdf.parse(queryMap.get("startDate").toString()+" 00:00"); Date endDate = sdf.parse(queryMap.get("endDate").toString()+" 23:59"); long betweenDays = (endDate.getTime() - startDate.getTime()) / 24 / 60 / 60 / 1000; if (betweenDays > 7) { throw new EJBException("查询时间跨度不能超过7天"); }else { // 设置查询参数 query.setParameter("startDate", startDate); query.setParameter("endDate", endDate); } } catch (ParseException e) { e.printStackTrace(); } } }
最新发布
07-24
<think>根据错误信息,问题在于设置参数时使用了不存在的参数名。具体来说,错误信息指出:`You have attempted to set a parameter value using a name of locator that does not exist in the query string`。 在动态构建的查询语句中,我们注意到有一个条件:`c.locator not like :notLikeKey`,这里使用的参数名是`:notLikeKey`。然而,在设置参数的方法`setQueryParameters`中,我们却使用了`locator`作为参数名来设置这个值: ```java if (queryMap.containsKey("notLikeKey") && StringUtils.isNotBlank(queryMap.get("notLikeKey"))) { query.setParameter("locator", queryMap.get("notLikeKey")+"%"); } ``` 这里将参数名写成了`locator`,而查询语句中对应的参数名是`:notLikeKey`。因此,当我们尝试设置参数名为`locator`时,JPA引擎在查询字符串中找不到这个参数名,从而抛出异常。 另外,还有一个潜在的问题:在同一个查询中,我们同时使用了`locator`和`notLikeKey`两个条件,但是设置参数时,对于`notLikeKey`条件错误地使用了`locator`参数名,而对于正常的`locator`条件,我们使用了`locator`参数名。这样就会导致冲突,因为两个条件都试图设置同一个参数名`locator`,但查询语句中实际有两个不同的参数(`:locator`和`:notLikeKey`)。 因此,我们需要做以下修正: 1. 对于`notLikeKey`条件,设置参数时应该使用参数名`notLikeKey`,而不是`locator`。 2. 同时,确保其他条件使用正确的参数名。 修改`setQueryParameters`方法中关于`notLikeKey`的部分: ```java if (queryMap.containsKey("notLikeKey") && StringUtils.isNotBlank(queryMap.get("notLikeKey"))) { // 注意:这里参数名改为"notLikeKey" query.setParameter("notLikeKey", queryMap.get("notLikeKey") + "%"); } ``` 此外,检查其他条件的参数设置,确保参数名与查询语句中的命名参数一致。 另外,在查询语句中,我们固定添条件:`c.state='已完成'`。注意,如果这个条件需要参数化,也应该使用命名参数,以避免SQL注入风险。但这里我们暂时不修改,因为它是固定值。 但是,请注意:在构建查询语句时,我们一开始就添了`WHERE`关键字,然后紧接着是`c.state='已完成'`,然后其他条件前面都了`AND`。这样,如果其他条件不存在,那么整个WHERE子句只有`c.state='已完成'`,这是正确的。 然而,在计数查询(isCountQuery为true)时,我们没有添GROUP BY,但是我们在计数查询中并没有添JOIN,这很好。但是,计数查询的语句中,我们只写了`FROM ToolCheckEntity c`,而后面又添了WHERE条件。注意,在计数查询中,我们并没有添LEFT JOIN,所以当WHERE条件中包含与ToolNoteRequestEntity相关的条件时,计数查询会失败。但是目前我们的WHERE条件中并没有使用到n(ToolNoteRequestEntity的别名),所以是安全的。 但是,在普通查询中,我们使用了LEFT JOIN,并且使用了聚合函数MAX,所以需要GROUP BY。而在计数查询中,我们使用了`COUNT(DISTINCT c.id)`,这是正确的,因为可能会有重复(由于LEFT JOIN导致的行数增多),使用DISTINCT c.id可以避免重复计数。 现在,我们修正参数设置的问题,并重新检查整个代码。 修改后的`setQueryParameters`方法中关于`notLikeKey`的部分: ```java private void setQueryParameters(Query query, Map<String, String> queryMap) { // ... 其他条件设置 if (queryMap.containsKey("notLikeKey") && StringUtils.isNotBlank(queryMap.get("notLikeKey"))) { // 使用正确的参数名 "notLikeKey" query.setParameter("notLikeKey", "%" + queryMap.get("notLikeKey") + "%"); // 注意:这里我们使用了LIKE,所以通常要通配符。但是根据条件“not like”,我们可能只需要在值中加通配符,具体取决于业务。 // 但是原始代码中,我们使用的是:c.locator not like :notLikeKey // 所以,我们设置参数值时要上通配符。但是注意,原始代码中设置的是:queryMap.get("notLikeKey")+"%",即后缀匹配。 // 这里根据业务需求,如果是要排除以某个字符串开头的locator,那么使用后缀通配符是合适的。 // 但是,在SQL的LIKE中,如果使用not like,并且要排除包含某个字符串,通常使用前后都通配符,即:%T11% // 但是根据业务,这里可能是要排除以T11开头的,所以使用T11%即可。 // 所以,原始代码中设置参数为:queryMap.get("notLikeKey")+"%" 是正确的。 // 但是,我们这里修改了参数名,所以: query.setParameter("notLikeKey", queryMap.get("notLikeKey") + "%"); } // ... 日期参数设置 } ``` 但是,注意在构建查询字符串时,对于`notLikeKey`条件,我们写的是: ```sql AND c.locator not like :notLikeKey ``` 所以,我们设置参数时,参数名必须是`:notLikeKey`,并且值应该是要排除的模式,比如`T11%`。 另外,在设置`itemCode`条件时,我们使用了`like`,并且设置了参数值为`%...%`,这是正确的。 还有一个问题:在设置`locator`条件(等于条件)时,我们使用了: ```java if (queryMap.containsKey("locator") && StringUtils.isNotBlank(queryMap.get("locator"))) { sqlBuilder.append(" AND c.locator = :locator "); } ``` 而在设置参数时: ```java if (queryMap.containsKey("locator") && StringUtils.isNotBlank(queryMap.get("locator"))) { query.setParameter("locator", queryMap.get("locator")); } ``` 这是正确的。 因此,主要问题就是`notLikeKey`条件在设置参数时使用了错误的参数名`locator`,修正为`notLikeKey`即可。 下面给出修正后的`setQueryParameters`方法中关于`notLikeKey`的部分: 修改后: ```java private void setQueryParameters(Query query, Map<String, String> queryMap) { if (queryMap == null) return; if (queryMap.containsKey("orgCode") && StringUtils.isNotBlank(queryMap.get("orgCode"))) { query.setParameter("orgCode", queryMap.get("orgCode")); } if (queryMap.containsKey("locator") && StringUtils.isNotBlank(queryMap.get("locator"))) { query.setParameter("locator", queryMap.get("locator")); } if (queryMap.containsKey("empNo") && StringUtils.isNotBlank(queryMap.get("empNo"))) { query.setParameter("empNo", queryMap.get("empNo")); } if (queryMap.containsKey("itemCode") && StringUtils.isNotBlank(queryMap.get("itemCode"))) { query.setParameter("itemCode", "%"+queryMap.get("itemCode")+"%"); } // 修正这里:将参数名从'locator'改为'notLikeKey' if (queryMap.containsKey("notLikeKey") && StringUtils.isNotBlank(queryMap.get("notLikeKey"))) { // 注意:这里参数名改为"notLikeKey" query.setParameter("notLikeKey", queryMap.get("notLikeKey") + "%"); } // 日期参数设置保持不变 if (queryMap.containsKey("startDate") && StringUtils.isNotBlank(queryMap.get("startDate")) && queryMap.containsKey("endDate") && StringUtils.isNotBlank(queryMap.get("endDate"))) { try { SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm"); // 注意:这里小时用的是hh,是12小时制,建议用HH表示24小时制 // 修改为24小时制 // 另外,注意传入的日期字符串是"2025-07-18"这样的格式,所以我们上时间部分 Date startDate = sdf.parse(queryMap.get("startDate") + " 00:00"); Date endDate = sdf.parse(queryMap.get("endDate") + " 23:59"); // 检查时间跨度 long betweenDays = (endDate.getTime() - startDate.getTime()) / (24 * 60 * 60 * 1000); if (betweenDays > 7) { throw new EJBException("查询时间跨度不能超过7天"); } else { query.setParameter("startDate", startDate); query.setParameter("endDate", endDate); } } catch (ParseException e) { e.printStackTrace(); } } } ``` 另外,注意在日期解析中,我们使用了`hh`,这是12小时制,而23:59这样的时间在12小时制中会解析为11:59 PM,并且没有指定AM/PM,所以可能会解析错误。建议修改为24小时制,即使用`HH`: ```java SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm"); ``` 这样,解析`23:59`就不会有问题。 总结:主要修正点有两个: 1. 将`notLikeKey`条件的参数设置由`locator`改为`notLikeKey`。 2. 将日期解析的格式改为24小时制。 这样修改后,参数设置错误的问题应该就能解决。 如果还有其他参数设置问题,请按照同样的思路检查:确保查询语句中出现的命名参数在设置参数时使用相同的名字。 最后,请注意,在构建查询字符串时,我们固定添了`c.state='已完成'`,如果这个状态值可能变化,建议参数化以避免SQL注入,并且提高灵活性。但根据目前需求,我们暂时不修改。 修改后的完整代码示例: 由于代码较长,这里只给出修改后的`setQueryParameters`方法,以及指出在构建查询字符串时关于`notLikeKey`的部分没有变化,只需修正参数设置。 但是,为了确保完整,这里也给出构建查询字符串时关于`notLikeKey`的部分(在`buildDynamicQuery`方法中): ```java if (queryMap.containsKey("notLikeKey") && StringUtils.isNotBlank(queryMap.get("notLikeKey"))) { sqlBuilder.append(" AND c.locator not like :notLikeKey "); } ``` 这已经是正确的。 因此,我们只需修正`setQueryParameters`方法中关于`notLikeKey`的参数设置即可。</think>这个错误是由于在设置查询参数时使用了错误的参数名导致的。具体来说,错误信息指出:"You have attempted to set a parameter value using a name of locator that does not exist in the query string"。 ### 问题分析 1. **参数名不匹配问题**: - 在查询字符串中,您使用了 `:notLikeKey` 参数: ```sql AND c.locator not like :notLikeKey ``` - 但在设置参数时,您错误地使用了 `locator` 作为参数名: ```java query.setParameter("locator", queryMap.get("notLikeKey")+"%"); ``` 2. **多个参数使用相同名称**: - 查询中同时存在 `c.locator = :locator` 和 `c.locator not like :notLikeKey` - 但在参数设置中,您都使用了 `locator` 作为键名 3. **日期格式问题**: - 使用 `hh` 表示小时(12小时制)可能导致解析错误 - 应该使用 `HH` 表示24小时制 ### 解决方案 #### 修正后的参数设置方法 ```java private void setQueryParameters(Query query, Map<String, String> queryMap) { if (queryMap == null) return; // 设置 orgCode 参数 if (queryMap.containsKey("orgCode") && StringUtils.isNotBlank(queryMap.get("orgCode"))) { query.setParameter("orgCode", queryMap.get("orgCode")); } // 设置 locator 参数(等值查询) if (queryMap.containsKey("locator") && StringUtils.isNotBlank(queryMap.get("locator"))) { query.setParameter("locator", queryMap.get("locator")); } // 设置 empNo 参数 if (queryMap.containsKey("empNo") && StringUtils.isNotBlank(queryMap.get("empNo"))) { query.setParameter("empNo", queryMap.get("empNo")); } // 设置 itemCode 参数(模糊查询) if (queryMap.containsKey("itemCode") && StringUtils.isNotBlank(queryMap.get("itemCode"))) { query.setParameter("itemCode", "%" + queryMap.get("itemCode") + "%"); } // 修正:使用正确的参数名 notLikeKey(非等值查询) if (queryMap.containsKey("notLikeKey") && StringUtils.isNotBlank(queryMap.get("notLikeKey"))) { query.setParameter("notLikeKey", queryMap.get("notLikeKey") + "%"); } // 设置日期参数(使用24小时制格式) if (queryMap.containsKey("startDate") && StringUtils.isNotBlank(queryMap.get("startDate")) && queryMap.containsKey("endDate") && StringUtils.isNotBlank(queryMap.get("endDate"))) { try { // 使用 HH 表示24小时制 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm"); Date startDate = sdf.parse(queryMap.get("startDate") + " 00:00"); Date endDate = sdf.parse(queryMap.get("endDate") + " 23:59"); long betweenDays = (endDate.getTime() - startDate.getTime()) / (24 * 60 * 60 * 1000); if (betweenDays > 7) { throw new EJBException("查询时间跨度不能超过7天"); } else { // 使用 TemporalType 确保正确类型转换 query.setParameter("startDate", startDate, TemporalType.TIMESTAMP); query.setParameter("endDate", endDate, TemporalType.TIMESTAMP); } } catch (ParseException e) { throw new RuntimeException("日期格式解析错误", e); } } } ``` ### 关键修正说明 1. **参数名修正**: - 将 `notLikeKey` 条件的参数设置从 `locator` 改为 `notLikeKey` - 确保查询字符串中的参数名与设置参数时使用的名称完全一致 2. **日期格式修正**: - 将 `hh` 改为 `HH` 使用24小时制: ```java SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm"); ``` 3. **参数类型明确指定**: - 使用 `TemporalType.TIMESTAMP` 确保日期参数正确转换: ```java query.setParameter("startDate", startDate, TemporalType.TIMESTAMP); ``` 4. **错误处理改进**: - 添更明确的异常信息: ```java throw new RuntimeException("日期格式解析错误", e); ``` ### 完整的最佳实践建议 #### 1. 使用参数映射避免名称冲突 ```java // 创建参数映射 Map<String, Object> params = new HashMap<>(); // 添参数 if (queryMap.containsKey("locator")) { params.put("locatorParam", queryMap.get("locator")); } if (queryMap.containsKey("notLikeKey")) { params.put("notLikeKeyParam", queryMap.get("notLikeKey") + "%"); } // 在查询中使用不同的参数名 sqlBuilder.append("c.locator = :locatorParam AND c.locator not like :notLikeKeyParam"); // 设置参数 params.forEach(query::setParameter); ``` #### 2. 使用 Criteria API 避免字符串拼接错误 ```java public List<ToolCheckEntity> findToolChecks(Map<String, String> filters) { CriteriaBuilder cb = entityManager.getCriteriaBuilder(); CriteriaQuery<ToolCheckEntity> cq = cb.createQuery(ToolCheckEntity.class); Root<ToolCheckEntity> root = cq.from(ToolCheckEntity.class); Join<ToolCheckEntity, ToolNoteRequestEntity> noteJoin = root.join("noteRequest", JoinType.LEFT); List<Predicate> predicates = new ArrayList<>(); // 添固定条件 predicates.add(cb.equal(root.get("state"), "已完成")); // 动态添条件 if (filters.containsKey("locator")) { predicates.add(cb.equal(root.get("locator"), filters.get("locator"))); } if (filters.containsKey("notLikeKey")) { predicates.add(cb.notLike(root.get("locator"), filters.get("notLikeKey") + "%")); } // 日期范围 if (filters.containsKey("startDate") && filters.containsKey("endDate")) { Date start = parseDate(filters.get("startDate")); Date end = parseDate(filters.get("endDate")); predicates.add(cb.between(root.get("checkOutDate"), start, end)); } cq.where(predicates.toArray(new Predicate[0])); cq.groupBy(root.get("id"), ...); // 所有非聚合字段 return entityManager.createQuery(cq).getResultList(); } ``` #### 3. 使用命名参数常量避免拼写错误 ```java public class QueryConstants { public static final String PARAM_ORG_CODE = "orgCode"; public static final String PARAM_LOCATOR = "locator"; public static final String PARAM_NOT_LIKE_KEY = "notLikeKey"; // 其他参数... } // 在查询构建中使用 sqlBuilder.append("c.locator not like :" + QueryConstants.PARAM_NOT_LIKE_KEY); // 在参数设置中使用 query.setParameter(QueryConstants.PARAM_NOT_LIKE_KEY, value); ``` ### 预防类似错误的建议 1. **参数命名一致性**: - 在查询字符串和参数设置中使用完全相同的参数名 - 避免使用保留字或易混淆的名称 2. **使用参数日志**: ```java public void logParameters(Query query) { Parameter<?>[] parameters = query.getParameters().toArray(new Parameter[0]); for (Parameter<?> param : parameters) { System.out.println("Param: " + param.getName() + " = " + query.getParameterValue(param)); } } ``` 3. **单元测试验证**: ```java @Test void testSetQueryParameters() { // 准备测试数据 Map<String, String> params = new HashMap<>(); params.put("notLikeKey", "T11"); // 执行方法 setQueryParameters(query, params); // 验证参数 assertEquals("T11%", query.getParameterValue("notLikeKey")); } ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值