Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
提交反馈
为 GitLab 提交贡献
登录
切换导航
I
ibizlab-generator
项目
项目
详情
动态
版本
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
ibiz4jteam
ibizlab-generator
提交
cf83eadb
提交
cf83eadb
编写于
1月 14, 2022
作者:
sq3536
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
引用修复
上级
dabd888a
变更
8
显示空白字符变更
内嵌
并排
正在显示
8 个修改的文件
包含
380 行增加
和
42 行删除
+380
-42
application-{{apps}}-prod.yml.hbs
...ps}}/src/main/resources/application-{{apps}}-prod.yml.hbs
+1
-4
LiquibaseConfiguration.java.hbs
...geName}}/core/util/config/LiquibaseConfiguration.java.hbs
+2
-0
AuthPermissionEvaluator.java.hbs
...ame}}/core/util/security/AuthPermissionEvaluator.java.hbs
+328
-0
application-sys.yml.hbs
...ectName}}-core/src/main/resources/application-sys.yml.hbs
+20
-20
pom.xml.hbs
...sources/templ/r7/{{projectName}}-dependencies/pom.xml.hbs
+21
-15
pom.xml.hbs
...n/resources/templ/r7/{{projectName}}-provider/pom.xml.hbs
+5
-0
SwaggerConfiguration.java.hbs
...ava/{{packageName}}/swagger/SwaggerConfiguration.java.hbs
+1
-1
{{system.codeName}}{{api.codeName}}Application.java.hbs
...}/{{system.codeName}}{{api.codeName}}Application.java.hbs
+2
-2
未找到文件。
modules/ibizlab-generator-core/src/main/resources/templ/r7/{{projectName}}-app/{{projectName}}-app-{{apps}}/src/main/resources/application-{{apps}}-prod.yml.hbs
浏览文件 @
cf83eadb
server:
port:
{{#if
app
.
httpPort
}}{{
app
.
httpPort
}}{{else}}
8080
{{/if}}
spring:
cache:
redis:
...
...
@@ -62,6 +58,7 @@ ibiz:
### 启用Gzip压缩
server:
port:
{{#if
app
.
httpPort
}}{{
app
.
httpPort
}}{{else}}
8080
{{/if}}
compression:
enabled: true
mime-types: application/javascript,text/css,application/json,application/xml,text/html,text/xml,text/plain
...
...
modules/ibizlab-generator-core/src/main/resources/templ/r7/{{projectName}}-core/src/main/java/{{packageName}}/core/util/config/LiquibaseConfiguration.java.hbs
浏览文件 @
cf83eadb
package
{{
packageName
}}
.core.util.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
...
...
@@ -21,6 +22,7 @@ import com.alibaba.druid.pool.DruidDataSource;
import org.apache.ibatis.session.SqlSessionFactory;
@Configuration
@ConditionalOnProperty( name = "spring.datasource.isSyncDBSchema", havingValue = "true")
public class LiquibaseConfiguration {
/**
...
...
modules/ibizlab-generator-core/src/main/resources/templ/r7/{{projectName}}-core/src/main/java/{{packageName}}/core/util/security/AuthPermissionEvaluator.java.hbs
0 → 100644
浏览文件 @
cf83eadb
package
{{
packageName
}}
.core.util.security;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.SneakyThrows;
import cn.ibizlab.util.annotation.DEField;
import cn.ibizlab.util.domain.EntityBase;
import cn.ibizlab.util.enums.DEPredefinedFieldType;
import cn.ibizlab.util.filter.QueryWrapperContext;
import cn.ibizlab.util.helper.DEFieldCacheMap;
import cn.ibizlab.util.security.AuthenticationUser;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.access.PermissionEvaluator;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.function.Consumer;
/**
* spring security 权限管理类
* 重写权限控制方法
*/
@Component
public class AuthPermissionEvaluator implements PermissionEvaluator {
@Value("${ibiz.enablePermissionValid:false}")
boolean enablePermissionValid; //是否开启权限校验
/**
* 服务接口鉴权
* @param authentication 用户
* @param entity 实体
* @param action 操作
* @return
*/
@Override
@SneakyThrows
public boolean hasPermission(Authentication authentication, Object entity, Object action) {
if(!enablePermissionValid){
return true;
}
Object principal = authentication.getPrincipal();
if(ObjectUtils.isEmpty(principal)){
return false;
}
AuthenticationUser authenticationUser= (AuthenticationUser) authentication.getPrincipal();
if(authenticationUser.getSuperuser()==1){
return true;
}
String strAction=String.valueOf(action);
Set
<String>
userAuthorities = getAuthorities(authentication,strAction);
if(userAuthorities.size()==0){
return false;
}
if(isAllData(strAction,userAuthorities)){
return true;
}
if(entity instanceof ArrayList){
List
<EntityBase>
entities= (List
<EntityBase>
) entity;
for(EntityBase entityBase: entities){
boolean result=actionValid(entityBase, strAction ,userAuthorities,authenticationUser);
if(!result){
return false;
}
}
}
else if (entity instanceof QueryWrapperContext){
QueryWrapperContext queryWrapperContext= (QueryWrapperContext) entity;
setPermissionCondToSearchContext(getEntity(queryWrapperContext),queryWrapperContext,userAuthorities,authenticationUser);
}
else{
EntityBase entityBase= (EntityBase) entity;
return actionValid(entityBase , strAction ,userAuthorities,authenticationUser);
}
return true;
}
/**
* 获取实体信息
* @param qc
* @return
*/
@SneakyThrows
private EntityBase getEntity(QueryWrapperContext qc){
EntityBase entity=null;
Type type =qc.getClass().getGenericSuperclass();
if(type instanceof ParameterizedType){
ParameterizedType parameterizedType= (ParameterizedType) qc.getClass().getGenericSuperclass();
Type [] typeArr= parameterizedType.getActualTypeArguments();
if(typeArr.length>0){
Class
<EntityBase>
entityClass = (Class) typeArr[0];
return entityClass.newInstance();
}
}
return entity;
}
/**
* 在searchContext中拼接权限条件
* @param entity 实体
* @param qc 查询上下文
* @param userAuthorities 用户权限
* @param authenticationUser 当前用户
*/
@SneakyThrows
private void setPermissionCondToSearchContext(EntityBase entity, QueryWrapperContext qc , Set
<String>
userAuthorities ,AuthenticationUser authenticationUser){
if(entity==null){
return ;
}
Map
<String
,
String
>
permissionField=getPermissionField(entity);//获取组织、部门预置属性
String orgField=permissionField.get("orgfield");
String orgDeptField=permissionField.get("orgsecfield");
String createManField=permissionField.get("createmanfield");
Map
<String
,
Set
<
String
>
> userInfo = authenticationUser.getOrgInfo();
Set
<String>
orgParent = userInfo.get("parentorg");
Set
<String>
orgChild = userInfo.get("suborg");
Set
<String>
orgDeptParent = userInfo.get("parentdept");
Set
<String>
orgDeptChild = userInfo.get("subdept");
Set
<String>
userOrg = new HashSet
<>
();
Set
<String>
userOrgDept = new HashSet
<>
();
Set
<String>
userCreateMan = new HashSet
<>
();
for(String authority:userAuthorities){
if(authority.endsWith("curorg")){ //本单位
userOrg.add(authenticationUser.getOrgid());
}
else if(authority.endsWith("porg")){//上级单位
userOrg.addAll(orgParent);
}
else if(authority.endsWith("sorg")){//下级单位
userOrg.addAll(orgChild);
}
else if(authority.endsWith("curorgdept")){//本部门
userOrgDept.add(authenticationUser.getMdeptid());
}
else if(authority.endsWith("porgdept")){//上级部门
userOrgDept.addAll(orgDeptParent);
}
else if(authority.endsWith("sorgdept")){//下级部门
userOrgDept.addAll(orgDeptChild);
}
else if (authority.endsWith("createman")){
userCreateMan.add(authority);
}
}
if(userOrg.size()==0
&&
userOrgDept.size()==0
&&
userCreateMan.size()==0){
qc.getSelectCond().apply("1
<>
1");
}
else{
Consumer
<QueryWrapper>
consumer = qw -> {
if(userOrg.size()>0){
Consumer
<QueryWrapper>
org = orgQw -> {
orgQw.in(orgField,userOrg);
};
qw.or(org);
}
if(userOrgDept.size()>0){
Consumer
<QueryWrapper>
dept = deptQw -> {
deptQw.in(orgDeptField,userOrgDept);
};
qw.or(dept);
}
if(userCreateMan.size()>0){
Consumer
<QueryWrapper>
createMan = createManQw -> {
createManQw.eq(createManField,authenticationUser.getUserid());
};
qw.or(createMan);
}
};
qc.getSelectCond().and(consumer);
}
}
@Override
public boolean hasPermission(Authentication authentication, Serializable id, String action, Object params) {
return true;
}
/**
* 获取用户权限资源
* @param authentication
* @param action
* @return
*/
private Set
<String>
getAuthorities(Authentication authentication , String action){
Collection authorities=authentication.getAuthorities();
Set
<String>
userAuthorities = new HashSet();
Iterator it = authorities.iterator();
while(it.hasNext()) {
GrantedAuthority authority = (GrantedAuthority)it.next();
if(authority.getAuthority().contains(action)){
userAuthorities.add(authority.getAuthority());
}
}
return userAuthorities;
}
/**
* 是否为全部数据
* @param action
* @param entityDataRange
* @return
*/
private boolean isAllData(String action , Set
<String>
entityDataRange) {
for(String dataRange : entityDataRange ){
if(dataRange.endsWith(String.format("%s-all",action))){
return true;
}
}
return false;
}
/**
* 实体行为权限校验
* @param entity
* @param userAuthorities
* @return
*/
private boolean actionValid(EntityBase entity, String action , Set
<String>
userAuthorities ,AuthenticationUser authenticationUser){
Map
<String
,
String
>
permissionField=getPermissionField(entity);//获取组织、部门预置属性
String orgField=permissionField.get("orgfield");
String orgDeptField=permissionField.get("orgsecfield");
String createManField=permissionField.get("createmanfield");
Map
<String
,
Set
<
String
>
> userInfo = authenticationUser.getOrgInfo();
Set
<String>
orgParent = userInfo.get("parentorg");
Set
<String>
orgChild = userInfo.get("suborg");
Set
<String>
orgDeptParent = userInfo.get("parentdept");
Set
<String>
orgDeptChild = userInfo.get("subdept");
Object orgFieldValue=entity.get(orgField);
Object orgDeptFieldValue=entity.get(orgDeptField);
Object crateManFieldValue=entity.get(createManField);
Set
<String>
userOrg = new HashSet
<>
();
Set
<String>
userOrgDept = new HashSet
<>
();
for(String authority:userAuthorities){
if(authority.endsWith("curorg")){ //本单位
userOrg.add(authenticationUser.getOrgid());
}
else if(authority.endsWith("porg")){//上级单位
userOrg.addAll(orgParent);
}
else if(authority.endsWith("sorg")){//下级单位
userOrg.addAll(orgChild);
}
else if(authority.endsWith("curorgdept")){//本部门
userOrgDept.add(authenticationUser.getMdeptid());
}
else if(authority.endsWith("porgdept")){//上级部门
userOrgDept.addAll(orgDeptParent);
}
else if(authority.endsWith("sorgdept")){//下级部门
userOrgDept.addAll(orgDeptChild);
}
}
if(action.endsWith("Create") || action.endsWith("Save")){
if(!ObjectUtils.isEmpty(orgFieldValue)
&&
!userOrg.contains(orgFieldValue)){
return false;
}
if(!ObjectUtils.isEmpty(orgDeptFieldValue)
&&
!userOrgDept.contains(orgDeptFieldValue)){
return false;
}
if(!ObjectUtils.isEmpty(crateManFieldValue)
&&
!authenticationUser.getUserid().equals(crateManFieldValue)){
return false;
}
return true;
}
else{
if(!ObjectUtils.isEmpty(orgFieldValue)
&&
userOrg.contains(orgFieldValue)){
return true;
}
if(!ObjectUtils.isEmpty(orgDeptFieldValue)
&&
userOrgDept.contains(orgDeptFieldValue)){
return true;
}
if(!ObjectUtils.isEmpty(crateManFieldValue)
&&
authenticationUser.getUserid().equals(crateManFieldValue)){
return true;
}
return false;
}
}
/**
* 获取实体权限字段 orgid/orgsecid
* @param entityBase
* @return
*/
private Map
<String
,
String
>
getPermissionField(EntityBase entityBase){
Map
<String
,
String
>
permissionFiled=new HashMap
<>
();
String orgField="orgid"; //组织属性
String orgDeptField="orgsectorid"; //部门属性
String createManField="createman"; //创建人属性
DEFieldCacheMap.getFieldMap(entityBase.getClass().getName());
Map
<String
,
DEField
>
preFields= DEFieldCacheMap.getDEFields(entityBase.getClass()); //从缓存中获取当前类预置属性
for (Map.Entry
<String
,
DEField
>
entry : preFields.entrySet()){
DEField fieldAnnotation=entry.getValue();//获取注解值
String fieldName=fieldAnnotation.name();//获取注解字段
DEPredefinedFieldType prefieldType=fieldAnnotation.preType();
if(!StringUtils.isEmpty(fieldName)){
//用户配置系统预置属性-组织机构标识
if(prefieldType==prefieldType.ORGID){
orgField=fieldName;
}
//用户配置系统预置属性-部门标识
if(prefieldType==prefieldType.ORGSECTORID){
orgDeptField=fieldName;
}
//用户配置系统预置属性-部门标识
if(prefieldType==prefieldType.CREATEMAN){
createManField=fieldName;
}
}
}
permissionFiled.put("orgfield",orgField);
permissionFiled.put("orgsecfield",orgDeptField);
permissionFiled.put("createmanfield",createManField);
return permissionFiled;
}
}
\ No newline at end of file
modules/ibizlab-generator-core/src/main/resources/templ/r7/{{projectName}}-core/src/main/resources/application-sys.yml.hbs
浏览文件 @
cf83eadb
...
...
@@ -43,26 +43,6 @@ spring:
isSyncDBSchema: false
defaultSchema:
{{
projectName
}}
{{#if
system
.
enableDS
}}
conf: classpath:liquibase/master.xml
filters: stat,wall,log4j2
#配置初始化大小/最小/最大
initial-size: 1
min-idle: 1
max-active: 20
#获取连接等待超时时间
max-wait: 60000
#间隔多久进行一次检测,检测需要关闭的空闲连接
time-between-eviction-runs-millis: 60000
#一个连接在池中最小生存的时间
min-evictable-idle-time-millis: 300000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
#打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false
pool-prepared-statements: false
max-pool-prepared-statement-per-connection-size: 20
{{else}}
dynamic:
druid: #以下是全局默认值,可以全局更改
filters: stat,log4j2
...
...
@@ -100,6 +80,26 @@ spring:
conf: classpath:liquibase/master.xml
isSyncDBSchema: ${spring.datasource.isSyncDBSchema}
defaultSchema: ${spring.datasource.defaultSchema}
{{else}}
conf: classpath:liquibase/master.xml
filters: stat,wall,log4j2
#配置初始化大小/最小/最大
initial-size: 1
min-idle: 1
max-active: 20
#获取连接等待超时时间
max-wait: 60000
#间隔多久进行一次检测,检测需要关闭的空闲连接
time-between-eviction-runs-millis: 60000
#一个连接在池中最小生存的时间
min-evictable-idle-time-millis: 300000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
#打开PSCache,并指定每个连接上PSCache的大小。oracle设为true,mysql设为false。分库分表较多推荐设置为false
pool-prepared-statements: false
max-pool-prepared-statement-per-connection-size: 20
{{/if}}
#Mybatis-plus配置
...
...
modules/ibizlab-generator-core/src/main/resources/templ/r7/{{projectName}}-dependencies/pom.xml.hbs
浏览文件 @
cf83eadb
...
...
@@ -15,7 +15,7 @@
<parent>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-parent
</artifactId>
<version>
2.2.
1
.RELEASE
</version>
<version>
2.2.
5
.RELEASE
</version>
</parent>
<properties>
...
...
@@ -25,22 +25,22 @@
<spring-cloud-security
.
version
>
2.1.1.RELEASE
</spring-cloud-security
.version
>
<!-- openfeign -->
<spring-cloud-openfeign
.
version
>
2.2.
1
.RELEASE
</spring-cloud-openfeign
.version
>
<spring-cloud-openfeign
.
version
>
2.2.
5
.RELEASE
</spring-cloud-openfeign
.version
>
<!-- Zuul网关 -->
<spring-cloud-starter-netflix-zuul
.
version
>
2.2.
1
.RELEASE
</spring-cloud-starter-netflix-zuul
.version
>
<spring-cloud-starter-netflix-zuul
.
version
>
2.2.
5
.RELEASE
</spring-cloud-starter-netflix-zuul
.version
>
<!-- Spring Cloud Alibaba(2.2.x.RELEASE) & Spring Cloud(Spring Cloud Greenwich) & Spring Boot(2.2.x.RELEASE) compatibility -->
<spring-cloud-alibaba
.
version
>
2.2.1.RELEASE
</spring-cloud-alibaba
.version
>
<!-- eureka微服务注册中心 -->
<eureka-client
.
version
>
2.2.
1
.RELEASE
</eureka-client
.version
>
<eureka-client
.
version
>
2.2.
5
.RELEASE
</eureka-client
.version
>
<!--Java Web Token-->
<jsonwebtoken-jjwt
.
version
>
0.9.1
</jsonwebtoken-jjwt
.version
>
<!--caffeine缓存-->
<caffeine
-cache
.
version
>
2.6.0
</caffeine-cach
e
.version
>
<caffeine
.
version
>
2.8.1
</caffein
e
.version
>
<!--反序列化工具-->
<kryo
.
version
>
4.0.2
</kryo
.version
>
...
...
@@ -80,24 +80,24 @@
<baomidou-jobs
.
version
>
1.0.3
</baomidou-jobs
.version
>
<!-- 阿里sentinel熔断器 -->
<alibaba-sentinel
.
version
>
2.
1
.1.RELEASE
</alibaba-sentinel
.version
>
<alibaba-sentinel
.
version
>
2.
2
.1.RELEASE
</alibaba-sentinel
.version
>
<!-- 阿里seata分布式事务 -->
<alibaba-seata
.
version
>
1.3.0
</alibaba-seata
.version
>
<o
racle
.
version
>
19.8.0.0
</oracle
.version
>
<o
jdbc
.
version
>
19.8.0.0
</ojdbc
.version
>
<postgresql
.
version
>
42.2.
6
</postgresql
.version
>
<postgresql
.
version
>
42.2.
10
</postgresql
.version
>
<mysql
.
version
>
8.0.1
8
</mysql
.version
>
<mysql
.
version
>
8.0.1
9
</mysql
.version
>
<rocketmq
.
version
>
4.7.0
</rocketmq
.version
>
<flowable-modeler
.
version
>
6.4.2
</flowable-modeler
.version
>
<ibizlab-common
.
version
>
1.0.
2
</ibizlab-common
.version
>
<ibizlab-common
.
version
>
1.0.
3
</ibizlab-common
.version
>
<ibizlab-util
.
version
>
1.0.
2
</ibizlab-util
.version
>
<ibizlab-util
.
version
>
1.0.
3
</ibizlab-util
.version
>
</properties>
...
...
@@ -154,7 +154,7 @@
<dependency>
<groupId>
com.github.ben-manes.caffeine
</groupId>
<artifactId>
caffeine
</artifactId>
<version>
${caffeine
-cache
.version}
</version>
<version>
${caffeine.version}
</version>
</dependency>
<dependency>
...
...
@@ -301,12 +301,12 @@
<dependency>
<groupId>
com.oracle.database.jdbc
</groupId>
<artifactId>
ojdbc8
</artifactId>
<version>
${o
racle
.version}
</version>
<version>
${o
jdbc
.version}
</version>
</dependency>
<dependency>
<groupId>
com.oracle.database.nls
</groupId>
<artifactId>
orai18n
</artifactId>
<version>
${o
racle
.version}
</version>
<version>
${o
jdbc
.version}
</version>
</dependency>
<!-- PostgreSQL驱动包 -->
<dependency>
...
...
@@ -319,6 +319,12 @@
<groupId>
mysql
</groupId>
<artifactId>
mysql-connector-java
</artifactId>
<version>
${mysql.version}
</version>
<exclusions>
<exclusion>
<artifactId>
protobuf-java
</artifactId>
<groupId>
com.google.protobuf
</groupId>
</exclusion>
</exclusions>
</dependency>
...
...
modules/ibizlab-generator-core/src/main/resources/templ/r7/{{projectName}}-provider/pom.xml.hbs
浏览文件 @
cf83eadb
...
...
@@ -27,6 +27,11 @@
<scope>
provided
</scope>
</dependency>
<dependency>
<groupId>
org.springframework.cloud
</groupId>
<artifactId>
spring-cloud-starter-netflix-zuul
</artifactId>
</dependency>
</dependencies>
...
...
modules/ibizlab-generator-core/src/main/resources/templ/r7/{{projectName}}-provider/{{projectName}}-provider-{{apis}}/src/main/java/{{packageName}}/swagger/SwaggerConfiguration.java.hbs
浏览文件 @
cf83eadb
...
...
@@ -59,7 +59,7 @@ public class SwaggerConfiguration {
.build()
)
.select()
.apis(RequestHandlerSelectors.basePackage("
{{
packageName
}}
.
{{
api
.
codeName
}}
"))
.apis(RequestHandlerSelectors.basePackage("
{{
packageName
}}
.
{{
lowerCase
api
.
codeName
}}
"))
.paths(PathSelectors.any())
.build();
}
...
...
modules/ibizlab-generator-core/src/main/resources/templ/r7/{{projectName}}-provider/{{projectName}}-provider-{{apis}}/src/main/java/{{packageName}}/{{apis}}/{{system.codeName}}{{api.codeName}}Application.java.hbs
浏览文件 @
cf83eadb
...
...
@@ -25,11 +25,11 @@ import java.util.List;
@EnableTransactionManagement
@ComponentScan(basePackages = {"
{{
packageName
}}
","cn.ibizlab.util"}
// ,excludeFilters={
// @ComponentScan.Filter(type= org.springframework.context.annotation.FilterType.REGEX,pattern="
{{
packageName
}}
.
${item.codeName?lower_case
}.rest.xxx"),
// @ComponentScan.Filter(type= org.springframework.context.annotation.FilterType.REGEX,pattern="
{{
packageName
}}
.
{{
lowerCase
api
.
codeName
}
}
.rest.xxx"),
// }
)
@EnableMongoRepositories(basePackages = {"
{{
packageName
}}
"})
@MapperScan(
"
{{
packageName
}}
.*.mapper"
)
@MapperScan(
{"
{{
packageName
}}
.*.mapper","cn.ibizlab.util.mapper" }
)
@SpringBootApplication(exclude = {
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration.class,
{{#
unless
system
.
enableMongo
}}
...
...
编辑
预览
Markdown
格式
0%
请重试
or
添加新附件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
先完成此消息的编辑!
取消
想要评论请
注册
或
登录