Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
ghsby
o2oa
提交
16db7134
o2oa
项目概览
ghsby
/
o2oa
落后 Fork 源项目 2880 个版本
Fork自
浙江兰德纵横网络技术股份有限公司 / o2oa
通知
1
Star
1
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
o2oa
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
体验新版 GitCode,发现更多精彩内容 >>
提交
16db7134
编写于
10月 16, 2020
作者:
Z
zhourui
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
修复未装载custom到jndi
上级
78e1dfc7
变更
3
隐藏空白更改
内联
并排
Showing
3 changed file
with
266 addition
and
41 deletion
+266
-41
o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Config.java
.../src/main/java/com/x/base/core/project/config/Config.java
+16
-1
o2server/x_console/ResourceFactory.java
o2server/x_console/ResourceFactory.java
+190
-0
o2server/x_console/src/main/java/com/x/server/console/ResourceFactory.java
...e/src/main/java/com/x/server/console/ResourceFactory.java
+60
-40
未找到文件。
o2server/x_base_core_project/src/main/java/com/x/base/core/project/config/Config.java
浏览文件 @
16db7134
...
...
@@ -100,6 +100,7 @@ public class Config {
public
static
final
String
DIR_LOCAL_UPDATE
=
"local/update"
;
public
static
final
String
DIR_LOCAL_TEMP
=
"local/temp"
;
public
static
final
String
DIR_LOCAL_TEMP_CLASSES
=
"local/temp/classes"
;
public
static
final
String
DIR_LOCAL_TEMP_CUSTOM
=
"local/temp/custom"
;
public
static
final
String
DIR_LOCAL_TEMP_SQL
=
"local/temp/sql"
;
public
static
final
String
DIR_LOCAL_TEMP_DYNAMIC
=
"local/temp/dynamic"
;
public
static
final
String
DIR_LOCAL_TEMP_DYNAMIC_SRC
=
"local/temp/dynamic/src"
;
...
...
@@ -320,6 +321,20 @@ public class Config {
return
new
File
(
base
(),
DIR_LOCAL_TEMP_CLASSES
);
}
public
static
File
dir_local_temp_custom
()
throws
Exception
{
return
new
File
(
base
(),
DIR_LOCAL_TEMP_CUSTOM
);
}
public
static
File
dir_local_temp_custom
(
Boolean
force
)
throws
Exception
{
File
dir
=
new
File
(
base
(),
DIR_LOCAL_TEMP_CUSTOM
);
if
(
force
)
{
if
((!
dir
.
exists
())
||
dir
.
isFile
())
{
FileUtils
.
forceMkdir
(
dir
);
}
}
return
dir
;
}
public
static
File
dir_local_temp_dynamic
()
throws
Exception
{
return
new
File
(
base
(),
DIR_LOCAL_TEMP_DYNAMIC
);
}
...
...
@@ -1415,4 +1430,4 @@ public class Config {
initialContext
().
rebind
(
RESOURCE_NODE_PROCESSPLATFORMEXECUTORS
,
executorServices
);
}
}
\ No newline at end of file
}
o2server/x_console/ResourceFactory.java
0 → 100644
浏览文件 @
16db7134
package
com.x.server.console
;
import
java.io.PrintStream
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Map
;
import
java.util.Map.Entry
;
import
java.util.Properties
;
import
java.util.TreeMap
;
import
java.util.concurrent.ExecutorService
;
import
java.util.concurrent.Executors
;
import
java.util.concurrent.LinkedBlockingQueue
;
import
org.apache.commons.collections.MapUtils
;
import
org.apache.commons.collections4.ListUtils
;
import
org.apache.commons.lang3.BooleanUtils
;
import
org.eclipse.jetty.plus.jndi.Resource
;
import
org.eclipse.jetty.util.RolloverFileOutputStream
;
import
com.alibaba.druid.pool.DruidDataSourceC3P0Adapter
;
import
com.google.gson.JsonElement
;
import
com.x.base.core.container.factory.SlicePropertiesBuilder
;
import
com.x.base.core.entity.Storage
;
import
com.x.base.core.entity.annotation.ContainerEntity
;
import
com.x.base.core.project.annotation.Module
;
import
com.x.base.core.project.config.CenterServer
;
import
com.x.base.core.project.config.Config
;
import
com.x.base.core.project.config.DataServer
;
import
com.x.base.core.project.config.ExternalDataSource
;
import
com.x.base.core.project.logger.Logger
;
import
com.x.base.core.project.logger.LoggerFactory
;
import
com.x.base.core.project.tools.ClassLoaderTools
;
import
com.x.base.core.project.tools.DefaultCharset
;
import
com.x.base.core.project.tools.ListTools
;
import
com.x.server.console.node.EventQueueExecutor
;
import
io.github.classgraph.ClassGraph
;
import
io.github.classgraph.ClassInfo
;
import
io.github.classgraph.ScanResult
;
public
class
ResourceFactory
{
private
static
Logger
logger
=
LoggerFactory
.
getLogger
(
ResourceFactory
.
class
);
private
ResourceFactory
()
{
// nothing
}
public
static
void
bind
()
throws
Exception
{
try
(
ScanResult
sr
=
new
ClassGraph
()
.
addClassLoader
(
ClassLoaderTools
.
urlClassLoader
(
true
,
false
,
true
,
true
,
true
)).
enableAnnotationInfo
()
.
scan
())
{
node
(
sr
);
containerEntities
(
sr
);
containerEntityNames
(
sr
);
stroageContainerEntityNames
(
sr
);
}
if
(
BooleanUtils
.
isTrue
(
Config
.
logLevel
().
audit
().
enable
()))
{
auditLog
();
}
if
(
BooleanUtils
.
isTrue
(
Config
.
externalDataSources
().
enable
()))
{
external
();
}
else
{
internal
();
}
processPlatformExecutors
();
}
private
static
void
node
(
ScanResult
sr
)
throws
Exception
{
LinkedBlockingQueue
<
JsonElement
>
eventQueue
=
new
LinkedBlockingQueue
<>();
EventQueueExecutor
eventQueueExecutor
=
new
EventQueueExecutor
(
eventQueue
);
eventQueueExecutor
.
start
();
new
Resource
(
Config
.
RESOURCE_NODE_EVENTQUEUE
,
eventQueue
);
new
Resource
(
Config
.
RESOURCE_NODE_EVENTQUEUEEXECUTOR
,
eventQueueExecutor
);
new
Resource
(
Config
.
RESOURCE_NODE_APPLICATIONS
,
null
);
new
Resource
(
Config
.
RESOURCE_NODE_APPLICATIONSTIMESTAMP
,
null
);
Entry
<
String
,
CenterServer
>
entry
=
Config
.
nodes
().
centerServers
().
first
();
new
Resource
(
Config
.
RESOURCE_NODE_CENTERSPRIMARYNODE
,
entry
.
getKey
());
new
Resource
(
Config
.
RESOURCE_NODE_CENTERSPRIMARYPORT
,
entry
.
getValue
().
getPort
());
new
Resource
(
Config
.
RESOURCE_NODE_CENTERSPRIMARYSSLENABLE
,
entry
.
getValue
().
getSslEnable
());
}
private
static
void
external
()
throws
Exception
{
external_druid_c3p0
();
}
private
static
void
external_druid_c3p0
()
throws
Exception
{
for
(
ExternalDataSource
ds
:
Config
.
externalDataSources
())
{
if
(
BooleanUtils
.
isNotTrue
(
ds
.
getEnable
()))
{
continue
;
}
DruidDataSourceC3P0Adapter
dataSource
=
new
DruidDataSourceC3P0Adapter
();
dataSource
.
setJdbcUrl
(
ds
.
getUrl
());
dataSource
.
setDriverClass
(
ds
.
getDriverClassName
());
dataSource
.
setPreferredTestQuery
(
SlicePropertiesBuilder
.
validationQueryOfUrl
(
ds
.
getUrl
()));
dataSource
.
setUser
(
ds
.
getUsername
());
dataSource
.
setPassword
(
ds
.
getPassword
());
dataSource
.
setMaxPoolSize
(
ds
.
getMaxTotal
());
dataSource
.
setMinPoolSize
(
ds
.
getMaxIdle
());
// 增加校验
dataSource
.
setTestConnectionOnCheckin
(
true
);
dataSource
.
setTestConnectionOnCheckout
(
true
);
dataSource
.
setAcquireIncrement
(
2
);
if
(
BooleanUtils
.
isTrue
(
ds
.
getStatEnable
()))
{
dataSource
.
setFilters
(
ds
.
getStatFilter
());
Properties
properties
=
new
Properties
();
properties
.
setProperty
(
"druid.stat.slowSqlMillis"
,
ds
.
getSlowSqlMillis
().
toString
());
dataSource
.
setProperties
(
properties
);
}
String
name
=
Config
.
externalDataSources
().
name
(
ds
);
new
Resource
(
Config
.
RESOURCE_JDBC_PREFIX
+
name
,
dataSource
);
}
}
private
static
void
internal
()
throws
Exception
{
internal_driud_c3p0
();
}
private
static
void
internal_driud_c3p0
()
throws
Exception
{
for
(
Entry
<
String
,
DataServer
>
entry
:
Config
.
nodes
().
dataServers
().
entrySet
())
{
DruidDataSourceC3P0Adapter
dataSource
=
new
DruidDataSourceC3P0Adapter
();
String
url
=
"jdbc:h2:tcp://"
+
entry
.
getKey
()
+
":"
+
entry
.
getValue
().
getTcpPort
()
+
"/X;LOCK_MODE=0;DEFAULT_LOCK_TIMEOUT="
+
entry
.
getValue
().
getLockTimeout
()
+
";JMX="
+
(
BooleanUtils
.
isTrue
(
entry
.
getValue
().
getJmxEnable
())
?
"TRUE"
:
"FALSE"
)
+
";CACHE_SIZE="
+
(
entry
.
getValue
().
getCacheSize
()
*
1024
);
dataSource
.
setJdbcUrl
(
url
);
dataSource
.
setDriverClass
(
SlicePropertiesBuilder
.
driver_h2
);
dataSource
.
setPreferredTestQuery
(
SlicePropertiesBuilder
.
validationQueryOfUrl
(
url
));
dataSource
.
setUser
(
"sa"
);
dataSource
.
setPassword
(
Config
.
token
().
getPassword
());
dataSource
.
setMaxPoolSize
(
entry
.
getValue
().
getMaxTotal
());
dataSource
.
setMinPoolSize
(
entry
.
getValue
().
getMaxIdle
());
dataSource
.
setAcquireIncrement
(
2
);
if
(
BooleanUtils
.
isTrue
(
entry
.
getValue
().
getStatEnable
()))
{
dataSource
.
setFilters
(
entry
.
getValue
().
getStatFilter
());
Properties
properties
=
new
Properties
();
properties
.
setProperty
(
"druid.stat.slowSqlMillis"
,
entry
.
getValue
().
getSlowSqlMillis
().
toString
());
dataSource
.
setProperties
(
properties
);
}
String
name
=
Config
.
nodes
().
dataServers
().
name
(
entry
.
getValue
());
new
Resource
(
Config
.
RESOURCE_JDBC_PREFIX
+
name
,
dataSource
);
}
}
private
static
void
containerEntityNames
(
ScanResult
sr
)
throws
Exception
{
List
<
String
>
list
=
new
ArrayList
<>();
for
(
ClassInfo
info
:
sr
.
getClassesWithAnnotation
(
ContainerEntity
.
class
.
getName
()))
{
list
.
add
(
info
.
getName
());
}
list
=
ListTools
.
trim
(
list
,
true
,
true
);
new
Resource
(
Config
.
RESOURCE_CONTAINERENTITYNAMES
,
ListUtils
.
unmodifiableList
(
list
));
}
private
static
void
stroageContainerEntityNames
(
ScanResult
sr
)
throws
Exception
{
List
<
String
>
list
=
new
ArrayList
<>();
for
(
ClassInfo
info
:
sr
.
getClassesWithAnnotation
(
Storage
.
class
.
getName
()))
{
list
.
add
(
info
.
getName
());
}
list
=
ListTools
.
trim
(
list
,
true
,
true
);
new
Resource
(
Config
.
RESOURCE_STORAGECONTAINERENTITYNAMES
,
ListUtils
.
unmodifiableList
(
list
));
}
private
static
void
containerEntities
(
ScanResult
sr
)
throws
Exception
{
Map
<
String
,
List
<
String
>>
map
=
new
TreeMap
<>();
for
(
ClassInfo
info
:
sr
.
getClassesWithAnnotation
(
Module
.
class
.
getName
()))
{
Class
<?>
cls
=
Class
.
forName
(
info
.
getName
());
List
<
String
>
os
=
ListTools
.
toList
(
cls
.
getAnnotation
(
Module
.
class
).
containerEntities
());
map
.
put
(
info
.
getName
(),
ListUtils
.
unmodifiableList
(
os
));
}
new
Resource
(
Config
.
RESOURCE_CONTAINERENTITIES
,
MapUtils
.
unmodifiableMap
(
map
));
}
private
static
void
auditLog
()
throws
Exception
{
RolloverFileOutputStream
rolloverFileOutputStream
=
new
RolloverFileOutputStream
(
Config
.
dir_logs
(
true
).
getAbsolutePath
()
+
"/yyyy_mm_dd.audit.log"
,
true
,
Config
.
logLevel
().
audit
().
logSize
());
new
Resource
(
Config
.
RESOURCE_AUDITLOGPRINTSTREAM
,
new
PrintStream
(
rolloverFileOutputStream
,
true
,
DefaultCharset
.
name_iso_utf_8
));
}
private
static
void
processPlatformExecutors
()
throws
Exception
{
ExecutorService
[]
services
=
new
ExecutorService
[
Config
.
processPlatform
().
getExecutorCount
()];
for
(
int
i
=
0
;
i
<
Config
.
processPlatform
().
getExecutorCount
();
i
++)
{
services
[
i
]
=
Executors
.
newFixedThreadPool
(
1
);
}
new
Resource
(
Config
.
RESOURCE_NODE_PROCESSPLATFORMEXECUTORS
,
services
);
}
}
o2server/x_console/src/main/java/com/x/server/console/ResourceFactory.java
浏览文件 @
16db7134
package
com.x.server.console
;
import
java.io.PrintStream
;
import
java.nio.file.Path
;
import
java.nio.file.Paths
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Map
;
...
...
@@ -13,12 +15,13 @@ import java.util.concurrent.LinkedBlockingQueue;
import
org.apache.commons.collections.MapUtils
;
import
org.apache.commons.collections4.ListUtils
;
import
org.apache.commons.dbcp2.BasicDataSource
;
import
org.apache.commons.io.FileUtils
;
import
org.apache.commons.io.FilenameUtils
;
import
org.apache.commons.io.filefilter.WildcardFileFilter
;
import
org.apache.commons.lang3.BooleanUtils
;
import
org.eclipse.jetty.plus.jndi.Resource
;
import
org.eclipse.jetty.util.RolloverFileOutputStream
;
import
com.alibaba.druid.pool.DruidDataSource
;
import
com.alibaba.druid.pool.DruidDataSourceC3P0Adapter
;
import
com.google.gson.JsonElement
;
import
com.x.base.core.container.factory.SlicePropertiesBuilder
;
...
...
@@ -29,13 +32,14 @@ import com.x.base.core.project.config.CenterServer;
import
com.x.base.core.project.config.Config
;
import
com.x.base.core.project.config.DataServer
;
import
com.x.base.core.project.config.ExternalDataSource
;
import
com.x.base.core.project.gson.XGsonBuilder
;
import
com.x.base.core.project.logger.Logger
;
import
com.x.base.core.project.logger.LoggerFactory
;
import
com.x.base.core.project.script.ScriptFactory
;
import
com.x.base.core.project.tools.ClassLoaderTools
;
import
com.x.base.core.project.tools.Crypto
;
import
com.x.base.core.project.tools.DefaultCharset
;
import
com.x.base.core.project.tools.JarTools
;
import
com.x.base.core.project.tools.ListTools
;
import
com.x.base.core.project.tools.PathTools
;
import
com.x.server.console.node.EventQueueExecutor
;
import
io.github.classgraph.ClassGraph
;
...
...
@@ -51,13 +55,12 @@ public class ResourceFactory {
}
public
static
void
bind
()
throws
Exception
{
try
(
ScanResult
sr
=
new
ClassGraph
()
.
addClassLoader
(
ClassLoaderTools
.
urlClassLoader
(
true
,
false
,
true
,
true
,
true
)).
enableAnnotationInfo
()
.
scan
())
{
node
(
sr
);
containerEntities
(
sr
);
containerEntityNames
(
sr
);
stroageContainerEntityNames
(
sr
);
ClassLoader
cl
=
ClassLoaderTools
.
urlClassLoader
(
true
,
false
,
true
,
true
,
true
,
unzipCustomWar
());
try
(
ScanResult
sr
=
new
ClassGraph
().
addClassLoader
(
cl
).
enableAnnotationInfo
().
scan
())
{
node
(
cl
,
sr
);
containerEntities
(
cl
,
sr
);
containerEntityNames
(
cl
,
sr
);
stroageContainerEntityNames
(
cl
,
sr
);
}
if
(
BooleanUtils
.
isTrue
(
Config
.
logLevel
().
audit
().
enable
()))
{
auditLog
();
...
...
@@ -70,7 +73,24 @@ public class ResourceFactory {
processPlatformExecutors
();
}
private
static
void
node
(
ScanResult
sr
)
throws
Exception
{
private
static
Path
[]
unzipCustomWar
()
throws
Exception
{
FileUtils
.
cleanDirectory
(
Config
.
dir_local_temp_custom
(
true
));
List
<
String
>
list
=
new
ArrayList
<>();
for
(
String
str
:
Config
.
dir_custom
(
true
).
list
(
new
WildcardFileFilter
(
"*"
+
PathTools
.
DOT_WAR
)))
{
list
.
add
(
FilenameUtils
.
getBaseName
(
str
));
}
list
=
ListTools
.
includesExcludesWildcard
(
list
,
Config
.
currentNode
().
getApplication
().
getIncludes
(),
Config
.
currentNode
().
getApplication
().
getExcludes
());
List
<
Path
>
paths
=
new
ArrayList
<>();
for
(
String
str
:
list
)
{
Path
path
=
Paths
.
get
(
Config
.
dir_custom
().
toString
(),
str
+
PathTools
.
DOT_WAR
);
JarTools
.
unjar
(
path
,
""
,
Config
.
dir_local_temp_custom
().
toPath
().
resolve
(
str
),
true
);
paths
.
add
(
Config
.
dir_local_temp_custom
().
toPath
().
resolve
(
str
).
resolve
(
PathTools
.
WEB_INF_CLASSES
));
}
return
paths
.
toArray
(
new
Path
[
paths
.
size
()]);
}
private
static
void
node
(
ClassLoader
classLoader
,
ScanResult
sr
)
throws
Exception
{
LinkedBlockingQueue
<
JsonElement
>
eventQueue
=
new
LinkedBlockingQueue
<>();
EventQueueExecutor
eventQueueExecutor
=
new
EventQueueExecutor
(
eventQueue
);
eventQueueExecutor
.
start
();
...
...
@@ -84,6 +104,34 @@ public class ResourceFactory {
new
Resource
(
Config
.
RESOURCE_NODE_CENTERSPRIMARYSSLENABLE
,
entry
.
getValue
().
getSslEnable
());
}
private
static
void
containerEntityNames
(
ClassLoader
classLoader
,
ScanResult
sr
)
throws
Exception
{
List
<
String
>
list
=
new
ArrayList
<>();
for
(
ClassInfo
info
:
sr
.
getClassesWithAnnotation
(
ContainerEntity
.
class
.
getName
()))
{
list
.
add
(
info
.
getName
());
}
list
=
ListTools
.
trim
(
list
,
true
,
true
);
new
Resource
(
Config
.
RESOURCE_CONTAINERENTITYNAMES
,
ListUtils
.
unmodifiableList
(
list
));
}
private
static
void
stroageContainerEntityNames
(
ClassLoader
classLoader
,
ScanResult
sr
)
throws
Exception
{
List
<
String
>
list
=
new
ArrayList
<>();
for
(
ClassInfo
info
:
sr
.
getClassesWithAnnotation
(
Storage
.
class
.
getName
()))
{
list
.
add
(
info
.
getName
());
}
list
=
ListTools
.
trim
(
list
,
true
,
true
);
new
Resource
(
Config
.
RESOURCE_STORAGECONTAINERENTITYNAMES
,
ListUtils
.
unmodifiableList
(
list
));
}
private
static
void
containerEntities
(
ClassLoader
classLoader
,
ScanResult
sr
)
throws
Exception
{
Map
<
String
,
List
<
String
>>
map
=
new
TreeMap
<>();
for
(
ClassInfo
info
:
sr
.
getClassesWithAnnotation
(
Module
.
class
.
getName
()))
{
Class
<?>
cls
=
classLoader
.
loadClass
(
info
.
getName
());
List
<
String
>
os
=
ListTools
.
toList
(
cls
.
getAnnotation
(
Module
.
class
).
containerEntities
());
map
.
put
(
info
.
getName
(),
ListUtils
.
unmodifiableList
(
os
));
}
new
Resource
(
Config
.
RESOURCE_CONTAINERENTITIES
,
MapUtils
.
unmodifiableMap
(
map
));
}
private
static
void
external
()
throws
Exception
{
external_druid_c3p0
();
}
...
...
@@ -146,34 +194,6 @@ public class ResourceFactory {
}
}
private
static
void
containerEntityNames
(
ScanResult
sr
)
throws
Exception
{
List
<
String
>
list
=
new
ArrayList
<>();
for
(
ClassInfo
info
:
sr
.
getClassesWithAnnotation
(
ContainerEntity
.
class
.
getName
()))
{
list
.
add
(
info
.
getName
());
}
list
=
ListTools
.
trim
(
list
,
true
,
true
);
new
Resource
(
Config
.
RESOURCE_CONTAINERENTITYNAMES
,
ListUtils
.
unmodifiableList
(
list
));
}
private
static
void
stroageContainerEntityNames
(
ScanResult
sr
)
throws
Exception
{
List
<
String
>
list
=
new
ArrayList
<>();
for
(
ClassInfo
info
:
sr
.
getClassesWithAnnotation
(
Storage
.
class
.
getName
()))
{
list
.
add
(
info
.
getName
());
}
list
=
ListTools
.
trim
(
list
,
true
,
true
);
new
Resource
(
Config
.
RESOURCE_STORAGECONTAINERENTITYNAMES
,
ListUtils
.
unmodifiableList
(
list
));
}
private
static
void
containerEntities
(
ScanResult
sr
)
throws
Exception
{
Map
<
String
,
List
<
String
>>
map
=
new
TreeMap
<>();
for
(
ClassInfo
info
:
sr
.
getClassesWithAnnotation
(
Module
.
class
.
getName
()))
{
Class
<?>
cls
=
Class
.
forName
(
info
.
getName
());
List
<
String
>
os
=
ListTools
.
toList
(
cls
.
getAnnotation
(
Module
.
class
).
containerEntities
());
map
.
put
(
info
.
getName
(),
ListUtils
.
unmodifiableList
(
os
));
}
new
Resource
(
Config
.
RESOURCE_CONTAINERENTITIES
,
MapUtils
.
unmodifiableMap
(
map
));
}
private
static
void
auditLog
()
throws
Exception
{
RolloverFileOutputStream
rolloverFileOutputStream
=
new
RolloverFileOutputStream
(
Config
.
dir_logs
(
true
).
getAbsolutePath
()
+
"/yyyy_mm_dd.audit.log"
,
true
,
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录