汽服系統(tǒng)

汽服系統(tǒng)主要包括了門店系統(tǒng)、集團(tuán)系統(tǒng)急前、運(yùn)營(yíng)系統(tǒng)、員工Pad這幾個(gè)部分瀑构。

交互流程

  • 公共倉(cāng)庫(kù)提供代碼的CRUD操作裆针。包括了常用工具類(util),模型對(duì)象(domain)检碗,服務(wù)類(service)据块。如果出現(xiàn)業(yè)務(wù)異常,應(yīng)該在service拋出serviceException
  • 運(yùn)營(yíng)后臺(tái)主要是給運(yùn)營(yíng)人員使用的
  • 門店系統(tǒng)跟集團(tuán)系統(tǒng)現(xiàn)在都在同一個(gè)工程中折剃,后面有可能會(huì)拆分另假。門店系統(tǒng)提供員工pad端的api
  • 汽服系統(tǒng)后面考慮做一個(gè)用戶微信,微信后臺(tái)目前待定
  • carme-qifu-operation域名:qfadm.car-me.com
    carme-qifu-admin域名:qfstore.car-me.com
    carme-qifu-group域名:qfgroup.car-me.com
  • 運(yùn)營(yíng)系統(tǒng)用的是之前運(yùn)營(yíng)平臺(tái)的那一套
    集團(tuán)系統(tǒng)和門店系統(tǒng)需要重新出頁(yè)面怕犁,做版式

FAQ

  • service為什么不采取接口的形式边篮,而是直接實(shí)現(xiàn)類
    用接口實(shí)現(xiàn)類的方式的好處主要是可以進(jìn)行多實(shí)現(xiàn)己莺,但是目前系統(tǒng)service跟dao只會(huì)有一個(gè)實(shí)現(xiàn)類,所以沒(méi)有使用接口的方式戈轿,這樣相對(duì)來(lái)說(shuō)簡(jiǎn)單凌受,而且減少定義無(wú)用的接口類。如果要對(duì)外暴露接口思杯,可以在carme-qifu-common上面加一層胜蛉,然后用dubbo的方式對(duì)外提供接口。

系統(tǒng)邊界

  • 對(duì)外暴露的接口需要做接口的參數(shù)校驗(yàn)

技術(shù)框架

數(shù)據(jù)庫(kù)設(shè)計(jì)

1色乾、表需要帶一個(gè)前綴誊册。例如:內(nèi)容管理(c_)、資源管理(r_)暖璧、設(shè)備管理(f_)案怯、站點(diǎn)管理(w_)
2、相同功能的字段需要放在一起澎办。例如 order_state,order_price有關(guān)訂單的需要放在一起
3嘲碱、每張表中需要有is_delete、created_at局蚀、created_by麦锯、changed_at、changed_by五個(gè)字段
4至会、有展示列表的數(shù)據(jù)需要在表中做冗余字段离咐。例如:seller_name
5谱俭、表邏輯外鍵需要帶前綴奉件。例如:p_seller_id
6、有關(guān)定時(shí)任務(wù)的字段需要加前綴昆著。例如:cron_state
7县貌、所有的邏輯外鍵需要加上索引
8、字段盡量不要設(shè)置為可空
9凑懂、表注釋要完整煤痕,涉及字典的字段需要在注釋上標(biāo)明dict_type_value。例如:退款狀態(tài)(car_tkzt )
10接谨、索引命名規(guī)范為:idx_(字段名) ** 例如:idx_model_id
11摆碉、 建表的時(shí)候id都用
bigint(20). 字符串都用varchar(255). 大文本用text**
數(shù)據(jù)庫(kù)腦圖地址:http://naotu.baidu.com/file/fd2d8817fe0c44a68ad24883e84bcf30?token=7e3f0cccf1a9be7f

編碼規(guī)范

1、代碼中不允許出現(xiàn)magic number脓豪,應(yīng)該定義為常量

錯(cuò)誤:test.setStatus(1);
正確:test.setStatus(CodeConstant.SUCCESS)

2巷帝、用AccountUtil來(lái)獲取當(dāng)前登錄用戶信息

Account account = AccountUtil.getAccountInfo();
String id = account.getId();
String operateName = account.getName();

3、涉及到業(yè)務(wù)異常的應(yīng)該在service中拋出serviceException扫夜,涉及權(quán)限等其他的異常請(qǐng)?jiān)贑ontroller拋出

Service:
if(car==null){
     throw new ServiceException("該類型的卡沒(méi)有數(shù)據(jù),請(qǐng)檢查傳入的id是否正確");
}
Controller:
if(CodeConstant.isAdmin==user.getIsAdmin){
    throw new ManagerException("該用戶沒(méi)有該權(quán)限");
}

如果在底層拋出業(yè)務(wù)異常楞泼,在controller請(qǐng)catch掉驰徊,如果不catch ,默認(rèn)認(rèn)為為系統(tǒng)異常

controller
type1:正常請(qǐng)求
try{
  // do something
}catch(ServiceException e){
    return ControllerHelper.showMsg(map.e.getMessage,'/list.do');
}
type2:ajax請(qǐng)求
try{
  // do something
}catch(ServiceException e){
    AjaxResult res = new AjaxResult();
    res.setCode(1211);
    res.setMessage(e.getMessage);
    return  res;
}

有些人直接把底層的異常直接catch掉堕阔,然后拋出ServiceException

錯(cuò)誤:
try{
  //do something
}catch(Exception e){
    e.printStackTrace();
    throw new ServiceException("xxx失敗");
}

此種寫法錯(cuò)在兩點(diǎn):

  • e.printStackTrace 打印堆棧到控制臺(tái)棍厂,大家都知道,服務(wù)器的IO資源是非常寶貴的超陆,如果有很多system.out.print()造成了服務(wù)器的io資源被占用牺弹,嚴(yán)重的會(huì)極大影響服務(wù)器性能。
  • 既然已經(jīng)catch了異常时呀,又拋出一個(gè)新的異常(把大異常轉(zhuǎn)成了小異常)例驹,但是原始的異常沒(méi)有記錄,而且catch的是Exception的異常退唠,容易把其他的異常也catch掉鹃锈,這樣造成很難排查到線上bug造成的原因。
    正確的做法
  try{
  }catch(NullPointException e){
    logger.error(e.getMessage(),e);  
  }

注意:因?yàn)槲以谌值漠惓L幚碇杏涗浟巳罩厩圃ぃ訰untimeException可以不用catch
4屎债、在前端調(diào)用webUtil來(lái)獲取css,js垢油,host盆驹,img的域名

js域名 :${webUtil.getJsDomain('/js/operate.js')}
img域名:${webUtil.getImgDomain('/img/dd.png')}
css域名:${webUtil.getCssDomain('/css/ddd.css')}

5、前端js綁定事件時(shí)需要注意不要直接在class的選擇器上綁定(前端可能會(huì)修改樣式)滩愁,常用做法
1) 給元素添加一個(gè)屬性nc_type(推薦)

html
<a href="javascript:void(0);" nc_type="search" />
js
$("a[nc_type="search"]").on("click",function(){
    //do something
});
好處:前端更改樣式不會(huì)影響js事件

2)需要綁定的樣式加一個(gè)js前綴

html
<a href="javascript:void(0);" class="js-test btn"/>
js
$("a.js-test").on("click",function(){
    //do something
});
此種做法不太推薦躯喇,但是如果你想在class上面綁定事件,要加上js前綴硝枉,這樣前端改樣式的時(shí)候就會(huì)注意不會(huì)用有js前綴的樣式來(lái)寫css

6廉丽、之前由于因?yàn)閒orm和dto沒(méi)有在之前定下規(guī)則,導(dǎo)致了很多的問(wèn)題妻味,故指定命名規(guī)范和使用規(guī)則正压。form跟dto的命名規(guī)范和使用規(guī)則如下:
form分為查詢form跟新增編輯form。
1) 查詢form(模塊名+SearchForm,如:UserSearchForm)可復(fù)用责球。復(fù)用時(shí)具體的變量命名規(guī)則:

 1焦履、日期的開(kāi)始結(jié)束時(shí)間(字段名+Begin/End),例如:createdAtBegin雏逾、createdEnd
 2嘉裤、關(guān)鍵字(模糊查詢時(shí)使用,查詢字段+And+查詢字段+Key)栖博,例如:關(guān)鍵字(用戶名/手機(jī)號(hào)碼)->userNameAndPhoneKey
例如:
if (StringUtils.isNotEmpty(deviceSearchForm.getMeiAndSiteNameKey())) {    
     Predicate p1 = cb.like(r.get("mei").as(String.class),        "%" + deviceSearchForm.getMeiAndSiteNameKey() + "%");   
     Predicate p2 = cb.like(r.get("siteName").as(String.class),        "%" + deviceSearchForm.getMeiAndSiteNameKey() + "%");    
     predicate.getExpressions().add(cb.or(p1, p2));
}

2)新增/修改 form 不可復(fù)用屑宠。命名規(guī)則:

1、新增(模塊+動(dòng)作+AddForm)笛匙,例如:UserAddForm侨把,UserStep1AddForm(新增商家第一步)犀变、UserStep2AddForm(新增商家第二步)...
2、編輯(模塊+動(dòng)作+EditForm)秋柄,例如:UserEditForm获枝、UserStep1EditForm、UserStep2EditForm...
新增和修改form需要加上注解骇笔,進(jìn)行服務(wù)端校驗(yàn)

dto的命名規(guī)范同form
7省店、現(xiàn)在項(xiàng)目有三個(gè)日志文件,分別是root笨触、request懦傍、sql



可以根據(jù)自己的需要添加logger。例如:跟金額結(jié)算有關(guān)的可以增加一個(gè)bill的logger
8芦劣、關(guān)于常量使用
目前系統(tǒng)的常量分為三種粗俱,分別是公共常量(common constant)、業(yè)務(wù)常量(business constant)虚吟、系統(tǒng)常量(system constant)

  • 公共常量寸认。主要是一些常用不帶有業(yè)務(wù)意義的常量
//公共常量
private static final int FLAG_TRUE=1;    //狀態(tài)標(biāo)識(shí) true
private static final int FLAG_FALSE=0;   //狀態(tài)標(biāo)識(shí) false
private static final int PAGE_SIZE=5;     //默認(rèn)分頁(yè)大小

公共常量放在carme-qifu-common中的Constant類中,正常情況下串慰,類似于是否刪除偏塞,是否付款這樣的簡(jiǎn)單的標(biāo)識(shí)位無(wú)需定義其他的常量,直接使用公共常量中的FLAG_TRUE和FLAG_FALSE

  • 業(yè)務(wù)常量邦鲫。主要跟業(yè)務(wù)狀態(tài)有關(guān)的一些常量
//業(yè)務(wù)常量
public static final String EVALUATION_TAG_STATUS_NORMAL     = "0"; // 評(píng)價(jià)標(biāo)簽表---狀態(tài):0灸叼,正常
public static final String EVALUATION_TAG_STATUS_SHIELD     = "1"; // 評(píng)價(jià)標(biāo)簽表---狀態(tài):1,屏蔽
  • 系統(tǒng)常量庆捺。主要跟系統(tǒng)的運(yùn)行有關(guān)的一些常量
/** * 編碼 */
public static final String I18N_ENCODIND = "UTF-8";
/** * 信息提示頁(yè)面 */
public static final String I18N_MSG      = "classpath:/i18n/messages";
public static final String I18N_VALIDATOR      = "classpath:/i18n/validator";

業(yè)務(wù)常量跟公共常量放置在carme-qifu-common的BusinessConstant.java和Constant.java類中古今,系統(tǒng)常量放在對(duì)應(yīng)項(xiàng)目的constant包中
9、攔截規(guī)則
因?yàn)閟pring boot對(duì)(urlPattern=/)的請(qǐng)求進(jìn)行攔截疼燥,所以大家的所有請(qǐng)求都會(huì)進(jìn)入spring boot中沧卢,所以不會(huì)出現(xiàn)自己定義servlet處理請(qǐng)求的情況。為了之后能夠做動(dòng)態(tài)請(qǐng)求做處理醉者,故增加后綴方便日后拓展。
web請(qǐng)求攔截規(guī)則:

  • 正常請(qǐng)求披诗。后綴加上.do
  • ajax異步請(qǐng)求撬即。如果需要返回json數(shù)據(jù)加上.json后綴,如果需要返回.xml數(shù)據(jù)呈队,加上.xml后綴
    api請(qǐng)求攔截規(guī)則:
  • 如果路徑規(guī)則符合(/rest/**)剥槐,為暴露出去的api
  • 如果需要返回json數(shù)據(jù)加上.json后綴,如果需要返回.xml數(shù)據(jù)宪摧,加上.xml后綴
    10粒竖、在velocity中使用常量類
    在velocity中需要進(jìn)行一些邏輯判斷颅崩,這個(gè)時(shí)候需要根據(jù)一些業(yè)務(wù)狀態(tài)進(jìn)行邏輯判斷
bad practice:
#if($item.modelType==1)    
     do something
#end
best practice:
#if($item.modelType==$fieldTool.EVALUATION_TAG_STATUS_NORMAL)    
     do something
#end

這個(gè)時(shí)候如對(duì)應(yīng)的業(yè)務(wù)狀態(tài)更改了(雖然比較少),這個(gè)時(shí)候就還需要更改velocity中的業(yè)務(wù)狀態(tài)判斷蕊苗,而且=1沿后,=2這種可讀性太差(今天寫了明天就忘了1,2代表什么了,還要去查對(duì)應(yīng)的字典朽砰,還是常量比較好理解
11尖滚、關(guān)于頁(yè)面復(fù)用
有的時(shí)候如果新增頁(yè)面和修改頁(yè)面基本一樣,這個(gè)時(shí)候我們就想了瞧柔,為什么既然頁(yè)面相同為什么我們不能復(fù)用了漆弄?其實(shí)這個(gè)時(shí)候新增頁(yè)面和修改頁(yè)面是一種很好的方案:

  • 如果頁(yè)面更改了,只要更改一個(gè)頁(yè)面即可
  • 提高了開(kāi)發(fā)效率造锅,減少了維護(hù)的工作量
    但是撼唾,關(guān)于復(fù)用的東西一定要謹(jǐn)慎,標(biāo)準(zhǔn)一定要制定好哥蔚,不然隨時(shí)可能因?yàn)橐粋€(gè)公共方法的修改影響多個(gè)頁(yè)面券坞。
    適用范圍:兩個(gè)頁(yè)面相差不大
    操作標(biāo)識(shí):opType
    類型常量:Constant.UPDATE,Constant.ADD
    頁(yè)面命名: xxx_input.vm 例如:card_input.vm
vm
<!--操作標(biāo)識(shí)-->
<input type="hidden" name="opType"  id="opType" value="${fieldTool.UPDATE}"/>
js
var opType = $("#opType").val();
if(opType=="update"){
    //回填值
    ...
   //js效果顯示
   ...
}
后臺(tái)
/** * 保存新增的企業(yè)信息 * * /
@RequestMapping("/input.do")
public String input(ModelMap modelMap, String opType) {
     if(Constant.UPDATE.equals(opType)){
         //do something
     }
     ...
}

注意:如果新增后修改頁(yè)面后面改變很大肺素,兩個(gè)頁(yè)面完全不同恨锚,不建議復(fù)用,具體要自己判斷下
12倍靡、關(guān)于createdBy猴伶、createdAt、changedBy塌西,changedAt
WHY:有人可能會(huì)疑惑每張表添加這幾個(gè)字段是干啥用的他挎,舉個(gè)例子,如果我想知道某個(gè)訂單是什么時(shí)候創(chuàng)建的捡需,誰(shuí)創(chuàng)建的(createdAt办桨,createdBy)。這個(gè)訂單是誰(shuí)審核的站辉,最后審核時(shí)間是什么時(shí)候(changedBy呢撞,changedAt 當(dāng)然這兩個(gè)字段不然說(shuō)明操作類型,如果需要知道具體的操作類型饰剥,需要添加相應(yīng)的操作記錄表)殊霞。從上面的例子可以看出,這四個(gè)字段在我們做數(shù)據(jù)統(tǒng)計(jì)和操作歷史的時(shí)候是非常有用的汰蓉。
HOW: 如果添加這幾個(gè)字段绷蹲,但是在具體操作的時(shí)候沒(méi)有更新值的話就會(huì)有問(wèn)題了。推薦做法

service
public void update(Card card ,String operaterName){
     Card flushData= cardDao.findOne(card.getId());
     //將頁(yè)面?zhèn)魅氲膶?duì)象屬性拷貝到flushData中
     BeanHelper.copyProperty(flushData,card);
     flushData.setChangedBy(operatorName);
     flushData.setChangedAt(DateUtil.getDate());
     cardDao.saveAndFush(flushData);
}
public void save(Card card,String operatorName){
     card.setCreatedBy(operatorName);
     card.setCreatedAt(DateUtil.getDate());
     cardDao.save(card);
}

直接在sevice參數(shù)添加一個(gè)operatorName,你不傳的話就沒(méi)辦法調(diào)用service祝钢,媽媽再也不要擔(dān)心我漏更新時(shí)間了比规,哈哈!拦英!

代碼生成工具

GitHub:https://github.com/hjm017/code-generator
generator.xml文件說(shuō)明

<!--生成的類的包路徑-->
<entry key="basePackage">com.carme</entry>
<!--生成文件目錄-->
<entry key="outputDir">output</entry>
<!--模板文件目錄-->
<entry key="templateDir">template</entry>
<!--需要去除的表的前綴-->
<entry key="prefix">c,i,p,tb</entry>
<!--生成的表-->
<entry key="tables">c_card</entry>
<!--是否生成數(shù)據(jù)源中所有的表-->
<entry key="allSwitch">false</entry>
<!--數(shù)據(jù)庫(kù)用戶名-->
<entry key="jdbc_username">qifuowner</entry>
<!--數(shù)據(jù)庫(kù)密碼-->
<entry key="jdbc_password">qifuowner_cte</entry>
<!--數(shù)據(jù)庫(kù)URL-->
<entry key="jdbc_url">jdbc:mysql://192.168.51.195:3306/qifudbd01?useUnicode=true&characterEncoding=UTF-8</entry>

使用方法:
Step1:配置數(shù)據(jù)源信息

<entry key="jdbc_username">用戶名</entry>
<entry key="jdbc_password">密碼</entry>
<entry key="jdbc_url">jdbc:mysql://數(shù)據(jù)庫(kù)地址:3306/數(shù)據(jù)庫(kù)名?useUnicode=true&characterEncoding=UTF-8</entry>

Step2:配置需要生成的表

<!--需要去除的表的前綴-->
<entry key="prefix">c,i,p,tb</entry>
<!--生成的表-->
<entry key="tables">c_card</entry>

Step3:運(yùn)行GeneratorServer.java
注意:如果在eclipse中GeneratorServer.java不被識(shí)別為java類蜒什,請(qǐng)將GeneratorServer.java類放入包路徑中


Druid監(jiān)控平臺(tái)

URL:http://qfadm.car-me.com/operation/druid/login.html
用戶名: admin
密碼: admin


代碼碎片

1、生成自增長(zhǎng)的序號(hào)
初始化

--創(chuàng)建序列表
CREATE TABLE tb_sequence (
  seq_name VARCHAR (50) NOT NULL,
  current_value VARCHAR(15) NOT NULL,
  len  INT NOT NULL,
  increment INT NOT NULL DEFAULT 1,
  PRIMARY KEY (seq_name)
) ;
--創(chuàng)建_nextval函數(shù)
DELIMITER //
CREATE FUNCTION _nextval(n VARCHAR(50)) RETURNS VARCHAR(15)   
BEGIN
DECLARE _cur VARCHAR(15);
DECLARE _len INT;
SET _cur=(SELECT current_value FROM tb_sequence WHERE seq_name= n FOR UPDATE);
SET _len=(SELECT len FROM tb_sequence WHERE seq_name = n FOR UPDATE);
IF LENGTH(_cur)>_len THEN SET _cur='1';END IF;
UPDATE tb_sequence SET current_value = _cur + increment WHERE seq_name=n ;
RETURN LPAD(_cur,_len,'0000000000000000000000000000000');
END;
//

說(shuō)明:生成的序列可以自定義長(zhǎng)度(設(shè)置len)龄章,如果超過(guò)設(shè)置的長(zhǎng)度將會(huì)重置為1
例如:生成3位自增長(zhǎng)序號(hào)

insert into tb_sequence values('bill_no',3,1);
select _nextval('bill_*no')

在程序中

Class Service{
   @Autowire
   private SeqHelper seqHelper;
   ...
   String no = seqHelper.getNo("Bill_no");//序列  
  ...
}

注意:獲取序列的操作需要放在事務(wù)中(在方法上添加@Transactional注解)吃谣,出現(xiàn)異常時(shí),回滾事務(wù)做裙,保證事務(wù)的ACID特性(如果在service中自己拋出業(yè)務(wù)異常岗憋,事務(wù)會(huì)自動(dòng)回滾)。
2锚贱、分頁(yè)(carme-qifu-operation)(例子:card/list.vm)
1)form格式

<form action="${webUtil.getHostDomain('/card/list.do')}" method="post" id="listForm">
   <!--此處添加隱藏參數(shù)-->
   <input type="hidden"/>
    ...
   #foreach($item in $page.pageItems)
    <tr>
      ...   
   </tr>
   #end
   <!--此處添加分頁(yè)bar-->
   #pager("listForm" "pageNo" $page)
</from>

2)marco 說(shuō)明

#pager("listForm" "pageNo" $page)
 1)listForm  : form表單id
 2)pageNo:分頁(yè)隱藏域的name
 3)$page :后臺(tái)設(shè)置的page參數(shù)

3)controller

@RequestMapping("/list.do")
public String index(ModelMap map, CardSearchForm cardSearchForm) {    
//分頁(yè)參數(shù)處理:1仔戈、初始化的時(shí)候pageNo=0->pageNo=1,傳入pageNo的時(shí)候,pageNo=pageNo-1  2拧廊、設(shè)置分頁(yè)的pageSize
ControllerHelper.doDealParam(cardSearchForm);    
Page<Card> page = cardService.findAll(cardSearchForm);   
 //將jpa返回的分頁(yè)集合包裝為view層需要的分頁(yè)對(duì)象   
 PageInfo<Card> pageInfo = ControllerHelper.getPageInfo(page);    
map.addAttribute("page", pageInfo);    
return "card/list";
}

分頁(yè)的大小默認(rèn)為5监徘,可以更改OperationConstant.PAGE_SIZE的值,更改分頁(yè)默認(rèn)大小
3吧碾、后臺(tái)參數(shù)檢驗(yàn)
在需要進(jìn)行的方法上添加@ParamCheck注解凰盔,使用的是hibernate validator
方式一: 參數(shù)中只有對(duì)象

@ResponseBody
@ParamCheck
@RequestMapping("/register.do")
public String register(UserRegisterForm userRegisterForm) {    
   return "";
}

方式二:參數(shù)中只有基本類型

@ResponseBody
@ParamCheck
@RequestMapping("/register.do")
public String register(@NotEmpty(message = "設(shè)備類型不能為空") 
@RequestParam(value = "facilityId", required = false) String facilityId,                       
@Digits(fraction = 6, integer = 3) 
@RequestParam(value = "longitude", required = false) Double longitude                     
) {    
   return "";
}

方式三:參數(shù)中既有基本類型,又有對(duì)象

@ResponseBody
@ParamCheck
@RequestMapping("/register.do")
public String register(UserRegisterForm userRegisterForm, @NotEmpty(message = "設(shè)備類型不能為空") 
@RequestParam(value = "facilityId", required = false) String facilityId,                       
@Digits(fraction = 6, integer = 3) 
@RequestParam(value = "longitude", required = false) Double longitude                     
) {    
   return "";
}

4倦春、使用Beancopier框架來(lái)做實(shí)體映射
在項(xiàng)目中户敬,經(jīng)常需要做beancopier的操作,經(jīng)過(guò)測(cè)試睁本,使用jdk的set/get效率最高尿庐,但是對(duì)于字段較多的情況下不太使用,增加了代碼量呢堰,經(jīng)過(guò)比較抄瑟,cglib的beancopier效率最高并且容易擴(kuò)展
我對(duì)這些工具做了一個(gè)對(duì)比:Copy一個(gè)簡(jiǎn)單Bean 1,000,000次,計(jì)算總耗時(shí)枉疼。比較結(jié)果如下:

1,000,000 round
jdk set/get takes 17ms
cglib takes 117ms
jodd takes 5309ms
dozer mapper takes 2336ms
apche beanutils takes 6264ms

故在carme-qifu-common中寫了一個(gè)基于cglib的BeanCopier類皮假。在類中使用

WarehouseAddForm addForm = new WarehouseAddForm();
addForm.setStoreNo("00023011034");
addForm.setName("卡咪汽服");
addForm.setAddress("越達(dá)巷");
addForm.setCreatedBy("hxq");
addForm.setPurpose("隨便");
SimpleBeanCopier copier = new SimpleBeanCopier(WarehouseAddForm.class, Warehouse.class);
Warehouse warehouse = (Warehouse) copier.copy(addForm);

由于cglib是使用修改字節(jié)碼的方式實(shí)現(xiàn)beancopier的,這種方式如果程序出錯(cuò)了將會(huì)很難定位異常位置
所以往衷,推薦使用orika的beancopier框架

productForm.setPrice(NumberUtil.toYuanLong(productForm.getPrice()).toString());
Product product = BeanMapper.map(productForm, Product.class);
Product temp = productService.findOne(product.getId());
product.setCreatedBy(temp.getCreatedBy());
product.setCreatedAt(temp.getCreatedAt());
product.setIsUse(temp.getIsUse());
product.setIsDelete(temp.getIsDelete());
product.setQrcode(temp.getQrcode());
product.setChangedBy(temp.getChangedBy());
product.setChangedAt(temp.getChangedAt());
productService.save(product);

5钞翔、前端ajax使用
由于在項(xiàng)目中使用了ajax請(qǐng)求,因此導(dǎo)致在ajax請(qǐng)求回調(diào)需要處理因?yàn)閏ookie失效等情況席舍,故按照AOP的思想,基于jquery編寫了http.js

define(["jquery"], function($) {
  return {
    //提交ajax請(qǐng)求
    post:function(option){
          $.ajax({
                type: 'POST',
                url: option.uri,
                data: option.data,
                dataType: "json",
                success: function(data) {
                    //cookie失效判斷
                  if (result.status != undefined && result.status == 4000) {
                      var redirectURL = result.redirectURL;
                      window.location.href =  redirectURL;
                  }

                  option.success(data);
                },
                error: function(data) {
                    layer.alert("ajax請(qǐng)求失敗",function(index){
                               layer.close(index);
                    });
                }
            });
    }
  }
});

需要使用http.js的時(shí)候需要在require.js中配置

require.config({
    baseUrl: getJsBase(),
    paths:{
        "bootstrap":"bootstrap/bootstrap",
        "bootbox":"bootstrap/bootbox.min",
        "html5shiv":"bootstrap/html5shiv.min",
        "respond":"bootstrap/respond.min",
        "http":"http",
         ...
   }
})

然后在頭部引入

;require(['domReady!', "http"], function( http) {

在js中使用

  http.post({
           uri:$("#addProductFragmentUri").val(),
           data:{
                     productId:drag.attr("data-id"),
                     contentId:$("#contentId").val(),
                     modelId:$("#modelId").val(),
                     sort:target.attr("data-flag")
            }
   });


6哮笆、在項(xiàng)目中進(jìn)行多表查詢
方式一:使用jdbc template来颤。優(yōu)點(diǎn):方便快捷汰扭,直接能夠看到sql
vo實(shí)體類

public class CardExtendVo  {    
private Long   cardId;    
private String companyNo;    
public String getCompanyNo() {  return companyNo;    }    
public void setCompanyNo(String companyNo) {       this.companyNo = companyNo;    } 
 public Long getCardId() {        return cardId;    }  
public void setCardId(Long cardId) {        this.cardId = cardId;    } }

service類

@Service
public class CardService {   
 @Autowired    
private CardDao cardDao;    
@Autowired    
private JdbcTemplate jdbcTemplate;
@Transactional(readOnly = true)
public List<CardExtendVo> getMyCardInfo(){    
String sql = "select t1.id as cardId,t2.company_no as companyNo from c_card t1,p_company t2 where t1.p_company_id=t2.id";   
 List<CardExtendVo> cardExtendVo = jdbcTemplate.query(sql,new BeanPropertyRowMapper(            CardExtendVo.class));    
return cardExtendVo;
}

test類

@Test
public void getMyCardInfo() {    
List<CardExtendVo> list = cardService.getMyCardInfo();    
for (CardExtendVo cardExtendVo :list){        
        System.out.println("card id:"+cardExtendVo.getCardId());
        System.out.println("company_no:"+cardExtendVo.getCompanyNo());    
}
}

注意:查詢出來(lái)的字段別名需要與實(shí)體類的字段一致,否則無(wú)法映射
方式二:使用spring data jpa 優(yōu)點(diǎn):編碼簡(jiǎn)單 缺點(diǎn):效率低
多表連接查詢稍微麻煩一些福铅,下面演示一下常見(jiàn)的1:M萝毛,順帶演示一下1:1
使用Criteria查詢實(shí)現(xiàn)1對(duì)多的查詢
Step1:首先要添加一個(gè)實(shí)體對(duì)象DepModel,并設(shè)置好UserModel和它的1對(duì)多關(guān)系滑黔,如下:

@Entity
@Table(name="tbl_user")
public class UserModel {
@Id
private Integer uuid;
private String name;
private Integer age;
@OneToMany(mappedBy = "um", fetch = FetchType. *LAZY*, cascade = {CascadeType. *ALL*})
private Set<DepModel> setDep;
//省略getter/setter
}
@Entity
@Table(name="tbl_dep")
public class DepModel {
@Id
private Integer uuid;
private String name;
@ManyToOne()
  @JoinColumn(name = "user_id", nullable = false)
//表示在tbl_dep里面有user_id的字段
private UserModel um = new UserModel();
//省略getter/setter
}

Step2:配置好Model及其關(guān)系后笆包,就可以在構(gòu)建Specification的時(shí)候使用了,示例如下:

Specification<UserModel> spec = new Specification<UserModel>() {
      public Predicate toPredicate(Root<UserModel> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
        Predicate p1 = cb.like(root.get("name").as(String.class), "%"+um.getName()+"%");
        Predicate p2 = cb.equal(root.get("uuid").as(Integer.class), um.getUuid());
        Predicate p3 = cb.gt(root.get("age").as(Integer.class), um.getAge());
        SetJoin<UserModel,DepModel> depJoin =root.join(root.getModel().getSet("setDep",DepModel.class) , JoinType.LEFT);
        Predicate p4 = cb.equal(depJoin.get("name").as(String.class), "ddd");//把Predicate應(yīng)用到CriteriaQuery去,因?yàn)檫€可以給CriteriaQuery添加其他的功能略荡,比如排序庵佣、分組啥的
       query.where(cb.and(cb.and(p3,cb.or(p1,p2)),p4));**
       //添加分組的功能
       query.orderBy(cb.desc(root.get("uuid").as(Integer.class)));
       return query.getRestriction();
}};

接下來(lái)看看使用Criteria查詢實(shí)現(xiàn)1:1的查詢
Step1:在UserModel中去掉setDep的屬性及其配置,然后添加如下的屬性和配置:

@OneToOne()
@JoinColumn(name = "depUuid")
private DepModel dep;
public DepModel getDep() { return dep;}
public void setDep(DepModel dep) {this.dep = dep; 
 }

Step2:在DepModel中um屬性上的注解配置去掉汛兜,換成如下的配置:

@OneToOne(mappedBy = "dep", fetch = FetchType. *EAGER*, cascade = {CascadeType. *ALL*})

Step3:在Specification實(shí)現(xiàn)中巴粪,把SetJoin的那句換成如下的語(yǔ)句:

Join<UserModel,DepModel> depJoin =
root.join(root.getModel().getSingularAttribute("dep",DepModel.class),JoinType.LEFT);
//root.join(“dep”,JoinType.LEFT); //這句話和上面一句的功能一樣,更簡(jiǎn)單

7粥谬、api接口規(guī)范
1)如果是Api接口的控制器需要在controller上面增加@ApiController
2)方法上面需要添加@ApiRequestBody的注解
例如:

/** * @author hjm * @Time 2016/5/1 20:21. */
@ApiController
public class UserEndpoint {    
      private static Logger logger = LoggerFactory.getLogger(UserEndpoint.class);    
      @Autowired    
      private UserService userService;    
      @RequestMapping(value = "rest/users", produces = MediaTypes.JSON_UTF_8)    
      public List<UserDto> getList() {        
           List<User> users = userService.getList();        
           if (false) {            
                   throw new ApiException("這是一個(gè)測(cè)試?yán)?, ErrorCode.INTERNAL_SERVER_ERROR);       
            }        
            return BeanMapper.mapList(users, UserDto.class);    
        }    
       @RequestMapping(value = "rest/user/{id}/modify", produces = MediaTypes.JSON_UTF_8)    
       public List<UserDto> getList(@ApiRequestBody UserDto userDto) {        
             List<User> users = userService.getList();       
            return BeanMapper.mapList(users, UserDto.class);    
       }
}

8肛根、使用js回填select

html
<select class="w150 beautyselect" name="workState" init-value="1">
...
</select>
js
$.each($(".beautyselect"),function(idx,element){
        var initValue = $(element).attr("init-value");
        $(element).find("option[value='"+initValue+"']").attr("selected",true);
}) ;

只需要在select中添加init-value即可回填
注意:需要在beautySelect渲染之前添加回填的代碼
9掸茅、消息提示頁(yè)面
如果A打開(kāi)一個(gè)列表頁(yè)面艇纺,不操作轧拄。這個(gè)時(shí)候筋夏,B也打開(kāi)了這個(gè)列表頁(yè)面透葛,并且刪除了這個(gè)列表的某條數(shù)據(jù)秽梅,這個(gè)時(shí)候如果A如果不刷新烁巫,直接點(diǎn)擊列表頁(yè)面的查看按鈕赖舟,那么就會(huì)出現(xiàn)異常巢寡,頁(yè)面也會(huì)顯示為500喉脖。
比較好的做法是在后臺(tái)對(duì)這種情況進(jìn)行判斷

@RequestMapping("/detail.do")
public String index(ModelMap map, @NotEmpty(message = "billId不能為空") @RequestParam("billId") String billId) {    
WorkorderBill workorderBill = workorderBillService.findOne(Long.parseLong(billId));    
//如果記錄不存在,跳轉(zhuǎn)提示頁(yè)面    
if (workorderBill==null){        
return ControllerHelper.showMsgPage(map, MsgConstant.FAIL_FIND_RECORD,WORKORDER_LIST);    
}    
return "finance/settlement/input";
}

10、關(guān)于在汽服項(xiàng)目中使用jdbcTemplate
由于在汽服項(xiàng)目中有些復(fù)雜查詢抑月,這個(gè)時(shí)候就需要用到j(luò)dbcTemplate了树叽,但是如果dao中有了jdbcTemplate,那么我們就無(wú)法使用spring data jpa簡(jiǎn)單好用的功能了谦絮。對(duì)于這個(gè)問(wèn)題有以下幾種解決方式:
1题诵、使用jdbcTemplate的dao全部加個(gè)Extend后綴。例如:UserCardExtendDao
這樣层皱,我們還可以愉快的使用spring data jpa提供的便利(推薦
2性锭、直接在service中把jdbcTemplate注入到屬性中,在service中使用叫胖。
以上兩種方式都可以讓我們同時(shí)使用jdbctemplate和spring data jpa草冈,但是按照java的分層來(lái)說(shuō),service應(yīng)該寫的是業(yè)務(wù)邏輯,不應(yīng)該寫數(shù)據(jù)庫(kù)操作怎棱,所以第一種方式是最優(yōu)選擇
11哩俭、關(guān)于mysql查詢中通配符的問(wèn)題
在mysql中‘,%’等被識(shí)別為通配符(有寫過(guò)模糊查詢的同學(xué)應(yīng)該知道吧)拳恋。假如凡资,現(xiàn)在我要查詢一個(gè)“_”字符串,最終發(fā)出的sql會(huì)是這樣的谬运。

select * from o_workorder where car_no like '%_%'

這樣查詢出來(lái)的結(jié)果會(huì)很多(因?yàn)椤癬”被識(shí)別為通配符)隙赁。這樣的結(jié)果不是我想要的。其實(shí)應(yīng)該這樣

select * from o_workorder where car_no like '%\\_%'

利用轉(zhuǎn)義符對(duì)“_”進(jìn)行轉(zhuǎn)義梆暖,這樣就能得到我們最終想要的結(jié)果了伞访,在java中,我將轉(zhuǎn)義的步驟封裝成了一個(gè)工具類SQLUtil.java

if (StringUtil.isNotEmpty(workorderSearchForm.getCarNo())) {   
 predicate.getExpressions() .add( cb.like(
      r.<String> get("carNo"), "%" + SQLUtil.processWildCard(workorderSearchForm.getCarNo().toString()) + "%"));
}

12式廷、重復(fù)提交問(wèn)題
在頁(yè)面中咐扭,如果多次點(diǎn)擊保存,會(huì)發(fā)出多個(gè)請(qǐng)求滑废,這非常容易造成無(wú)意中多創(chuàng)建了記錄蝗肪。為了解決這個(gè)問(wèn)題在js增加以下代碼

    //提交
    $("#js-save-btn").on("click",function(){
         layer.msg('請(qǐng)稍后...', {icon: 16,shade: [0.6, '#393D49']});    
         $(this).on("click",submitFail);
         $(this).parentsUntil("form").parent().submit();
    });
    
    function submitFail(){   
        layer.alert("網(wǎng)絡(luò)繁忙,請(qǐng)刷新頁(yè)面后嘗試");
    }

效果:




13蠕趁、業(yè)務(wù)異常那些事
首先薛闪,要理解我們的系統(tǒng)是異步的,這說(shuō)明了俺陋,服務(wù)器允許兩個(gè)或多個(gè)人同時(shí)登陸系統(tǒng)操作豁延。就拿工單的開(kāi)單步驟來(lái)說(shuō),假設(shè)一個(gè)店員在開(kāi)單的同時(shí)腊状,一個(gè)門店管理員刪除了門店的某個(gè)物料的記錄或者將該物料的庫(kù)存更改為0(這種情況是存在的)诱咏,那么這個(gè)時(shí)候該店員已經(jīng)填寫好了工單,像服務(wù)器發(fā)起了請(qǐng)求缴挖。如果我們不對(duì)庫(kù)存不足和物料記錄不存在的情況進(jìn)行判斷的話就會(huì)造成空指針了袋狞。



在店員填好工單,提交請(qǐng)求之前映屋,門店管理員將工單中對(duì)應(yīng)的物料刪除或者將庫(kù)存改為了0苟鸯,這樣后端如果不判斷,直接根據(jù)前臺(tái)傳的物料id去查詢對(duì)應(yīng)物料的數(shù)據(jù)就會(huì)報(bào)NullException了
@ParamCheck
@RequestMapping("/add.do")
public String add(ModelMap map, HttpServletRequest request, WorkorderAddForm workorderAddForm) {    
         if (workorderAddForm != null) {        
                 AccountInfo accountInfo = accountCookieHelper.getAccountInfo(request,                            BusinessConstant.STORE_TYPE);        
              //保存workorder記錄        
               Workorder workorder = getWorkorderInfo(workorderAddForm);                         workorderService.save(workorder, accountInfo.getRealName());        
             try {            
                  //保存workorderItem記錄           
                 List<WorkorderItem> workorderItems = getWorkorderItems(workorderAddForm,                workorder.getId());            
                 workorderItemService.save(workorderItems, accountInfo.getRealName());            
                 //保存workorderMaterial記錄           
                 List<WorkorderMaterial> workorderMaterials = getWorkorderMaterials(workorderAddForm,                workorder.getId());            
                  workorderMaterialService.save(workorderMaterials, accountInfo.getRealName());       
              } catch (ServiceException e) {            
                     logger.info(e.toString());            
                      return ControllerHelper.showMsgPage(map,e.getMessage(),WORKORDER_LIST);        
              }    
         }   
 return ControllerHelper.showMsgPage(map, MsgConstant.OPT_SUCCESS, WORKORDER_LIST);
}

/** 
* 獲取工單物料數(shù)據(jù) 
* @param workorderAddForm 
* @return 
*/
private List<WorkorderMaterial> getWorkorderMaterials(WorkorderAddForm workorderAddForm,                                                      Long workorderId) {    
              List<WorkorderMaterial> materials = new ArrayList<WorkorderMaterial>();    
              //物料集合   
              if (workorderAddForm.getMaterialId() != null) {        
                      String[] materialIds = workorderAddForm.getMaterialId();        
                      WorkorderMaterial workorderMaterial = null;        
                      for (int i = 0; i < materialIds.length; i++) {            
                            workorderMaterial = new WorkorderMaterial();      
                            workorderMaterial.setMaterialId(Long.parseLong(materialIds[i])); 
                            workorderMaterial.setWorkorderId(workorderId);            
                           //查詢物料數(shù)據(jù)            
                            Material material = materialService.findOne(workorderMaterial.getId());            
                            if (material == null) {               
                                        throw new ServiceException("ID=" + workorderMaterial.getId() + "的物料被刪除,請(qǐng)聯(lián)系管理員");            
                             }            
                             materials.add(workorderMaterial);        
                       }   
                 }   
                return materials;
}

從代碼可以看出棚点,對(duì)于物料是否存在和庫(kù)存不足的情況我們是需要拋出業(yè)務(wù)異常的早处,業(yè)務(wù)異常的處理在controller中(提示用戶庫(kù)存不足或者物料不存在
14、關(guān)于XSS攻擊
網(wǎng)站經(jīng)常會(huì)遇到CSXF瘫析、XSS砌梆、SQL注入攻擊(鏈接:http://itindex.net/blog/2013/10/25/1382688300000.html)默责。 針對(duì)其中的XSS攻擊,我在項(xiàng)目中加了一個(gè)XssFilter么库,用于處理輸出的腳本傻丝,對(duì)于輸入腳本進(jìn)行轉(zhuǎn)義處理甘有。

@WebFilter(filterName = "xssFilter", urlPatterns = "/*")
public class XssFilter implements Filter {    
          FilterConfig filterConfig = null;    
          public void init(FilterConfig filterConfig) throws ServletException {        
                    this.filterConfig = filterConfig;    
           }    
           public void destroy() {       
                    this.filterConfig = null;    
           }    
           public void doFilter(ServletRequest request, ServletResponse response,                         FilterChain chain) throws IOException, ServletException {        
              chain.doFilter(new XssHttpServletRequestWrapper(                (HttpServletRequest) request), response);    
            }
}

15诉儒、關(guān)于分頁(yè)操作后回到操作頁(yè)面的解決方案
在系統(tǒng)中存在這么一個(gè)問(wèn)題,我在第二頁(yè)操作了一條記錄亏掀,但是等我操作完之后跳轉(zhuǎn)頁(yè)面跳到了第一頁(yè)忱反,這樣造成的用戶體驗(yàn)很不好。解決方案:
1滤愕、在分頁(yè)列表的form的action加上一個(gè)listMode=cache的參數(shù)

<form method="post" name="form_workorder" id="form_workorder"         action="$!{webUtil.getHostDomain('/workorder/list.do')}?listMode=cache">

2温算、在返回按鈕添加一個(gè)listMode=restore參數(shù)

<a class="btn btn-primary" href="${webUtil.getHostDomain('/workorder/list.do?listMode=restore')}">返回</a>


當(dāng)傳了listMode=cache的參數(shù)時(shí),后臺(tái)會(huì)對(duì)這次請(qǐng)求的參數(shù)進(jìn)行緩存间影,你需要跳回當(dāng)時(shí)的頁(yè)面只要在list.do后面加上一個(gè)listMode=restore(恢復(fù)緩存頁(yè)面)即可
16注竿、系統(tǒng)的中的數(shù)據(jù)權(quán)限
用戶登陸系統(tǒng)之后按理說(shuō)是只能查看屬于自己的數(shù)據(jù)的,但是有這樣的一種情況魂贬,如果一個(gè)門店操作員登陸了巩割,但是直接更改url,那么他就能夠訪問(wèn)到另一個(gè)門店的記錄付燥。
例如:A門店操作員登陸之后宣谈,訪問(wèn)工單http://qfstore.car-me.com/store/workorder/detail.do?workorderId=275 ,然后我更改了workorderId=200键科。在數(shù)據(jù)庫(kù)中workorderId=200的記錄是存在的闻丑,但是這個(gè)工單記錄不屬于改門店,這樣這個(gè)操作員就查看到了不屬于他能看到的記錄勋颖。對(duì)于這種情況我們需要在后臺(tái)中進(jìn)行處理

WorkorderVo workorderVo = workorderService.findWorkorderInfo(Long.parseLong(workorderId),    accountInfo.getSellerId(), store.getId());
if (workorderVo == null) 
{    
     return ControllerHelper.showMsgPage(map, MsgConstant.FAIL_FIND_RECORD, WORKORDER_LIST);
}

查詢單條記錄的時(shí)候帶上sellerId和storeId嗦嗡,如果找不到該記錄跳轉(zhuǎn)到消息提示頁(yè)面


FAQ

1、使用內(nèi)嵌的tomcat啟動(dòng)的時(shí)候提示找不到j(luò)ava.lang.FunctionalInterface這個(gè)類


原因:低版本的jdk中沒(méi)有java.lang.FunctionalInterface這個(gè)類
解決方法:在電腦安裝高版本(我電腦上jdk版本是1.7_079)的jdk饭玲,然后

  • eclipse項(xiàng)目jdk指定到高版本
  • 更改電腦環(huán)境變量的JAVA_HOME

2侥祭、關(guān)于類循環(huán)調(diào)用的問(wèn)題


看到這個(gè)堆棧溢出我開(kāi)始是不知所措的。進(jìn)一步了解之后明白了咱枉,一般出現(xiàn)StackOverflowError的異常是由于調(diào)用棧的長(zhǎng)度超過(guò)了JVM的預(yù)設(shè)值(參考:http://blog.csdn.net/zhuyijian135757/article/details/38025339 )卑硫,一般這種問(wèn)題都是由于遞歸調(diào)用造成的。進(jìn)一步觀察蚕断,通過(guò)堆棧發(fā)現(xiàn)異常主要出現(xiàn)在CustomerCardItem.java和CustomerCard.java的toString方法欢伏,這個(gè)地方出現(xiàn)了循環(huán)調(diào)用的問(wèn)題。

CustomerCard.java
@Override
public String toString() {    
                return "CustomerCard [id=" + id + ", customerId=" + customerId + ", cardId=" + 
                            cardId  + ", balance=" + balance + ", expireAt=" + expireAt + ", bindCarNo=" 
                          + bindCarNo + ", bindTelephoneNumber=" + bindTelephoneNumber 
                          + ", cardActiveStatus=" + cardActiveStatus + ", isDelete=" + isDelete + 
                            ",  createdBy=" + createdBy + ", createdAt=" + createdAt + ", changedBy=" 
                            + changedBy + ", changedAt=" + changedAt + ", version=" + version +
                            ",   cardItems=" + cardItems           + ", cardTypeInfo=" + cardTypeInfo + "]";
}
CustomerCardItem.java
@Override
public String toString() {    
              return "CustomerCardItem [id=" + id + ", customerCardId=" + customerCardId + 
               ", itemId=" + itemId + ", itemQuantity=" + itemQuantity + ", discountRatio=" 
               + discountRatio + ", isDelete=" + isDelete + ", createdBy=" + createdBy + 
                ", createdAt="      + createdAt  + ", changedBy=" + changedBy + ", changedAt=" 
                + changedAt + ", version=" + version + ", customerCard=" + 
                 customerCard + ",  item=" + item + "]";}

看出問(wèn)題了嗎亿乳?機(jī)智的我已經(jīng)發(fā)現(xiàn)了(柯南附體)硝拧,CustomerCard調(diào)用了CardItems(CardItems是CustomerCardItem的集合)的toString方法径筏,在CustomerCardItem的toString方法又調(diào)用了CustomerCardItem的toStrng方法


看了圖應(yīng)該秒懂了吧,兩個(gè)類循環(huán)調(diào)用直到堆棧的內(nèi)存耗盡
3障陶、@ServletComponentScan注解的使用

在springBoot中庸@WebFilter注冊(cè)filter的時(shí)候滋恬,用內(nèi)嵌tomcat啟動(dòng)的時(shí)候會(huì)發(fā)現(xiàn)無(wú)法進(jìn)入注冊(cè)的filter中,這是因?yàn)锧webfilter并沒(méi)有注冊(cè)抱究,@Webfilter沒(méi)有掃描到恢氯。這個(gè)時(shí)候需要在啟動(dòng)類中加入@ServletComponentScan這個(gè)注解。但是如果項(xiàng)目部署在外部的tomcat的時(shí)候鼓寺,這個(gè)注解可以不用加勋拟,因?yàn)橥獠縯omcat會(huì)獨(dú)立掃描

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市妈候,隨后出現(xiàn)的幾起案子敢靡,更是在濱河造成了極大的恐慌,老刑警劉巖苦银,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件啸胧,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡幔虏,警方通過(guò)查閱死者的電腦和手機(jī)纺念,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)所计,“玉大人柠辞,你說(shuō)我怎么就攤上這事≈麟剩” “怎么了叭首?”我有些...
    開(kāi)封第一講書(shū)人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)踪栋。 經(jīng)常有香客問(wèn)我焙格,道長(zhǎng),這世上最難降的妖魔是什么夷都? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任眷唉,我火速辦了婚禮,結(jié)果婚禮上囤官,老公的妹妹穿的比我還像新娘冬阳。我一直安慰自己,他們只是感情好党饮,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開(kāi)白布肝陪。 她就那樣靜靜地躺著,像睡著了一般刑顺。 火紅的嫁衣襯著肌膚如雪氯窍。 梳的紋絲不亂的頭發(fā)上饲常,一...
    開(kāi)封第一講書(shū)人閱讀 49,784評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音狼讨,去河邊找鬼贝淤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛政供,可吹牛的內(nèi)容都是我干的播聪。 我是一名探鬼主播,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼鲫骗,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼犬耻!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起执泰,我...
    開(kāi)封第一講書(shū)人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎渡蜻,沒(méi)想到半個(gè)月后术吝,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡茸苇,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年排苍,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片学密。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡淘衙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出腻暮,到底是詐尸還是另有隱情彤守,我是刑警寧澤,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布哭靖,位于F島的核電站具垫,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏试幽。R本人自食惡果不足惜筝蚕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望铺坞。 院中可真熱鬧起宽,春花似錦、人聲如沸济榨。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)腿短。三九已至屏箍,卻和暖如春绘梦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背赴魁。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工卸奉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人颖御。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓榄棵,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親潘拱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子疹鳄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類相關(guān)的語(yǔ)法芦岂,內(nèi)部類的語(yǔ)法瘪弓,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法禽最,線程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,598評(píng)論 18 399
  • 原文:https://my.oschina.net/liuyuantao/blog/751438 查詢集API 參...
    陽(yáng)光小鎮(zhèn)少爺閱讀 3,813評(píng)論 0 8
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理腺怯,服務(wù)發(fā)現(xiàn),斷路器川无,智...
    卡卡羅2017閱讀 134,629評(píng)論 18 139
  • 來(lái)源與:阿里云棲 禁止用于商業(yè)用途 ps:如果需要電子書(shū) 評(píng)論你們郵箱 我會(huì)發(fā)給你們 下面感覺(jué)還是有點(diǎn)亂 目錄 一...
    小向資源網(wǎng)閱讀 7,579評(píng)論 0 12
  • 提筆難抒心中情呛占,心中盡是雜緒。已是三更還無(wú)法入睡懦趋。我心中很亂晾虑,不知在想些什么,也不敢想些什么仅叫。已是成年還一所所獲帜篇。...
    帥氣的男孩ing閱讀 251評(píng)論 0 0