服務端接口用於接收客戶端登陸、快遞公司查詢、同步訂單、查詢已同步訂單等業務。主要用tp3.2完成。只寫了一個controller,數據庫查詢都寫在controller裏了。php
tp原本的設計思想,也不分業務邏輯層和數據層。他把數據層和model混合在一塊兒,業務邏輯層和controller合在一塊兒。對於小型項目,快速實現,變動快速響應有很大優點。我這邊業務邏輯不是不少,只用了一個入口。ajax
入口處,先放接收一個方法名,根據方法名,再去執行相應的業務邏輯。算法
接口模板拿了一個之前作手機app的文檔改了下,設計以下:spring
1. 擴展了一個簡單的枚舉值做爲與客戶端約定的狀態碼。PHP處理這個有點麻煩。msg不知道怎麼對應,仍是直接在各處寫漢字。sql
2. 簡單的通用返回體getRepsonseMessage結構與客戶端保持一致。數據庫
3. 簽名算法根據流水和入參作了字典排序,而後再加密,與客戶端作對比。express
4. 只有入口函數是public,其餘所有是private。json
5. 除幾個公共入參外,其餘入參都由本身的子函數接收。大部分用php的I方法,訂單同步由於要傳json字符串,因此用了$_POST。centos
public function interface(){ $transid = I("transid"); $ts = I("ts"); $sign = I("sign"); $method = I("method"); $this->logger("參數:$transid,$ts,$sign,$method"); if(!$sign || !$transid || !$ts || !$method){ $this->getResponseMessage(ResponseCode::EMPTY_PARAM,"參數爲空",""); }else{ if($this->checkSign($sign) || $method == "upgrade"){ switch($method){ case "upgrade": //版本檢測 $this->upgrade(); break; case "login": //登陸 $this->login(I("uname"),I("ucode")); break; case 'changepwd': //密碼修改 $this->changepwd(); case "scan": //掃碼 $this->scan(); break; case "syncorders": //同步 $this->syncorders(); break; case "userinfo": //用戶中心數據源 $this->userinfo(); break; case "express": //快遞公司清單 $this->express(); break; case "express_byid": //根據id查找快遞公司 $this->express_byid(); break; case "express_daycount": //今日快遞公司統計 $this->express_daycount(); break; case "backorder_daylist": //今日退單列表 $this->backorder_daylist(); break; case 'getprebackorder_bybarcode': //根據掃碼單號獲取訂單詳情 $this->getprebackorder_bybarcode(); break; case 'getprebackorder_byid': //根據id獲取訂單 $this->getprebackorder_byid(); break; case 'preorder_unusal_deal': //異常訂單處理 $this->preorder_unusal_deal(); break; case 'backorder_forcheck': //待拆包訂單 $this->backorder_forcheck(); break; case 'backorder_forcheck_submit': //提交拆包檢查結果 $this->backorder_forcheck_submit(); break; default: $this->getResponseMessage(ResponseCode::UNKNOWN_METHOD,"未知的方法名!",""); break; } }else{ $this->getResponseMessage(ResponseCode::SIGN_ERROR,"簽名檢驗失敗",""); } } }
private function getResponseMessage($code,$msg,$data="",$mark1=0,$mark2=""){ return $this->ajaxReturn(array("code"=>$code,"msg"=>$msg,"data"=>$data,"mark1"=>$mark1,"mark2"=>$mark2)); }
/** * method: login */ private function login($uname,$ucode){ $user = M("User")->where(array("user_code"=>$uname))->find(); if(!$user){ $this->getResponseMessage(ResponseCode::INVALID_USER,"錯誤的用戶名或密碼",null); }else{ //客戶端應該上傳md5之後的密碼 if($user["password"]==$ucode){ $_SESSION["user"] = $user; //更新登陸次數和最後登陸時間 $user["logincount"] = $user["logincount"]+1; M("User")->save($user); $this->getResponseMessage(ResponseCode::SUCCESS,"登陸成功",$user); }else{ $this->getResponseMessage(ResponseCode::INVALID_USER,"錯誤的用戶名或密碼",null); } } }
後臺服務主要功能爲:springboot
1. 超過必定時間訂單轉爲超時訂單。
2. 預退貨單匹配入庫單轉拆包。
3. 退貨單若是沒有預退貨單,自動轉拆包(需求變動)。
4. 丟包單匹配入庫單轉拆包。
庫表結構比較簡單,需求產生過變化,一開始入庫單是不轉自動拆包的,後來客戶提取此需求,我沒有擴展再建新表了,就在預退貨單表裏加了一個order_from字段,1是後臺錄入預退貨,2是入庫單沒有匹配到預退貨單自動拆包,以下圖:
考慮到須要常駐後臺進程,執行定時任務,服務器爲centos,使用springboot搭建,mvc結構,持久層用了jdbcTemplate,表關係比較簡單,沒有用事務。JdbcTemplate爲了獲取剛插入的自增鍵ID,費了老鼻子勁。
dao層實現主要幾個功能以下:
超時訂單,爲了後臺修改超時天數能實時生效,每次執行定時任務,查詢一下超時天數:
/** * 超時訂單 */ public void updateExpiredPrebackorder(){ String sql = "SELECT config_value FROM tb_config WHERE config_code='expire_time'"; int days = this.jdbcTemplate.queryForObject(sql,int.class); log.info("超時設置爲:"+days); sql = "SELECT * FROM tb_prebackorder WHERE prebackorder_status=1 AND DATEDIFF(now(),prebackorder_date)>=?"; List<PrebackorderEntity> list = this.jdbcTemplate.query(sql,new BeanPropertyRowMapper(PrebackorderEntity.class),days); for (PrebackorderEntity pbo: list) { sql = "UPDATE tb_prebackorder SET prebackorder_status=5 WHERE prebackorder_id=?"; this.jdbcTemplate.update(sql,pbo.getPrebackorder_id()); //寫日誌 sql = "INSERT INTO tb_flows(flows_date,flows_man,flows_content,flow_forms,flow_forms_id) VALUES(now(),?,?,?,?)"; log.info("設置爲超時:"+pbo.getPrebackorder_id()); jdbcTemplate.update(sql,"系統服務","超過"+days+"天未匹配退貨單,自動設置爲超時","prebackorder",pbo.getPrebackorder_id()); } }
丟包單匹配入庫單:
/** * 丟包單匹配入庫單進程 * 考慮到全部的backorder都會流轉到prebackorder,全部只要與prebackorder進行匹配便可 * 只有prebackstatus=2(拆包)的才能進行匹配,其餘狀態不能匹配,如手工錄入預退貨單、超時單等都不能進行匹配,2是從掃碼入庫單過來的,才能保證是入庫的。 */ public void lostOrdersMatchs(){ String sql = "SELECT a.lostorder_id,b.prebackorder_id FROM tb_lostorder a INNER JOIN tb_prebackorder b ON b.prebackorder_code=a.lostorder_code " + "WHERE backstore_flag=0 AND prebackorder_status=2"; List<Map<String,Object>> list = this.jdbcTemplate.queryForList(sql); log.info("進行一次丟包單匹配,共匹配到"+list.size()+"條入庫單"); for (Map<String,Object> map: list) { //1. 更新lostorder的backstore_flag爲1 String sql1 = "UPDATE tb_lostorder SET backstore_flag=1 WHERE lostorder_id="+map.get("lostorder_id"); //2. 更新prebackorder的狀態爲2(拆包檢驗) String sql2 = "UPDATE tb_prebackorder SET prebackorder_status=2 WHERE prebackorder_id="+map.get("prebackorder_id"); //3. 插入一條流程記錄 String sql3 = "INSERT INTO tb_flows(flows_date,flows_man,flows_content,flow_forms,flow_forms_id) " + "VALUES(now(),'系統服務','丟包單入庫轉拆包','prebackorder',"+map.get("prebackorder_id")+")"; log.info("更新入庫單"+map.get("prebackorder_id")+"進入拆包流程"); this.jdbcTemplate.batchUpdate(sql1,sql2,sql3); } }
退貨入庫單(即快遞掃碼入庫),若是沒有預退貨單匹配成功,自動進拆包檢驗流程:
/** * 將超過必定時間的退貨單,自動流轉進拆包檢驗 */ public void setBackorder2Prebackorder(){ String sql = "SELECT * FROM tb_backorder WHERE match_preorder_flag=0"; List<BackorderEntity> list = this.jdbcTemplate.query(sql,new BeanPropertyRowMapper(BackorderEntity.class)); for (BackorderEntity bo: list) { //先要判斷,若是已存在預退貨單號,則不能再次插入 List<PrebackorderEntity> pbolist = this.jdbcTemplate.query("SELECT * FROM tb_prebackorder WHERE prebackorder_code=?",new BeanPropertyRowMapper(PrebackorderEntity.class), bo.getBackorder_code()); if(pbolist!=null && pbolist.size()>0){ log.info("匹配一條,但單號重複,不用進prebackorder,("+bo.getBackorder_code()+"),直接將該訂單matc_preorder_flag改成1。"); this.jdbcTemplate.update("UPDATE tb_backorder SET match_preorder_flag=1 WHERE backorder_id=?",bo.getBackorder_id()); }else{ sql = "INSERT INTO tb_prebackorder (prebackorder_code,prebackorder_date,express_id,userid,prebackorder_status,add_date,match_flag,match_date,match_code,prebackorder_from)" + " VALUES (?,?,?,?,?,?,?,?,?,?)"; KeyHolder keyHolder = new GeneratedKeyHolder(); //直接進入狀態2,退貨單匹配成功,待拆包 PreparedStatementCreatorFactory pscf = new PreparedStatementCreatorFactory(sql,new int[]{ Types.VARCHAR, Types.DATE,Types.INTEGER,Types.INTEGER,Types.INTEGER,Types.DATE,Types.INTEGER,Types.DATE,Types.VARCHAR,Types.INTEGER }); pscf.setReturnGeneratedKeys(true); Object[] obj = new Object[]{ bo.getBackorder_code(),bo.getBackorder_date(),bo.getExpress_id(),bo.getUserid(),2,new Date(),1,new Date(),bo.getBackorder_code(),2 }; PreparedStatementCreator psc = pscf.newPreparedStatementCreator(obj); int result = this.jdbcTemplate.update(psc,keyHolder); //加一行flows int preid = keyHolder.getKey().intValue(); sql = "INSERT INTO tb_flows(flows_date,flows_man,flows_content,flow_forms,flow_forms_id) VALUES(now(),?,?,?,?)"; jdbcTemplate.update(sql,"系統服務","退貨單自動流轉","prebackorder",preid); //更新入庫單爲已匹配 this.jdbcTemplate.update("UPDATE tb_backorder SET match_preorder_flag=1 WHERE backorder_id=?",bo.getBackorder_id()); log.info("自動流轉進入拆包訂單"); } } }
預退貨單匹配入庫單:
/** * 匹配預退貨訂單與退貨入庫單 */ public void MatchPreOrders(){ String sql = "SELECT * FROM tb_prebackorder WHERE prebackorder_status IN (1,5)"; //超時訂單也能夠匹配從新入庫 List<PrebackorderEntity> list = this.jdbcTemplate.query(sql,new BeanPropertyRowMapper(PrebackorderEntity.class)); for (PrebackorderEntity pbo: list) { String msql = "SELECT * FROM tb_backorder WHERE backorder_code=? LIMIT 1"; List<BackorderEntity> bolist = this.jdbcTemplate.query(msql,new BeanPropertyRowMapper(BackorderEntity.class), pbo.getPrebackorder_code()); if(bolist!=null && bolist.size()>0){ matchOrders(pbo, bolist.get(0)); sql = "INSERT INTO tb_flows(flows_date,flows_man,flows_content,flow_forms,flow_forms_id) VALUES(now(),?,?,?,?)"; jdbcTemplate.update(sql,"系統服務","自動匹配退貨單","prebackorder",pbo.getPrebackorder_id()); //再更新下退貨單表爲匹配成功 jdbcTemplate.update("UPDATE tb_backorder SET match_preorder_flag=1 WHERE backorder_id=?",bolist.get(0).getBackorder_id()); log.info("匹配成功一條記錄("+pbo.getPrebackorder_code()+")"); }else{ //有的退單,是在原單號上加一個前綴,因此若是預退單號的indexof入庫單號大於0,則匹配成功 sql = "SELECT * FROM tb_backorder WHERE LOCATE(backorder_code,'"+pbo.getPrebackorder_code()+"')>1 LIMIT 1"; bolist = this.jdbcTemplate.query(sql,new BeanPropertyRowMapper(BackorderEntity.class)); if(bolist!=null && bolist.size()>0){ matchOrders(pbo, bolist.get(0)); log.info("匹配成功一條記錄("+pbo.getPrebackorder_code()+")"); //寫日誌 sql = "INSERT INTO tb_flows(flows_date,flows_man,flows_content,flow_forms,flow_forms_id) VALUES(now(),?,?,?,?)"; jdbcTemplate.update(sql,"系統服務","自動匹配退貨單","prebackorder",pbo.getPrebackorder_id()); //同步更新退貨單表爲匹配成功 jdbcTemplate.update("UPDATE tb_backorder SET match_preorder_flag=1 WHERE backorder_id=?",bolist.get(0).getBackorder_id()); log.info("模糊匹配成功一條記錄"); } } } }
service層,沒有具體的業務邏輯,略。
定時任務:
/** * 每10秒鐘執行一次 */ @Scheduled(cron = "0/10 * * * * *" ) public void Match(){ this.service.MatchPreOrders(); log.info("執行一次匹配退單流程"); } /** * 天天0點執行一次 */ @Scheduled(cron = "0 0 0 * * ?") public void updateExpiredPrebackorder(){ log.info("更新一次超時記錄"); this.service.updateExpiredPrebackorder(); } /** * 每30秒鐘執行一次 */ @Scheduled(cron = "0/30 * * * * ?") public void setBackorder2Prebackorder(){ log.info("執行一次:退貨單自動流轉至拆包"); this.service.setBackorder2Prebackorder(); log.info("執行一次:丟包單自動匹配"); this.service.lostOrdersMatchs(); }