程序员必知:元旦三天假期,实现一个电商退单管理系统【三】

简介: 程序员必知:元旦三天假期,实现一个电商退单管理系统【三】

一、服务端接口实现

服务端接口用于接收客户端登录、快递公司查询、同步订单、查询已同步订单等业务。主要用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) {

//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 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=?//代码效果参考:http://www.zidongmutanji.com/bxxx/341336.html

",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 //代码效果参考:http://www.zidongmutanji.com/bxxx/444556.html

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月前
|
人工智能 自然语言处理 计算机视觉
StyleStudio:支持图像风格迁移的文生图模型,能将融合参考图像的风格和文本提示内容生成风格一致的图像
StyleStudio 是一种文本驱动的风格迁移模型,能够将参考图像的风格与文本提示内容融合。通过跨模态 AdaIN 机制、基于风格的分类器自由引导等技术,解决了风格过拟合、控制限制和文本错位等问题,提升了风格迁移的质量和文本对齐的准确性。
388 8
StyleStudio:支持图像风格迁移的文生图模型,能将融合参考图像的风格和文本提示内容生成风格一致的图像
|
存储 安全 PHP
PHP应用开发中的安全性考虑与实践
在当前互联网应用盛行的背景下,PHP作为一种广泛应用于Web开发的编程语言,其安全性显得尤为重要。本文探讨了PHP应用开发中的几个关键安全性考虑因素,并提供了一些实用的安全实践建议,旨在帮助开发人员构建更加安全可靠的应用程序。 【7月更文挑战第11天】
111 4
|
安全 Java 编译器
Go语言面试宝典:50道必会题目与精解
本文提供了50道覆盖Go语言核心概念、并发编程、内存管理、包管理、错误处理和测试等方面的面试题及其详细答案,旨在帮助开发者全面准备Go语言技术面试。
|
存储 缓存 NoSQL
一文讲透 Redis 事务 (事务模式 VS Lua 脚本)
先说结论: Redis 的事务模式具备如下特点: - 保证隔离性; - 无法保证持久性; - 具备了一定的原子性,但不支持回滚; - 一致性的概念有分歧,假设在一致性的核心是约束的语意下,Redis 的事务可以保证一致性。 但 Lua 脚本更具备实用场景,它是另一种形式的事务,他具备一定的原子性,但脚本报错的情况下,事务并不会回滚。Lua 脚本可以保证隔离性,而且可以完美的支持**后面的步骤依赖前面步骤的结果**。
一文讲透 Redis 事务 (事务模式 VS Lua 脚本)
|
编解码 算法 开发者
RTSP摄像机为什么还保留MJPEG编码格式
细心的开发者会发现,海康大华之类摄像机厂商,除了常规的H.264、H.265(HEVC)编码外,主码流或子码流依然会有MJPEG编码选项。
340 0
|
存储 消息中间件 缓存
【高并发】高并发秒杀系统架构解密,不是所有的秒杀都是秒杀!
在电商领域,存在着典型的秒杀业务场景,那何谓秒杀场景呢。简单的来说就是一件商品的购买人数远远大于这件商品的库存,而且这件商品在很短的时间内就会被抢购一空。 比如每年的618、双11大促,小米新品促销等业务场景,就是典型的秒杀业务场景。
1093 1
【高并发】高并发秒杀系统架构解密,不是所有的秒杀都是秒杀!
|
Java Spring
Spring原理学习系列之一:注解原理解析
对于Spring注解大家肯定都不陌生,在日常开发工作中也会经常使用到注解。有时候提问小伙伴,注解的原理是什么,大部分都回答是利用了反射机制。但是继续深入提问,在Spring中是如何解析这些自带注解以及注解到底在什么时候起作用等问题时,很多人都会犯嘀咕。同样我在实际使用的过程中,也会有相同的困惑。所以一直想探究下注解实际的工作原理以及设计思想。用此文记录下自己对于注解原理的理解,也为有同样疑问的小伙伴提供些不同的理解角度。 原理解析 使用实例
Spring原理学习系列之一:注解原理解析
|
前端开发
前端项目实战167-对传入的数据进行id和日期匹配
前端项目实战167-对传入的数据进行id和日期匹配
148 0
|
人工智能 物联网 传感器
中国花椒之乡携手阿里 打造县域数字经济示范样板
8月16日,汉源县人民政府宣布和阿里云达成合作。借助云计算、人工智能方面的领先技术,结合当地的花椒等优势农业产业资源,双方将共同打造以数字化和智能化驱动的全国县域数字经济示范区新样板。
1213 0
|
6天前
|
存储 弹性计算 人工智能
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾
2025年9月24日,阿里云弹性计算团队多位产品、技术专家及服务器团队技术专家共同在【2025云栖大会】现场带来了《通用计算产品发布与行业实践》的专场论坛,本论坛聚焦弹性计算多款通用算力产品发布。同时,ECS云服务器安全能力、资源售卖模式、计算AI助手等用户体验关键环节也宣布升级,让用云更简单、更智能。海尔三翼鸟云服务负责人刘建锋先生作为特邀嘉宾,莅临现场分享了关于阿里云ECS g9i推动AIoT平台的场景落地实践。
【2025云栖精华内容】 打造持续领先,全球覆盖的澎湃算力底座——通用计算产品发布与行业实践专场回顾