技术经验解读:元旦三天假期,实现一个电商退单管理系统【三】

简介: 技术经验解读:元旦三天假期,实现一个电商退单管理系统【三】

一、服务端接口实现


服务端接口用于接收客户端登录、快递公司查询、同步订单、查询已同步订单等业务。主要用tp3.2完成。只写了一个controller,数据库查询都写在controller里了。


tp本来的设计思想,也不分业务逻辑层和数据层。他把数据层和model混合在一起,业务逻辑层和controller合在一起。对于小型项目,快速实现,变更快速响应有很大优势。我这边业务逻辑不是很多,只用了一个入口。


入口处,先放接收一个方法名,根据方法名,再去执行相应的业务逻辑。


接口模板拿了一个以前做手机app的文档改了下,设计如下:


入口函数


1. 扩展了一个简单的枚举值作为与客户端约定的状态码。PHP处理这个有点麻烦。msg不知道怎么对应,还是直接在各处写汉字。


2. 简单的通用返回体getRepsonseMessage结构与客户端保持一致。


3. 签名算法根据流水和入参做了字典排序,然后再加密,与客户端做对比。


4. 只有入口函数是public,其他全部是private。


5. 除几个公共入参外,其他入参都由自己的子函数接收。大部分用php的I方法,订单同步因为要传json字符串,所以用了$_POST。


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);


}


}


}


二、后台服务实现


后台服务主要功能为:


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


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

log.info("进行一次丢包单匹配,共匹配到"+list.size()+"条入库单");


for (Map map:


list) {


////代码效果参考:http://www.lyjsj.net.cn/wx/art_22772.html

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. 插入一条流程记录


//代码效果参考:http://www.lyjsj.net.cn/wx/art_22770.html

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 list = this.jdbcTemplate.query(sql,new BeanPropertyRowMapper(BackorderEntity.class));


for (BackorderEntity bo:


list) {


//先要判断,如果已存在预退货单号,则不能再次插入


List


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


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 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!=<span style="color:

相关文章
|
10月前
|
开发框架 小程序 前端开发
东郊到家丨家政服务丨预约上门丨系统开发稳定版,家政服务丨预约上门丨东郊到家系统开发(开发案例)及源码技术
 随着人们对家政服务的需求不断增加,家政服务行业也变得越来越受欢迎。家政服务小程序和家政服务系统是两种常见的家政服务解决方案,它们可以为人们提供更加便捷和高效的家政服务体验。下面将详细介绍这两种解决方案。
|
2天前
|
SQL JSON 程序员
程序员必知:元旦三天假期,实现一个电商退单管理系统【三】
程序员必知:元旦三天假期,实现一个电商退单管理系统【三】
|
1月前
|
存储 数据可视化 安全
大学餐厅菜品推荐和点评系统设计与实现
大学餐厅菜品推荐和点评系统设计与实现
|
1月前
|
人工智能 数据可视化 搜索推荐
年货节开跑!这份电商打工人备战指南你还没看?
年货节开跑!这份电商打工人备战指南你还没看?
|
区块链 数据安全/隐私保护 开发者
字画拍卖竞拍系统开发(开发详情)丨字画拍卖竞拍开发源码案例
数字藏品,是指使用区块链技术,对应特定的作品、艺术品生成的唯一数字凭证,在保护其数字版权的基础上,实现真实可信的数字化发行,购买,收藏和使用。
字画拍卖竞拍系统开发(开发详情)丨字画拍卖竞拍开发源码案例
|
存储 数据可视化 数据安全/隐私保护
绥北人民法院:用宜搭打造“线上法庭”,让群众少跑腿
钉钉宜搭帮助绥北人民法院打造出“线上法庭”,各类成本节省了90%,运转效率提升了50%。
489 0
绥北人民法院:用宜搭打造“线上法庭”,让群众少跑腿
|
运维 分布式计算 Java
为了30分钟配送,盒马工程师都有哪些“神操作”?
提到盒马鲜生,除了新鲜的大龙虾以外,大家印象最深的就是快速配送:门店附近3公里范围内,30分钟送货上门。
6149 0
|
定位技术
多人出游必备良品:高德地图上线组队功能
本文讲的是多人出游必备良品:高德地图上线组队功能最近,高德地图独家上线了一个“组队”功能,通过相互共享彼此的实时位置来帮助用户快速抵达目的地。在亲自测试之后,笔者惊喜地发现这个新功能,非常适合多人出游,尤其是自驾游的场景。
1494 0