提交 a24db4ff 编写于 作者: Y youyong205

Merge pull request #235 from sunryuan/biz

Biz
......@@ -5,12 +5,8 @@ import java.util.List;
import org.unidal.lookup.annotation.Inject;
import com.dianping.cat.analysis.AbstractMessageAnalyzer;
import com.dianping.cat.consumer.advanced.dal.UserAgent;
import com.dianping.cat.consumer.browser.model.entity.Browser;
import com.dianping.cat.consumer.browser.model.entity.BrowserReport;
import com.dianping.cat.consumer.browser.model.entity.BrowserVersion;
import com.dianping.cat.consumer.browser.model.entity.DomainDetail;
import com.dianping.cat.consumer.browser.model.entity.Os;
import com.dianping.cat.consumer.browser.model.entity.UserAgent;
import com.dianping.cat.message.Message;
import com.dianping.cat.message.Transaction;
import com.dianping.cat.message.spi.MessageTree;
......@@ -26,9 +22,25 @@ public class BrowserAnalyzer extends AbstractMessageAnalyzer<BrowserReport> {
@Inject
private UserAgentManager m_userAgentManager;
// public void removeMapItem(Map<String, Integer> map) {
//
// m_userAgents.put("domain", value );
//
// Map.Entry<String, Integer> itemRemoved = null;
//
// for (Map.Entry<String, Integer> item : map.entrySet()) {
// if (itemRemoved == null) {
// itemRemoved = item;
// } else if (item.getValue() < itemRemoved.getValue()) {
// itemRemoved = item;
// }
// }
// map.remove(itemRemoved.getKey());
// }
@Override
public void doCheckpoint(boolean atEnd) {
m_userAgentManager.storeResult();
// m_userAgentManager.storeResult();
if (atEnd && !isLocalMode()) {
m_reportManager.storeHourlyReports(getStartTime(), StoragePolicy.FILE_AND_DB);
} else {
......@@ -115,24 +127,38 @@ public class BrowserAnalyzer extends AbstractMessageAnalyzer<BrowserReport> {
}
private void updateBrowserReport(BrowserReport report, String domain, String data) {
String agent = parseValue("Agent", data);
if (agent == null || agent.isEmpty()) {
m_logger.error("Can not get agent from url when browser analyze: " + data);
UserAgent userAgent = report.findOrCreateDomainDetail(domain).findOrCreateUserAgent(data);
int count = 0;
if(userAgent.getCount()!=null){
count = userAgent.getCount();
}
UserAgent userAgent = m_userAgentManager.parse(agent);
String browserName = userAgent.getBrowser();
String osName = userAgent.getOs();
String versionName = userAgent.getVersion();
DomainDetail detail = report.findOrCreateDomainDetail(domain);
Browser browser = detail.findOrCreateBrowser(browserName);
BrowserVersion version = browser.findOrCreateBrowserVersion(versionName);
Os os = detail.findOrCreateOs(osName);
browser.setCount(browser.getCount() + 1);
os.setCount(os.getCount() + 1);
version.setCount(version.getCount() + 1);
userAgent.setCount(count + 1);
}
// private void updateBrowserReport(BrowserReport report, String domain,
// String data) {
// String agent = parseValue("Agent", data);
//
// if (agent == null || agent.isEmpty()) {
// m_logger.error("Can not get agent from url when browser analyze: "
// + data);
// }
//
// UserAgent userAgent = m_userAgentManager.parse(agent);
// String browserName = userAgent.getBrowser();
// String osName = userAgent.getOs();
// String versionName = userAgent.getVersion();
// DomainDetail detail = report.findOrCreateDomainDetail(domain);
//
// Browser browser = detail.findOrCreateBrowser(browserName);
// BrowserVersion version = browser
// .findOrCreateBrowserVersion(versionName);
// Os os = detail.findOrCreateOs(osName);
//
// browser.setCount(browser.getCount() + 1);
// os.setCount(os.getCount() + 1);
// version.setCount(version.getCount() + 1);
// }
}
package com.dianping.cat.consumer.browser;
import com.dianping.cat.consumer.browser.model.entity.Browser;
import com.dianping.cat.consumer.browser.model.entity.BrowserReport;
import com.dianping.cat.consumer.browser.model.entity.BrowserVersion;
import com.dianping.cat.consumer.browser.model.entity.DomainDetail;
import com.dianping.cat.consumer.browser.model.entity.Os;
import com.dianping.cat.consumer.browser.model.entity.UserAgent;
import com.dianping.cat.consumer.browser.model.transform.DefaultMerger;
public class BrowserReportMerger extends DefaultMerger {
......@@ -14,22 +11,13 @@ public class BrowserReportMerger extends DefaultMerger {
}
@Override
protected void mergeDomainDetail(DomainDetail old, DomainDetail other) {
}
@Override
protected void mergeBrowser(Browser old, Browser browser) {
old.setCount(old.getCount() + browser.getCount());
}
@Override
protected void mergeOs(Os old, Os os) {
old.setCount(old.getCount() + os.getCount());
}
@Override
protected void mergeBrowserVersion(BrowserVersion old, BrowserVersion browserVersion) {
old.setCount(old.getCount() + browserVersion.getCount());
protected void mergeUserAgent(UserAgent old, UserAgent other) {
if (old.getCount() == null) {
old.setCount(0);
}
if (other.getCount() != null) {
old.setCount(old.getCount() + other.getCount());
}
}
@Override
......
......@@ -4,24 +4,13 @@
<attribute name="domain" value-type="String" />
<attribute name="startTime" value-type="Date" format="yyyy-MM-dd HH:mm:ss" />
<attribute name="endTime" value-type="Date" format="yyyy-MM-dd HH:mm:ss" />
<element name="domain" value-type="String" type="list" names="domains" />
<entity-ref name="domain-detail" type="list" names="domain-details" />
<entity-ref name="domain-detail" />
</entity>
<entity name="domain-detail">
<attribute name="id" value-type="String" />
<entity-ref name="browser" type="list" names="browsers" />
<entity-ref name="os" type="list" names="oses" />
<entity-ref name="user-agent" type="list" names="user-agents" />
</entity>
<entity name="browser">
<attribute name="id" value-type="String" />
<attribute name="count" value-type="int" />
<entity-ref name="browser-version" type="list" names="browser-versions" />
</entity>
<entity name="browser-version">
<attribute name="id" value-type="String" />
<attribute name="count" value-type="int" />
</entity>
<entity name="os">
<entity name="user-agent">
<attribute name="id" value-type="String" />
<attribute name="count" value-type="int" />
</entity>
......
<?xml version="1.0" encoding="UTF-8"?>
<model model-package="com.dianping.cat.consumer.browser.model" enable-merger="true" enable-sax-parser="true"
enable-base-visitor="true" enable-native-parser="true" enable-native-builder="true">
<entity name="browser-report" root="true">
<attribute name="domain" value-type="String" key="true"/>
enable-base-visitor="true" enable-native-parser="true" enable-native-builder="true">
<entity name="browser-report" root="true">
<attribute name="domain" value-type="String" key="true"/>
<attribute name="startTime" value-type="Date" format="yyyy-MM-dd HH:mm:ss" />
<attribute name="endTime" value-type="Date" format="yyyy-MM-dd HH:mm:ss" />
<element name="domain" value-type="String" type="set" names="domain-names" />
<entity-ref name="domain-detail" type="map" names="domain-details" method-find-or-create="true"/>
</entity>
<entity name="domain-detail">
<attribute name="id" value-type="String" key="true" />
<entity-ref name="browser" type="map" names="browsers" method-find-or-create="true" />
<entity-ref name="os" type="map" names="oses" method-find-or-create="true" />
</entity>
<entity name="browser">
<attribute name="id" value-type="String" key="true" />
<attribute name="count" value-type="long" primitive="true" />
<entity-ref name="browser-version" type="map" names="browser-versions" method-find-or-create="true" />
</entity>
<entity name="browser-version">
<attribute name="id" value-type="String" key="true"/>
<attribute name="count" value-type="long" primitive="true" />
<attribute name="id" value-type="String" key="true" />
<entity-ref name="user-agent" type="map" names="user-agents" method-find-or-create="true"/>
</entity>
<entity name="os">
<entity name="user-agent">
<attribute name="id" value-type="String" key="true" />
<attribute name="count" value-type="long" primitive="true" />
<attribute name="count" value-type="int" />
</entity>
</model>
......@@ -101,4 +101,12 @@ public class BrowserAnalyzerTest extends ComponentTestCase {
return tree;
}
//
// @Test
// public void testRemoveMapItem() {
// Map<String, Integer> map = new HashMap<String, Integer>();
// map.put("test1", 1);
// map.put("test2", 2);
// m_analyzer.removeMapItem(map);
// }
}
<?xml version="1.0" encoding="utf-8"?>
<browser-report domain="Cat" startTime="2012-01-01 00:00:00" endTime="2012-01-01 00:59:59">
<domain>group</domain>
<domain-detail id="group">
<browser id="Chrome" count="100">
<browser-version id="14.0.802.30" count="50"/>
<browser-version id="28.0.1500.72" count="50"/>
</browser>
<os id="Windows NT 5.1" count="50"/>
<os id="Windows NT 6.1" count="50"/>
<user-agent id="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" count="100"/>
<user-agent id="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1650.63 Safari/537.36" count="99"/>
</domain-detail>
</browser-report>
<?xml version="1.0" encoding="UTF-8"?>
<browser-report domain="Cat" startTime="2012-07-05 00:00:00"
endTime="2012-07-05 00:59:59">
<domain></domain>
<domain></domain>
<domain-detail id="ShopWeb">
<browser id="Chrome" count="20000">
<browser-version count="1000">5.1</browser-version>
<browser-version count="2000">5.2</browser-version>
</browser>
<browser id="FireFox" count="20000">
<browser-version count="1000">5.1</browser-version>
<browser-version count="2000">5.2</browser-version>
</browser>
<os id="windows" count="10000"></os>
<os id="linux" count="10000"></os>
</domain-detail>
<domain-detail></domain-detail>
<browser-report domain="Cat" startTime="2012-01-01 00:00:00" endTime="2012-01-01 00:59:59">
<domain>group</domain>
<domain-detail id="group">
<user-agent id="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36" count="100"/>
<user-agent id="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1650.63 Safari/537.36" count="99"/>
</domain-detail>
</browser-report>
......@@ -19,6 +19,9 @@ public class DataExtractorImpl implements DataExtractor {
int length = values.length;
m_step = intervalCalculate(length);
int size = length / m_step;
if (size * m_step < length) {
size++;
}
double[] result = new double[size];
for (int i = 0; i < length; i = i + m_step) {
......
......@@ -10,7 +10,6 @@ import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.servlet.ServletException;
......@@ -25,12 +24,6 @@ import org.unidal.web.mvc.annotation.PayloadMeta;
import com.dianping.cat.Cat;
import com.dianping.cat.Constants;
import com.dianping.cat.consumer.browser.BrowserAnalyzer;
import com.dianping.cat.consumer.browser.model.entity.Browser;
import com.dianping.cat.consumer.browser.model.entity.BrowserReport;
import com.dianping.cat.consumer.browser.model.entity.BrowserVersion;
import com.dianping.cat.consumer.browser.model.entity.DomainDetail;
import com.dianping.cat.consumer.browser.model.entity.Os;
import com.dianping.cat.core.dal.Project;
import com.dianping.cat.core.dal.ProjectDao;
import com.dianping.cat.core.dal.ProjectEntity;
......@@ -50,8 +43,6 @@ import com.dianping.cat.home.service.entity.ServiceReport;
import com.dianping.cat.home.utilization.entity.UtilizationReport;
import com.dianping.cat.report.ReportPage;
import com.dianping.cat.report.page.PayloadNormalizer;
import com.dianping.cat.report.page.PieChart;
import com.dianping.cat.report.page.PieChart.Item;
import com.dianping.cat.report.service.ReportService;
import com.dianping.cat.report.task.heavy.HeavyReportMerger.ServiceComparator;
import com.dianping.cat.report.task.heavy.HeavyReportMerger.UrlComparator;
......@@ -59,6 +50,95 @@ import com.dianping.cat.system.config.BugConfigManager;
import com.dianping.cat.system.config.UtilizationConfigManager;
public class Handler implements PageHandler<Context> {
public class BugReportVisitor extends BaseVisitor {
private Domain m_currentDomain;
private Map<String, ErrorStatis> m_errors = new HashMap<String, ErrorStatis>();
public ErrorStatis findOrCreateErrorStatis(String productLine) {
ErrorStatis statis = m_errors.get(productLine);
if (statis == null) {
statis = new ErrorStatis();
m_errors.put(productLine, statis);
}
return statis;
}
public Map<String, ErrorStatis> getErrors() {
return m_errors;
}
@Override
public void visitDomain(Domain domain) {
m_currentDomain = domain;
super.visitDomain(domain);
}
@Override
public void visitExceptionItem(ExceptionItem exceptionItem) {
String exception = exceptionItem.getId();
int count = exceptionItem.getCount();
Project project = findProjectByDomain(m_currentDomain.getId());
if (project != null) {
String productLine = project.getProjectLine();
String department = project.getDepartment();
ErrorStatis statis = findOrCreateErrorStatis(productLine);
statis.setDepartment(department);
statis.setProductLine(productLine);
m_currentDomain.setDepartment(department);
m_currentDomain.setProductLine(productLine);
Map<String, ExceptionItem> items = null;
if (isBug(m_currentDomain.getId(), exception)) {
items = statis.getBugs();
} else {
items = statis.getExceptions();
}
ExceptionItem item = items.get(exception);
if (item == null) {
item = new ExceptionItem(exception);
item.setCount(count);
item.getMessages().addAll(exceptionItem.getMessages());
items.put(exception, item);
} else {
List<String> messages = item.getMessages();
item.setCount(item.getCount() + count);
messages.addAll(exceptionItem.getMessages());
if (messages.size() > 10) {
messages = messages.subList(0, 10);
}
}
}
}
}
public class ClearBugReport extends BaseVisitor {
@Override
public void visitDomain(Domain domain) {
String domainName = domain.getId();
Set<String> removes = new HashSet<String>();
Map<String, ExceptionItem> items = domain.getExceptionItems();
for (ExceptionItem item : items.values()) {
if (!isBug(domainName, item.getId())) {
removes.add(item.getId());
}
}
for (String remove : removes) {
items.remove(remove);
}
}
}
@Inject
private JspViewer m_jspViewer;
......@@ -77,34 +157,34 @@ public class Handler implements PageHandler<Context> {
@Inject
private PayloadNormalizer m_normalizePayload;
private String buildBrowserChart(Map<String, Browser> map) {
PieChart chart = new PieChart();
List<Item> items = new ArrayList<Item>();
for (Entry<String, Browser> entry : map.entrySet()) {
String key = entry.getKey();
Browser value = entry.getValue();
for (Entry<String, BrowserVersion> versionEntry : value.getBrowserVersions().entrySet()) {
String title = key + " " + versionEntry.getKey();
long count = versionEntry.getValue().getCount();
items.add(new Item().setTitle(title).setNumber(count));
}
}
chart.addItems(items);
return chart.getJsonString();
}
private void buildBrowserInfo(Model model, Payload payload) {
BrowserReport report = queryBrowserReport(payload);
model.setBrowserReport(report);
DomainDetail detail = report.findDomainDetail(payload.getDomain());
if (detail != null) {
model.setBrowserChart(buildBrowserChart(detail.getBrowsers()));
model.setOsChart(buildOsChart(detail.getOses()));
}
}
// private String buildBrowserChart(Map<String, Browser> map) {
// PieChart chart = new PieChart();
// List<Item> items = new ArrayList<Item>();
//
// for (Entry<String, Browser> entry : map.entrySet()) {
// String key = entry.getKey();
// Browser value = entry.getValue();
// for (Entry<String, BrowserVersion> versionEntry : value.getBrowserVersions().entrySet()) {
// String title = key + " " + versionEntry.getKey();
// long count = versionEntry.getValue().getCount();
// items.add(new Item().setTitle(title).setNumber(count));
// }
// }
// chart.addItems(items);
// return chart.getJsonString();
// }
//
// private void buildBrowserInfo(Model model, Payload payload) {
// BrowserReport report = queryBrowserReport(payload);
// model.setBrowserReport(report);
// DomainDetail detail = report.findDomainDetail(payload.getDomain());
//
// if (detail != null) {
// model.setBrowserChart(buildBrowserChart(detail.getBrowsers()));
// model.setOsChart(buildOsChart(detail.getOses()));
// }
//
// }
private void buildBugInfo(Model model, Payload payload) {
BugReport bugReport = queryBugReport(payload);
......@@ -128,19 +208,19 @@ public class Handler implements PageHandler<Context> {
buildSortedHeavyInfo(model, heavyReport);
}
private String buildOsChart(Map<String, Os> map) {
PieChart chart = new PieChart();
List<Item> items = new ArrayList<Item>();
for (Entry<String, Os> entry : map.entrySet()) {
String title = entry.getKey();
Os value = entry.getValue();
long count = value.getCount();
items.add(new Item().setTitle(title).setNumber(count));
}
chart.addItems(items);
return chart.getJsonString();
}
// private String buildOsChart(Map<String, Os> map) {
// PieChart chart = new PieChart();
// List<Item> items = new ArrayList<Item>();
//
// for (Entry<String, Os> entry : map.entrySet()) {
// String title = entry.getKey();
// Os value = entry.getValue();
// long count = value.getCount();
// items.add(new Item().setTitle(title).setNumber(count));
// }
// chart.addItems(items);
// return chart.getJsonString();
// }
private void buildServiceInfo(Model model, Payload payload) {
ServiceReport serviceReport = queryServiceReport(payload);
......@@ -266,10 +346,10 @@ public class Handler implements PageHandler<Context> {
case UTILIZATION_HISTORY_REPORT:
buildUtilizationInfo(model, payload);
break;
case BROWSER_REPORT:
case BROWSER_HISTORY_REPORT:
buildBrowserInfo(model, payload);
break;
// case BROWSER_REPORT:
// case BROWSER_HISTORY_REPORT:
// buildBrowserInfo(model, payload);
// break;
}
model.setPage(ReportPage.STATISTICS);
m_jspViewer.view(ctx, model);
......@@ -281,19 +361,19 @@ public class Handler implements PageHandler<Context> {
return !bugConfig.contains(exception);
}
private BrowserReport queryBrowserReport(Payload payload) {
Pair<Date, Date> pair = queryStartEndTime(payload);
Date start = pair.getKey();
Date end = pair.getValue();
BrowserReport report = m_reportService.queryBrowserReport("Cat", start, end);
Set<String> domains = m_reportService.queryAllDomainNames(start, end, BrowserAnalyzer.ID);
Set<String> domainNames = report.getDomainNames();
report.setStartTime(start);
report.setEndTime(end);
domainNames.addAll(domains);
return report;
}
// private BrowserReport queryBrowserReport(Payload payload) {
// Pair<Date, Date> pair = queryStartEndTime(payload);
// Date start = pair.getKey();
// Date end = pair.getValue();
// BrowserReport report = m_reportService.queryBrowserReport("Cat", start, end);
// Set<String> domains = m_reportService.queryAllDomainNames(start, end, BrowserAnalyzer.ID);
// Set<String> domainNames = report.getDomainNames();
//
// report.setStartTime(start);
// report.setEndTime(end);
// domainNames.addAll(domains);
// return report;
// }
private BugReport queryBugReport(Payload payload) {
Pair<Date, Date> pair = queryStartEndTime(payload);
......@@ -442,94 +522,5 @@ public class Handler implements PageHandler<Context> {
temp.setExceptions(MapUtils.sortMap(exceptions, compator));
}
return errors;
}
public class BugReportVisitor extends BaseVisitor {
private Domain m_currentDomain;
private Map<String, ErrorStatis> m_errors = new HashMap<String, ErrorStatis>();
public ErrorStatis findOrCreateErrorStatis(String productLine) {
ErrorStatis statis = m_errors.get(productLine);
if (statis == null) {
statis = new ErrorStatis();
m_errors.put(productLine, statis);
}
return statis;
}
public Map<String, ErrorStatis> getErrors() {
return m_errors;
}
@Override
public void visitDomain(Domain domain) {
m_currentDomain = domain;
super.visitDomain(domain);
}
@Override
public void visitExceptionItem(ExceptionItem exceptionItem) {
String exception = exceptionItem.getId();
int count = exceptionItem.getCount();
Project project = findProjectByDomain(m_currentDomain.getId());
if (project != null) {
String productLine = project.getProjectLine();
String department = project.getDepartment();
ErrorStatis statis = findOrCreateErrorStatis(productLine);
statis.setDepartment(department);
statis.setProductLine(productLine);
m_currentDomain.setDepartment(department);
m_currentDomain.setProductLine(productLine);
Map<String, ExceptionItem> items = null;
if (isBug(m_currentDomain.getId(), exception)) {
items = statis.getBugs();
} else {
items = statis.getExceptions();
}
ExceptionItem item = items.get(exception);
if (item == null) {
item = new ExceptionItem(exception);
item.setCount(count);
item.getMessages().addAll(exceptionItem.getMessages());
items.put(exception, item);
} else {
List<String> messages = item.getMessages();
item.setCount(item.getCount() + count);
messages.addAll(exceptionItem.getMessages());
if (messages.size() > 10) {
messages = messages.subList(0, 10);
}
}
}
}
}
public class ClearBugReport extends BaseVisitor {
@Override
public void visitDomain(Domain domain) {
String domainName = domain.getId();
Set<String> removes = new HashSet<String>();
Map<String, ExceptionItem> items = domain.getExceptionItems();
for (ExceptionItem item : items.values()) {
if (!isBug(domainName, item.getId())) {
removes.add(item.getId());
}
}
for (String remove : removes) {
items.remove(remove);
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<browser-report domain="Cat" startTime="2012-01-01 00:00:00" endTime="2012-01-01 00:59:59">
<domain>group</domain>
<domain-detail id="group">
<browser id="Chrome" count="100">
<browser-version id="14.0.802.30" count="50"/>
<browser-version id="28.0.1500.72" count="50"/>
</browser>
<os id="Windows NT 5.1" count="50"/>
<os id="Windows NT 6.1" count="50"/>
</domain-detail>
</browser-report>
......@@ -383,3 +383,13 @@ CREATE TABLE `baseline` (
KEY `ix_indexkey_reportperiod` (`index_key`,`report_period`),
KEY `ix_reportperiod` (`report_period`)
) ENGINE=InnoDB AUTO_INCREMENT=5062 DEFAULT CHARSET=utf8;
CREATE TABLE `user_agent` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`userAgent` varchar(200) NOT NULL DEFAULT '',
`browser` varchar(100) DEFAULT '',
`version` varchar(100) DEFAULT '',
`os` varchar(100) DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `userAgent` (`userAgent`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
......@@ -395,10 +395,17 @@ CREATE TABLE `baseline` (
PRIMARY KEY (`id`),
KEY `ix_indexkey_reportperiod` (`index_key`,`report_period`),
KEY `ix_reportperiod` (`report_period`)
) ENGINE=InnoDB AUTO_INCREMENT=5062 DEFAULT CHARSET=utf8
) ENGINE=InnoDB AUTO_INCREMENT=5062 DEFAULT CHARSET=utf8;
CREATE TABLE `user_agent` (
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
`userAgent` varchar(200) NOT NULL DEFAULT '',
`browser` varchar(100) DEFAULT '',
`version` varchar(100) DEFAULT '',
`os` varchar(100) DEFAULT '',
PRIMARY KEY (`id`),
UNIQUE KEY `userAgent` (`userAgent`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册