外贸推广网站邮箱收费,北京网页设计机构,org域名不能注册了,浅谈电子商务网站建设与规划该模块分为地址表的增删改查、用户下单、订单支付三个部分。 第一部分地址表的增删改查无非就是对于单表的增删改查#xff0c;较基础#xff0c;因此直接导入代码。
地址表 一个用户可以有多个地址#xff0c;同时有一个地址为默认地址。用户还可为地址添加例如较基础因此直接导入代码。
地址表 一个用户可以有多个地址同时有一个地址为默认地址。用户还可为地址添加例如公司、学校、家之类的标签。项目中的address_book表就包含了这些信息其中红色字体为重要信息。
字段名数据类型说明备注idbigint主键自增user_idbigint用户id逻辑外键consigneevarchar(50)收货人sexvarchar(2)性别phonevarchar(11)手机号province_codevarchar(12)省份编码province_namevarchar(32)省份名称city_codevarchar(12)城市编码city_namevarchar(32)城市名称district_codevarchar(12)区县编码district_namevarchar(32)区县名称detailvarchar(200)详细地址信息具体到门牌号labelvarchar(100)标签公司、家、学校等is_defaulttinyint(1)是否默认地址1是0否
新增地址 请求路径为/user/addressBook请求方法为Post以json格式提交请求参数。
// Controller———————————————————
RestController
RequestMapping(/user/addressBook)
Api(tags C端地址簿接口)
public class AddressBookController {Autowiredprivate AddressBookService addressBookService;PostMappingApiOperation(新增地址)public Result save(RequestBody AddressBook addressBook) {// 调用服务层方法保存地址信息addressBookService.save(addressBook);// 返回成功结果return Result.success();}
}
// Service———————————————————————
public interface AddressBookService {void save(AddressBook addressBook);
}
// ServiceImpl———————————————————
Service
Slf4j
public class AddressBookServiceImpl implements AddressBookService {Autowiredprivate AddressBookMapper addressBookMapper;public void save(AddressBook addressBook) {// 设置当前用户的IDaddressBook.setUserId(BaseContext.getCurrentId());// 设置地址簿是否为默认地址默认为0不是默认地址addressBook.setIsDefault(0);// 调用映射器插入地址簿信息addressBookMapper.insert(addressBook);}
}
// Mapper———————————————————————
Mapper
public interface AddressBookMapper {Insert(insert into address_book (user_id, consignee, phone, sex, province_code, province_name, city_code, city_name, district_code, district_name, detail, label, is_default) values (#{userId}, #{consignee}, #{phone}, #{sex}, #{provinceCode}, #{provinceName}, #{cityCode}, #{cityName}, #{districtCode}, #{districtName}, #{detail}, #{label}, #{isDefault}))void insert(AddressBook addressBook);
}
查询地址列表 请求路径为/user/addressBook/list请求方法为Get无需传参后端通过Token中的userId获取地址信息并返回。
// Controller———————————————————
GetMapping(/list)
ApiOperation(查询当前登录用户的所有地址信息) // API操作说明
public ResultListAddressBook list() {// 创建AddressBook对象并设置当前登录用户的IDAddressBook addressBook new AddressBook();addressBook.setUserId(BaseContext.getCurrentId());// 调用服务层方法获取地址列表ListAddressBook list addressBookService.list(addressBook);// 返回成功结果和地址列表return Result.success(list);
}
// Service———————————————————————ListAddressBook list(AddressBook addressBook);
// ServiceImpl———————————————————public ListAddressBook list(AddressBook addressBook) {return addressBookMapper.list(addressBook);}
// Mapper———————————————————————ListAddressBook list(AddressBook addressBook);
mapper namespacecom.sky.mapper.AddressBookMapperselect idlist parameterTypeAddressBook resultTypeAddressBookselect * from address_bookwhereif testuserId ! nulland user_id #{userId}/ifif testphone ! nulland phone #{phone}/ifif testisDefault ! nulland is_default #{isDefault}/if/where/select
/mapper查询默认地址 请求路径为/user/addressBook/default请求方法为Get后端通过Token中的userId获取地址信息并返回。
// Controller———————————————————
GetMapping(default)
ApiOperation(查询默认地址) // API操作说明用于描述查询默认地址的接口
public ResultAddressBook getDefault() {// 创建AddressBook对象并设置查询条件当前登录用户的ID和默认地址标志AddressBook addressBook new AddressBook();addressBook.setIsDefault(1); // 设置默认地址标志为1addressBook.setUserId(BaseContext.getCurrentId()); // 设置当前登录用户的ID// 调用服务层方法查询符合条件的地址列表ListAddressBook list addressBookService.list(addressBook);// 检查查询结果如果存在且只有一个默认地址则返回该地址if (list ! null list.size() 1) {return Result.success(list.get(0));}// 如果没有查询到默认地址则返回错误信息return Result.error(没有查询到默认地址);
}
修改地址 同样分为查询回显和修改地址两个接口。
根据Id查询地址 请求路径为/user/addressBook/{id}请求方法为Delete。
// Controller———————————————————GetMapping(/{id})ApiOperation(根据id查询地址)public ResultAddressBook getById(PathVariable Long id) {AddressBook addressBook addressBookService.getById(id);return Result.success(addressBook);}
// Service———————————————————————AddressBook getById(Long id);
// ServiceImpl———————————————————public AddressBook getById(Long id) {AddressBook addressBook addressBookMapper.getById(id);return addressBook;}
// Mapper———————————————————————Select(select * from address_book where id #{id})AddressBook getById(Long id);
修改地址 请求路径为/user/addressBook请求方法为Put以json格式提交请求参数(老师提供的xml文件有误无法修改省份城市和区域)。
// Controller———————————————————PutMappingApiOperation(根据id修改地址)public Result update(RequestBody AddressBook addressBook) {addressBookService.update(addressBook);return Result.success();}
// Service———————————————————————void update(AddressBook addressBook);
// ServiceImpl———————————————————public void update(AddressBook addressBook) {addressBookMapper.update(addressBook);}
// Mapper———————————————————————void update(AddressBook addressBook); update idupdate parameterTypeaddressBookupdate address_booksetif testconsignee ! nullconsignee #{consignee},/ifif testsex ! nullsex #{sex},/ifif testphone ! nullphone #{phone},/ifif testprovinceCode ! nullprovince_code #{provinceCode},/ifif testprovinceName ! nullprovince_name #{provinceName},/ifif testcityCode ! nullcity_code #{cityCode},/ifif testcityName ! nullcity_name #{cityName},/ifif testdistrictCode ! nulldistrict_code #{districtCode},/ifif testdistrictName ! nulldistrict_name #{districtName},/ifif testdetail ! nulldetail #{detail},/ifif testlabel ! nulllabel #{label},/ifif testisDefault ! nullis_default #{isDefault},/if/setwhere id #{id}/update
删除地址 请求路径为/user/addressBook请求方法为Delete以Query格式提交id。
// Controller———————————————————DeleteMappingApiOperation(根据id删除地址)public Result deleteById(Long id) {addressBookService.deleteById(id);return Result.success();}
// Service———————————————————————void deleteById(Long id);
// ServiceImpl———————————————————public void deleteById(Long id) {addressBookMapper.deleteById(id);}
// Mapper———————————————————————Delete(delete from address_book where id #{id})void deleteById(Long id);
设置默认地址 其本质上是一个修改操作将该地址is_default修改为1(默认)。 请求路径为/user/addressBook/default请求方法为Put以json格式提交请求。
// Controller———————————————————PutMapping(/default)ApiOperation(设置默认地址)public Result setDefault(RequestBody AddressBook addressBook) {addressBookService.setDefault(addressBook);return Result.success();}
// Service———————————————————————void setDefault(AddressBook addressBook);
// ServiceImpl———————————————————Transactionalpublic void setDefault(AddressBook addressBook) {//1、将当前用户的所有地址修改为非默认地址 update address_book set is_default ? where user_id ?addressBook.setIsDefault(0);addressBook.setUserId(BaseContext.getCurrentId());addressBookMapper.updateIsDefaultByUserId(addressBook);//2、将当前地址改为默认地址 update address_book set is_default ? where id ?addressBook.setIsDefault(1);addressBookMapper.update(addressBook);}
// Mapper———————————————————————Update(update address_book set is_default #{isDefault} where user_id #{userId})void updateIsDefaultByUserId(AddressBook addressBook); 代码完成后重新运行项目并前往小程序点击个人中心—地址管理—新增收货地址—填写相关信息然后测试各模块是否能实现预期目标 用户下单
接口设计 在电商系统中用户是通过下单的方式通知商家用户已经购买了商品需要商家进行备货和发货。用户下单后会产生订单相关数据订单数据需要能够体现出如购买的商品、每个商品数量、订单总金额、收货地址、用户名、用户手机号等信息。 在下单界面显示的收货地址并不需要提交只需提交该地址在地址簿中的id即可。配送状态默认为立即送出也可自选配送时间。购买的商品同理后端会读取购物车表中的数据只需传入购物车id即可。总金额包含商品金额打包费配送费本项目的配送费统一为6元打包费一个商品1元在小程序端便已计算传入的为总打包费和总金额。备注和餐具数量为必须传入的参数。 用户下单本质上是新增操作也就是将下单后产生的订单数据插入到数据库中。因此请求方式选择Post请求路径为/user/order/submit。 提交的数据包括地址簿id、总金额、配送状态、预计送达时间(下单时间1小时小程序端会自行计算)、打包费、付款方式(目前只有微信支付一种方式但为方便以后区分不同的支付方式也需保留)、还有备注和餐具数量。
数据库设计 因为前端传入的参数较多我们可以将信息分为订单表和订单明细表来分开存储一个订单包含多个明细属于一对多的关系。 订单表orders
字段名数据类型说明备注idbigint主键自增numbervarchar(50)订单号statusint订单状态 1待付款2待接单3已接单 4派送中5已完成6已取消 user_idbigint用户id逻辑外键address_book_idbigint地址id逻辑外键order_timedatetime下单时间checkout_timedatetime付款时间pay_methodint支付方式1微信支付2支付宝支付pay_statustinyint支付状态0未支付1已支付2退款amountdecimal(10,2)订单金额remarkvarchar(100)备注信息phonevarchar(11)手机号冗余字段addressvarchar(255)详细地址信息冗余字段consigneevarchar(32)收货人cancel_reasonvarchar(255)订单取消原因rejection_reasonvarchar(255)拒单原因cancel_timedatetime订单取消时间estimated_delivery_timedatetime预计送达时间delivery_statustinyint配送状态1立即送出0选择具体时间delivery_timedatetime送达时间pack_amountint打包费tableware_numberint餐具数量tableware_statustinyint餐具数量状态1按餐量提供0选择具体数量 订单明细表order_detail
字段名数据类型说明备注idbigint主键自增namevarchar(32)商品名称冗余字段imagevarchar(255)商品图片路径冗余字段order_idbigint订单id逻辑外键dish_idbigint菜品id逻辑外键setmeal_idbigint套餐id逻辑外键dish_flavorvarchar(50)菜品口味numberint商品数量amountdecimal(10,2)商品单价 下单后等待支付的页面包含支付倒计时默认为15分钟后端只需返回下单时间即可小程序端会自行计算。订单总金额和订单号也许返回。同时因为订单支付需以订单id来区分因此也需返回。
功能实现 后端可用OrdersSubmitDTO类来接收参数。返回的为OrdersSubmitDTO类对象。请求路径为/user/order/submit请求方式为Post。因为订单模块是用户端的功能日后在管理端也需开发同类名的接口为防止程序冲突需指定其生成的bean的名字。
// Controller———————————————————
RestController(userOrderController)
RequestMapping(/user/order)
Api(tags 用户端订单相关接口)
Slf4j
public class OrderController {Autowiredprivate OrderService orderService;PostMapping(/submit)ApiOperation(用户下单)public ResultOrderSubmitVO Submit(RequestBody OrdersSubmitDTO ordersSubmitDTO) {log.info(用户下单参数为:{}, ordersSubmitDTO);OrderSubmitVO orderSubmitVO orderService.submit(ordersSubmitDTO);return Result.success(orderSubmitVO);}
}
// Service———————————————————————
public interface OrderService {OrderSubmitVO submit(OrdersSubmitDTO ordersSubmitDTO);
}
// ServiceImpl———————————————————
Service
public class OrderServiceImpl implements OrderService {Autowiredprivate OrderMapper orderMapper;Autowiredprivate OrderDetailMapper orderDetailMapper;Autowiredprivate AddressBookMapper addressBookMapper;Autowiredprivate ShoppingCartMapper shoppingCartMapper;OverrideTransactional // 事务注解确保方法内所有操作在同一个事务中public OrderSubmitVO submit(OrdersSubmitDTO ordersSubmitDTO) {//一、处理各种业务异常(地址为空、购物车为空)AddressBook addressBook addressBookMapper.getById(ordersSubmitDTO.getAddressBookId());if (addressBook null) {// 抛出业务异常throw new AddressBookBusinessException(MessageConstant.ADDRESS_BOOK_IS_NULL);}Long userId BaseContext.getCurrentId(); // 获取当前用户IDShoppingCart shoppingCart new ShoppingCart();shoppingCart.setUserId(userId);ListShoppingCart shoppingCartList shoppingCartMapper.list(shoppingCart);if (shoppingCartList null || shoppingCartList.size() 0) {// 抛出业务异常throw new ShoppingCartBusinessException(MessageConstant.SHOPPING_CART_IS_NULL);}// 构造订单数据Orders orders new Orders();BeanUtils.copyProperties(ordersSubmitDTO, orders); // 复制属性orders.setOrderTime(LocalDateTime.now()); // 设置订单时间orders.setPayStatus(Orders.UN_PAID); // 设置支付状态orders.setStatus(Orders.PENDING_PAYMENT); // 设置订单状态orders.setNumber(String.valueOf(System.currentTimeMillis())); // 设置订单编号orders.setPhone(addressBook.getPhone()); // 设置联系电话orders.setConsignee(addressBook.getConsignee()); // 设置收货人orders.setUserId(userId); // 设置用户IDorders.setAddress(addressBook.getDetail()); // 设置地址//二、向订单表插入一条数据orderMapper.insert(orders);// 向订单明细表插入多条数据ListOrderDetail orderDetailList new ArrayList();for (ShoppingCart cart : shoppingCartList) {OrderDetail orderDetail new OrderDetail(); // 创建订单明细对象BeanUtils.copyProperties(cart, orderDetail); // 复制属性orderDetail.setOrderId(orders.getId()); // 设置当前订单明细关联的订单IDorderDetailList.add(orderDetail); // 添加到订单明细列表}//三、向订单明细表批量插入数据orderDetailMapper.insertBatch(orderDetailList);//四、清空当前用户的购物车数据shoppingCartMapper.cleanByUserId(userId);//五、封装对象并返回结果OrderSubmitVO submitVO OrderSubmitVO.builder().id(orders.getId()).orderTime(orders.getOrderTime()).orderNumber(orders.getNumber()).orderAmount(orders.getAmount()).build();return submitVO;}
}
// Mapper———————————————————————
Mapper
public interface OrderMapper {void insert(Orders orders);
}
Mapper
public interface OrderDetailMapper {void insertBatch(ListOrderDetail orderDetailList);
}
mapper namespacecom.sky.mapper.OrderMapperinsert idinsert useGeneratedKeystrue keyPropertyidinsert into orders (number, status, user_id, address_book_id, order_time, checkout_time, pay_method, pay_status,amount, remark, phone, address, user_name, consignee, cancel_reason, rejection_reason, cancel_time,estimated_delivery_time, delivery_status, delivery_time, pack_amount, tableware_number, tableware_status)values (#{number}, #{status}, #{userId}, #{addressBookId}, #{orderTime}, #{checkoutTime}, #{payMethod}, #{payStatus},#{amount}, #{remark}, #{phone}, #{address}, #{userName}, #{consignee}, #{cancelReason}, #{rejectionReason}, #{cancelTime},#{estimatedDeliveryTime}, #{deliveryStatus}, #{deliveryTime}, #{packAmount}, #{tablewareNumber}, #{tablewareStatus})/insert
/mapper
mapper namespacecom.sky.mapper.OrderDetailMapperinsert idinsertBatchinsert into order_detail (name, image, order_id, dish_id, setmeal_id, dish_flavor, number, amount)valuesforeach collectionorderDetailList itemod separator,(#{od.name}, #{od.image}, #{od.orderId}, #{od.dishId}, #{od.setmealId}, #{od.dishFlavor}, #{od.number},#{od.amount})/foreach/insert
/mapper 测试成功页面 订单支付
功能介绍 本项目采用微信支付但因为小程序的支付功能必须是商户注册才能开通如果在注册小程序时使用的是个人注册是无法实现该功能的因此我们只学习实现流程但并不真正的实现支付功能。 接入微信支付共需三步
一、提交资料 在线提交营业执照、身份证、银行账户等基本信息并按指引完成账户验证
二、签署协议 微信支付团队会在1-2个工作日内完成审核审核通过后请在线签约即可体验各项产品能力
三、绑定场景 如需自行开发完成收款需将商户号与APPID进行绑定或开通微信收款商业版免开发完成收款 这些一般由相关人员完成我们了解即可。目前微信支付支持的支付产品有多种如付款码支付、JSAPI支付、小程序支付、Native支付、APP支付、刷脸支付、刷掌支付。 因为我们目前开发的是小程序支付因此主要介绍这一种。下图为小程序支付时序图 首先是微信用户下单—商户系统返回订单号等信息—小程序向后端申请微信支付—后端调用微信下单接口(即发起请求)。但此时只是发起了订单并未支付。微信方会返回预交易标识字符串后端为了安全需再次对该数据进行处理并签名 再将处理后的数据返回到小程序端。 此时小程序端会弹出支付界面点击确认支付并输入密码后会调用wx.requestPayment方法并将刚刚获取的数据及其他数据返回给微信方微信方再返回支付结果小程序端再予以显示支付结果。 此时支付已完成但后端并无数据微信方还会推送支付结果到后端后端收到后予以处理并更新订单相关数据。 重要的有三步后端调用微信下单、小程序端调起微信支付、微信端推送支付结果。我们依次来看。
一、后端调用微信JSAPI下单接口在微信支付服务台生成预支付交易单。该接口请求URL: https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi请求方式: POST提交规定的的json格式数据
{// 商户注册所得的商户号mchid: 1900006XXX,// 商户系统内部订单号要求32个字符内只能是数字、大小写字母_-|* 且在同一个商户号下唯一。out_trade_no: 1217752501201407033233368318,// 应用IDappid: wxdace645e0bc2cXXX,// 商品简单描述该字段请按照以下规则填写商品名称示例腾讯充值中心-QQ会员充值description: Image形象店-深圳腾大-QQ公仔,// 接收微信支付异步通知回调地址通知url必须为直接可访问的URL不能携带参数。notify_url: https://www.weixin.qq.com/wxpay/pay.php,// 订单金额信息amount: {// 总金额单位为分total: 1, // 货币类型currency: CNY},// 支付人信息payer: {// 用户在直连商户appid下的唯一标识。openid: 04GgauInH_RCEdvrrNGrntXDuxXX}
}微信端会返回预支付交易会话标识prepay_id有效期为两小时
{prepay_id : wx201410272009395522657a690389285100
}
二、通过小程序下单接口获取到发起支付的必要参数prepay_id然后使用微信支付提供的小程序方法wx.requestPayment(OBJECT)调起微信支付。以下为Object请求参数。 其内部还包含了三个回调函数分别代表不同的结果
wx.requestPayment({// 时间戳从1970年1月1日00:00:00至今的秒数即当前的时间timeStamp: 1414561699,// 随机字符串不长于32位nonceStr: 5K8264ILTKCH16CQ2502SI8ZNMTM67VS,// 统一下单接口返回的 prepay_id 参数值提交格式如prepay_id***package: prepay_idwx201410272009395522657a690389285100,// 签名方式默认为MD5支持HMAC-SHA256和RSAsignType: RSA,// 签名具体签名方案参见小程序支付接口文档paySign: oR9d8PuhnIcYZ8cBHFCwfgpaK9gd7vaRvkYD7rthRAZ\/XQBhcCYL21N7cHCTUxbQEAt6UylwSN22f5YZvI45MLko8Pfso0jm46v5hqcVwrk6uddkGuTCdvu4WBqDzaDjnNa5UK3GfE1Wfl2gHxIIY5lLdUgWFts17D4WuolLLkiFZVJSHMvH7eaLdT9N5GBovBwu5yYKUR7skR8FuLozcSqQixnlEZUfyE55feLOQTUYzLmR9pNtPbPsu6WVhbNHMS3Ss2AehHvzn64GDmXxbXIOBvm2olHu3PsOUGRwhudhVf7UcGcunXt8cqNjKNqZLhLw4jq\/xDg,// 接口调用成功的回调函数success: function(res) {// 成功处理逻辑},// 接口调用失败的回调函数fail: function(res) {// 失败处理逻辑},// 接口调用结束的回调函数调用成功、失败都会执行complete: function(res) {// 完成处理逻辑}
})三、第三步由导入的PayNotifyController类完成后文会介绍
准备工作 在编写代码前我们还需做些准备工作。 首先是后端与微信端交互时涉及到了大量的数据交互我们需要对其进行一定的处理以确保数据的安全。 其次我们之前访问后端都是直接访问localhost本地服务器这就导致了外部的设备无法与本地服务器进行交互因此我们需要内网穿透功能来获取临时IP。 首先是数据处理我们需要从微信商户平台下载两文件
获取微信支付平台证书apiclient_key.pem商户私钥文件:wechatpay_166D96F876F45C7D07CE98952A96EC980368ACFC.pem 后续会使用到这两文件。 然后是实现内网穿透我们需要借助工具cpolar先前往官网注册并下载软件
cpolar下载地址https://dashboard.cpolar.com/get-startedhttps://dashboard.cpolar.com/get-startedhttps://dashboard.cpolar.com/get-started 然后回到网站点击左侧的验证并复制Authtoken 然后回到安装目录并执行cmd输入cpolar.exe authtoken 刚刚复制的token回车系统会在指定目录下生成一yml文件(该步骤只需执行一次即生成文件后便不需要再次这样操作) //cmd窗口弹出生成的yml文件存储位置
Authtoken saved to configuration file: C:\Users\chn/.cpolar/cpolar.yml 执行完上述步骤后回到cmd窗口输入cpolar.exe http 8080(此处的8080为后端接口需与自己的后端接口对应)我们就可以启动服务获取临时ip地址。cmd窗口弹出
cpolar by bestexpresser (CtrlC to quit)Tunnel Status online
Account aaaa (Plan: Free)
Version 2.86.16/3.18
Web Interface 127.0.0.1:4042
Forwarding https://12cfe0d9.r9.cpolar.top - http://localhost:8080
Forwarding http://12cfe0d9.r9.cpolar.top - http://localhost:8080
# Conn 0
Avg Conn Time 0.00ms 根据弹出信息我们就可以得知我们可以通过http://12cfe0d9.r9.cpolar.top来取代http://localhost:8080例如外网可以通过http://12cfe0d9.r9.cpolar.top/doc.html来访问该项目的接口文档(第一次访问较慢等待即可)。 注意因为我们目前仍处于学习阶段电脑大部分时间都处于局域网之内并无公网ip因此需要这样获取临时ip但实际开发中项目上线后一般都会有公网ip我们直接使用即可。
代码导入 因为微信支付的代码较为固定因此我们直接导入即可。 首先配置微信支付所需的配置项
//application.yml——————————————————————————
sky:......wechat:# 小程序的appidappid: ${sky.wechat.appid}# 小程序的秘钥secret: ${sky.wechat.secret}# 商户号mchId: ${sky.wechat.mchId}# 商户API证书的证书序列号mchSerialNo: ${sky.wechat.mchSerialNo}# 商户私钥文件路径privateKeyFilePath: ${sky.wechat.privateKeyFilePath}# 证书解密的密钥apiV3Key: ${sky.wechat.apiV3Key}# 平台证书路径weChatPayCertFilePath: ${sky.wechat.weChatPayCertFilePath}# 支付成功的回调地址notifyUrl: ${sky.wechat.notifyUrl}# 退款成功的回调地址refundNotifyUrl: ${sky.wechat.refundNotifyUrl}
//application-dev.yml——————————————————————————————————————————
sky:......wechat:# 微信公众号或小程序的AppIDappid: wxe8b6f903deb8566b# 微信公众号或小程序的AppSecretsecret: 23d7d1bc0eed6b49ef7e58bc0cc6a296# 微信支付分配的商户号mchid: 1561414331# 商户API证书的证书序列号mchSerialNo: 4B3B3DC35414AD50B1B755BAF8DE9CC7CF407606# 商户私钥文件的路径privateKeyFilePath: D:\pay\apiclient_key.pem# APIv3密钥用于签名和解密apiV3Key: CZBK51236435wxpay435434323FFDuv3# 微信支付平台证书文件的路径weChatPayCertFilePath: D:\pay\wechatpay_166D96F876F45C7D07CE98952A96EC980368ACFC.pem# 支付成功的回调地址notifyUrl: https://6619cf50.r6.cpolar.top/notify/paySuccess# 退款成功的回调地址refundNotifyUrl: https://6619cf50.r6.cpolar.top/notify/refundSuccess 回到server模块的controller包user包OrderController中添加相关方法
// OrderController———————————————————PutMapping(/payment)ApiOperation(订单支付)public ResultOrderPaymentVO payment(RequestBody OrdersPaymentDTO ordersPaymentDTO) throws Exception {log.info(订单支付{}, ordersPaymentDTO);OrderPaymentVO orderPaymentVO orderService.payment(ordersPaymentDTO);log.info(生成预支付交易单{}, orderPaymentVO);return Result.success(orderPaymentVO);}
// OrderService———————————————————————OrderPaymentVO payment(OrdersPaymentDTO ordersPaymentDTO) throws Exception;void paySuccess(String outTradeNo);
}
// OrderServiceImpl———————————————————Autowiredprivate UserMapper userMapper;Autowiredprivate WeChatPayUtil weChatPayUtil;public OrderPaymentVO payment(OrdersPaymentDTO ordersPaymentDTO) throws Exception {// 当前登录用户idLong userId BaseContext.getCurrentId();User user userMapper.getById(userId);//调用微信支付接口生成预支付交易单JSONObject jsonObject weChatPayUtil.pay(ordersPaymentDTO.getOrderNumber(), //商户订单号new BigDecimal(0.01), //支付金额单位 元苍穹外卖订单, //商品描述user.getOpenid() //微信用户的openid);if (jsonObject.getString(code) ! null jsonObject.getString(code).equals(ORDERPAID)) {throw new OrderBusinessException(该订单已支付);}OrderPaymentVO vo jsonObject.toJavaObject(OrderPaymentVO.class);vo.setPackageStr(jsonObject.getString(package));return vo;}public void paySuccess(String outTradeNo) {// 根据订单号查询订单Orders ordersDB orderMapper.getByNumber(outTradeNo);// 根据订单id更新订单的状态、支付方式、支付状态、结账时间Orders orders Orders.builder().id(ordersDB.getId()).status(Orders.TO_BE_CONFIRMED).payStatus(Orders.PAID).checkoutTime(LocalDateTime.now()).build();orderMapper.update(orders);}
// OrderMapper———————————————————————Select(select * from orders where number #{orderNumber})Orders getByNumber(String orderNumber);Select(select * from user where id #{id})User getById(Long Id);void update(Orders orders);
!--OrderMapper--update idupdate parameterTypecom.sky.entity.Ordersupdate orderssetif testcancelReason ! null and cancelReason! cancel_reason#{cancelReason}, /ifif testrejectionReason ! null and rejectionReason! rejection_reason#{rejectionReason}, /ifif testcancelTime ! null cancel_time#{cancelTime}, /ifif testpayStatus ! null pay_status#{payStatus}, /ifif testpayMethod ! null pay_method#{payMethod}, /ifif testcheckoutTime ! null checkout_time#{checkoutTime}, /ifif teststatus ! null status #{status}, /ifif testdeliveryTime ! null delivery_time #{deliveryTime} /if/setwhere id #{id}/update 然后在controller包下新建notify包并将PayNotifyController类复制进去(该类用于接收微信端推送的支付结果)
//支付回调相关接口
RestController
RequestMapping(/notify)
Slf4j
public class PayNotifyController {Autowiredprivate OrderService orderService;Autowiredprivate WeChatProperties weChatProperties;// 支付成功回调RequestMapping(/paySuccess)public void paySuccessNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {//读取数据String body readData(request);log.info(支付成功回调{}, body);//数据解密String plainText decryptData(body);log.info(解密后的文本{}, plainText);JSONObject jsonObject JSON.parseObject(plainText);String outTradeNo jsonObject.getString(out_trade_no);//商户平台订单号String transactionId jsonObject.getString(transaction_id);//微信支付交易号log.info(商户平台订单号{}, outTradeNo);log.info(微信支付交易号{}, transactionId);//业务处理修改订单状态、来单提醒orderService.paySuccess(outTradeNo);//给微信响应responseToWeixin(response);}// 读取数据private String readData(HttpServletRequest request) throws Exception {BufferedReader reader request.getReader();StringBuilder result new StringBuilder();String line null;while ((line reader.readLine()) ! null) {if (result.length() 0) {result.append(\n);}result.append(line);}return result.toString();}//数据解密private String decryptData(String body) throws Exception {JSONObject resultObject JSON.parseObject(body);JSONObject resource resultObject.getJSONObject(resource);String ciphertext resource.getString(ciphertext);String nonce resource.getString(nonce);String associatedData resource.getString(associated_data);AesUtil aesUtil new AesUtil(weChatProperties.getApiV3Key().getBytes(StandardCharsets.UTF_8));//密文解密String plainText aesUtil.decryptToString(associatedData.getBytes(StandardCharsets.UTF_8),nonce.getBytes(StandardCharsets.UTF_8),ciphertext);return plainText;}// 给微信响应private void responseToWeixin(HttpServletResponse response) throws Exception{response.setStatus(200);HashMapObject, Object map new HashMap();map.put(code, SUCCESS);map.put(message, SUCCESS);response.setHeader(Content-type, ContentType.APPLICATION_JSON.toString());response.getOutputStream().write(JSONUtils.toJSONString(map).getBytes(StandardCharsets.UTF_8));response.flushBuffer();}
}跳过支付 因为我们以个体注册的小程序无法进行支付所以需修改代码跳过微信支付这一步具体方法可参考该文章
跳过微信支付https://blog.csdn.net/2301_79693537/article/details/140846695
用户端订单操作
查询历史订单 请求路径为/user/order/historyOrders请求方法为getQuery传入三个参数page、pageSize、status分别代表页面、每页记录数、订单状态。
// Controller———————————————————GetMapping(/historyOrders)ApiOperation(历史订单查询)public ResultPageResult page(int page, int pageSize, Integer status) {PageResult pageResult orderService.pageQuery4User(page, pageSize, status);return Result.success(pageResult);}
// Service———————————————————————PageResult pageQuery4User(int page, int pageSize, Integer status);
// ServiceImpl———————————————————Overridepublic PageResult pageQuery4User(int page, int pageSize, Integer status) {//需要在查询功能之前开启分页功能当前页的页码 每页显示的条数PageHelper.startPage(page, pageSize);//封装所需的请求参数为DTO对象OrdersPageQueryDTO ordersPageQueryDTO new OrdersPageQueryDTO();ordersPageQueryDTO.setUserId(BaseContext.getCurrentId());ordersPageQueryDTO.setStatus(status);// 分页条件查询PageOrders ordersPage orderMapper.pageQuery(ordersPageQueryDTO);//由接口可知需要封装为orderVO类型订单菜品信息orderDishes订单详情orderDetailListListOrderVO list new ArrayList();// 查询出订单明细并封装入OrderVO进行响应if (ordersPage ! null ordersPage.getTotal() 0) { //有订单才有必要接着查询订单详情信息for (Orders orders : ordersPage) {Long orderId orders.getId();// 订单id// 根据订单id,查询订单明细ListOrderDetail orderDetails orderDetailMapper.getByOrderId(orderId);OrderVO orderVO new OrderVO();BeanUtils.copyProperties(orders, orderVO);orderVO.setOrderDetailList(orderDetails);list.add(orderVO);}}return new PageResult(ordersPage.getTotal(), list);}
// Mapper———————————————————————
public interface OrderDetailMapper {Select(select * from order_detail where order_id#{orderId})ListOrderDetail getByOrderId(Long orderId);
}
public interface OrderMapper {PageOrders pageQuery(OrdersPageQueryDTO ordersPageQueryDTO);
} 查询订单详情 请求路径为/user/order/orderDetail/{id}请求方法为getPath传入参数id意为订单id。
// Controller———————————————————GetMapping(/orderDetail/{id})ApiOperation(根据订单ID查看订单详情)public ResultOrderVO OrderDetailById(PathVariable Long id) {OrderVO orderVO orderService.OrderDetailById(id);return Result.success(orderVO);}
// Service———————————————————————OrderVO OrderDetailById(Long id);
// ServiceImpl———————————————————Overridepublic OrderVO OrderDetailById(Long id) {//根据id查询订单OrderVO要用Orders ordersorderMapper.getById(id);//根据订单查询订单详情ListOrderDetail orderDetails orderDetailMapper.getByOrderId(id);// 将结果封装到OrderVO并返回OrderVO orderVO new OrderVO();BeanUtils.copyProperties(orders, orderVO);orderVO.setOrderDetailList(orderDetails);return orderVO;}
// Mapper———————————————————————Select(select * from orders where id #{id})Orders getById(Long id); 取消订单 请求路径为/user/order/cancel/{id}请求方法为putPath传入参数id意为订单id。 待支付和待接单状态下用户可直接取消订单(status为1或2)其他状态下则抛出异常。如果在待接单状态下取消订单需要给用户退款因为无法实现微信接口的退款本项目以控制台输出XX订单已退款来代替微信退款。取消订单后需要将订单状态修改为“已取消”。
// Controller———————————————————PutMapping(/cancel/{id})ApiOperation(根据订单ID取消订单)public ResultOrderVO cancelOrderById(PathVariable Long id) throws Exception {orderService.cancelOrderById(id);return Result.success();}
// Service———————————————————————void cancelOrderById(Long id) throws Exception;
// ServiceImpl———————————————————Overridepublic void cancelOrderById(Long id) throws Exception {// 根据id查询订单Orders ordersDB orderMapper.getById(id);// 校验订单是否存在if (ordersDB null) {throw new OrderBusinessException(MessageConstant.ORDER_NOT_FOUND);}//订单状态 1待付款 2待接单 3已接单 4派送中 5已完成 6已取消if (ordersDB.getStatus() 2) {throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);}//以上验证都通过后此时订单处于待支付和待接单状态下Orders orders new Orders();orders.setId(ordersDB.getId());// 订单处于待接单状态下取消需要进行退款if (ordersDB.getStatus().equals(Orders.TO_BE_CONFIRMED)) {//调用微信支付退款接口,因为无法调用所以仅做示范/*weChatPayUtil.refund(ordersDB.getNumber(), //商户订单号ordersDB.getNumber(), //商户退款单号new BigDecimal(0.01),//退款金额单位 元new BigDecimal(0.01));//原订单金额*/log.info(订单{}已退款,ordersDB.getId());//支付状态修改为 退款orders.setPayStatus(Orders.REFUND);}// 更新订单状态、取消原因、取消时间orders.setStatus(Orders.CANCELLED);orders.setCancelReason(用户取消);orders.setCancelTime(LocalDateTime.now());orderMapper.update(orders);} 再来一单 请求路径为/user/order/repetition/{id}请求方法为PostPath传入参数id意为订单id。再来一单意为将原订单中的商品重新加入到购物车中。 小程序会先发起清空购物车的请求然后再发起再来一单的请求后台响应请求后小程序再跳转到点餐页并读取购物车中的数据。
// Controller———————————————————PostMapping(/repetition/{id})ApiOperation(再来一单)public Result oneMore(PathVariable Long id){orderService.oneMore(id);return Result.success();}
// Service———————————————————————void oneMore(Long id);
// ServiceImpl———————————————————public void oneMore(Long id) {// 获取当前用户的IDLong userId BaseContext.getCurrentId();// 根据提供的订单ID查询订单详情列表ListOrderDetail orderDetailList orderDetailMapper.getByOrderId(id);// 将每个订单详情转换为购物车项ListShoppingCart shoppingCartList orderDetailList.stream().map(orderDetail - {// 创建新的购物车对象ShoppingCart shoppingCart new ShoppingCart();// 复制订单详情到购物车对象排除ID属性BeanUtils.copyProperties(orderDetail, shoppingCart, id);// 设置购物车项的用户ID为当前用户IDshoppingCart.setUserId(userId);// 设置购物车项的创建时间为当前时间shoppingCart.setCreateTime(LocalDateTime.now());// 返回转换后的购物车对象return shoppingCart;}).collect(Collectors.toList()); // 收集转换后的购物车对象列表// 批量插入购物车项到数据库shoppingCartMapper.insertBatch(shoppingCartList);}
// Mapper———————————————————————
public interface ShoppingCartMapper {void insertBatch(ListShoppingCart shoppingCartList);
} insert idinsertBatch parameterTypelistinsert into shopping_cart(name, image, user_id, dish_id, setmeal_id, dish_flavor, number, amount, create_time)valuesforeach collectionshoppingCartList itemsc separator,(#{sc.name},#{sc.image},#{sc.userId},#{sc.dishId},#{sc.setmealId},#{sc.dishFlavor},#{sc.number},#{sc.amount},#{sc.createTime})/foreach/insert 商家端订单操作
订单查询 请求路径为/admin/order/conditionSearch请求方法为get请求方式为Query传参里page和pagesize为必须beginTime、endTime、number、phone 和 status 则是可选。 使用OrdersPageQueryDTO类来接收因为返回数据中有一项orderDishes为菜品信息其在Orders中并不存在需查询菜品详情表后返回对应的数据(例如宫保鸡丁*3;)因此因此在Impl中查询的返回类型为orderVO的集合orderVOList。 回到controller包的admin部分创建OrderController并编写代码
// Controller———————————————————
RestController(adminOrderController)
RequestMapping(/admin/order)
Slf4j
Api(tags 订单管理接口)
public class OrderController {Autowiredprivate OrderService orderService;//订单查询GetMapping(/conditionSearch)ApiOperation(订单查询)public ResultPageResult OrderQuery(OrdersPageQueryDTO ordersPageQueryDTO) {PageResult pageResult orderService.OrderQuery(ordersPageQueryDTO);return Result.success(pageResult);}
}
// Service———————————————————————PageResult OrderQuery(OrdersPageQueryDTO ordersPageQueryDTO);
// ServiceImpl———————————————————Overridepublic PageResult OrderQuery(OrdersPageQueryDTO ordersPageQueryDTO) {PageHelper.startPage(ordersPageQueryDTO.getPage(), ordersPageQueryDTO.getPageSize());PageOrders page orderMapper.pageQuery(ordersPageQueryDTO);// 部分订单状态需要额外返回订单菜品信息将Orders转化为OrderVO调用自定义方法ListOrderVO orderVOList getOrderVOList(page);return new PageResult(page.getTotal(), orderVOList);}//将的Orders对象转换为OrderVO对象列表。private ListOrderVO getOrderVOList(PageOrders page) {// 需要返回订单菜品信息自定义OrderVO响应结果ListOrderVO orderVOList new ArrayList();ListOrders ordersList page.getResult();//CollectionUtils工具类用于判断ordersList集合是否为空if (!CollectionUtils.isEmpty(ordersList)) {for (Orders orders : ordersList) {// 将共同字段复制到OrderVOOrderVO orderVO new OrderVO();BeanUtils.copyProperties(orders, orderVO);//调用自定义方法String orderDishes getOrderDishesStr(orders);// 将订单菜品信息封装到orderVO中并添加到orderVOListorderVO.setOrderDishes(orderDishes);orderVOList.add(orderVO);}}return orderVOList;}//根据订单id获取菜品信息字符串private String getOrderDishesStr(Orders orders) {// 查询订单菜品详情信息订单中的菜品和数量ListOrderDetail orderDetailList orderDetailMapper.getByOrderId(orders.getId());// 将每一条订单菜品信息拼接为字符串格式宫保鸡丁*3ListString orderDishList orderDetailList.stream().map(x - {String orderDish x.getName() * x.getNumber() ;;return orderDish;}).collect(Collectors.toList());// 将该订单对应的所有菜品信息拼接在一起return String.join(, orderDishList);}各个状态的订单数量统计 即红点中的数字当有新的未处理的订单时会通过红点来提醒管理者。 请求路径为/admin/order/statistics请求方法为get无请求参数。返回的数据为OrderStatisticsVO包含三个变量confirmed、deliveryInProgress、toBeConfirmed分别意为待派送数量、派送中数量、待接单数量。
// Controller———————————————————GetMapping(/statistics)ApiOperation(各个状态的订单数量统计)public ResultOrderStatisticsVO statistics() {OrderStatisticsVO orderStatisticsVO orderService.statistics();return Result.success(orderStatisticsVO);}
// Service———————————————————————OrderStatisticsVO statistics();
// ServiceImpl———————————————————public OrderStatisticsVO statistics() {// 根据状态分别查询出待接单、待派送、派送中的订单数量Integer toBeConfirmed orderMapper.countStatus(Orders.TO_BE_CONFIRMED);Integer confirmed orderMapper.countStatus(Orders.CONFIRMED);Integer deliveryInProgress orderMapper.countStatus(Orders.DELIVERY_IN_PROGRESS);// 将查询出的数据封装到orderStatisticsVO中响应OrderStatisticsVO orderStatisticsVO new OrderStatisticsVO();orderStatisticsVO.setToBeConfirmed(toBeConfirmed);orderStatisticsVO.setConfirmed(confirmed);orderStatisticsVO.setDeliveryInProgress(deliveryInProgress);//也可简写为
// OrderStatisticsVO orderStatisticsVO new OrderStatisticsVO(
// orderMapper.countStatus(Orders.TO_BE_CONFIRMED),
// orderMapper.countStatus(Orders.CONFIRMED),
// orderMapper.countStatus(Orders.DELIVERY_IN_PROGRESS));return orderStatisticsVO;}
// Mapper———————————————————————Select(select count(id) from orders where status #{status})Integer countStatus(Integer status); Impl中获取值并赋给OrderStatisticsVO对象的语句可直接使用构造函数代替但因为OrderStatisticsVO中并无构造函数若补充注解AllArgsConstructor其又会覆盖掉无参构造还需添加注解NoArgsConstructor代码变动较多我们作为初学者便不再尝试。 查询订单详情 请求路径为/admin/order/details/{id}请求方法为getPath传入参数id意为订单id。 该功能之前已实现直接在controller层调用orderService.OrderDetailById(id);并返回结果即可。
// Controller———————————————————GetMapping(/details/{id})ApiOperation(查询订单详情)public ResultOrderVO getDetailsById(PathVariable(id) Long id) {OrderVO orderVO orderService.OrderDetailById(id);//已实现return Result.success(orderVO);}接单 就是将订单的状态修改为3(已接单)。 请求路径为/admin/order/confirm请求方法为Put以json格式提交id后端使用OrdersConfirmDTO类来接收。(这里就一个id参数为什么不使用Path格式传参好怪)
// Controller———————————————————PutMapping(/confirm)ApiOperation(接单)public Result confirm(RequestBody OrdersConfirmDTO ordersConfirmDTO) {orderService.confirm(ordersConfirmDTO);return Result.success();}
// Service———————————————————————void confirm(OrdersConfirmDTO ordersConfirmDTO);
// ServiceImpl———————————————————public void confirm(OrdersConfirmDTO ordersConfirmDTO) {Orders orders Orders.builder().id(ordersConfirmDTO.getId()).status(Orders.CONFIRMED).build();orderMapper.update(orders);}拒单 与接单同理就是将订单状态修改为6(已取消)不过多了些业务逻辑只有订单处于“待接单”状态时可以执行拒单操作、商家拒单时需要指定拒单原因、商家拒单时如果用户已经完成了支付需要为用户退款。 请求路径为/admin/order/rejection请求方法为Put以json格式提交id和rejectionReason后端使用OrdersRejectionDTO类来接收。
// Controller———————————————————PutMapping(/rejection)ApiOperation(拒单)public Result rejection(RequestBody OrdersRejectionDTO ordersRejectionDTO) throws Exception {orderService.rejection(ordersRejectionDTO);return Result.success();}
// Service———————————————————————void rejection(OrdersRejectionDTO ordersRejectionDTO) throws Exception;
// ServiceImpl———————————————————Overridepublic void rejection(OrdersRejectionDTO ordersRejectionDTO) throws Exception {// 根据id查询订单Orders ordersDB orderMapper.getById(ordersRejectionDTO.getId());// 订单只有存在且状态为2待接单才可以拒单if (ordersDB null || !ordersDB.getStatus().equals(Orders.TO_BE_CONFIRMED)) {throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);}//支付状态Integer payStatus ordersDB.getPayStatus();if (Objects.equals(payStatus, Orders.PAID)) {
// //用户已支付需要退款(微信支付跳过)
// String refund weChatPayUtil.refund(
// ordersDB.getNumber(),
// ordersDB.getNumber(),
// new BigDecimal(0.01),
// new BigDecimal(0.01));
// log.info(申请退款{}, refund);log.info({}申请退款, BaseContext.getCurrentId());}// 拒单需要退款根据订单id更新订单状态、拒单原因、取消时间Orders orders new Orders();orders.setId(ordersDB.getId());orders.setStatus(Orders.CANCELLED);orders.setRejectionReason(ordersRejectionDTO.getRejectionReason());orders.setCancelTime(LocalDateTime.now());orderMapper.update(orders);}取消订单 同理取消订单也是将订单状态修改为6(已取消)但业务规则不一样商家取消订单时需要指定取消原因、商家取消订单时如果用户已经完成了支付需要为用户退款。 请求路径为/admin/order/cancel请求方法为put以json格式提交id和cancelReason后端使用OrdersRejectionDTO类来接收。
// Controller———————————————————PutMapping(/cancel)ApiOperation(取消订单)public Result cancel(RequestBody OrdersCancelDTO ordersCancelDTO) throws Exception {orderService.cancel(ordersCancelDTO);return Result.success();}
// Service———————————————————————void cancel(OrdersCancelDTO ordersCancelDTO) throws Exception;
// ServiceImpl———————————————————Overridepublic void cancel(OrdersCancelDTO ordersCancelDTO) throws Exception {// 根据id查询订单Orders ordersDB orderMapper.getById(ordersCancelDTO.getId());//支付状态Integer payStatus ordersDB.getPayStatus();if (payStatus 1) {
// //用户已支付需要退款(微信支付跳过)
// String refund weChatPayUtil.refund(
// ordersDB.getNumber(),
// ordersDB.getNumber(),
// new BigDecimal(0.01),
// new BigDecimal(0.01));
// log.info(申请退款{}, refund);log.info({}申请退款, BaseContext.getCurrentId());}// 管理端取消订单需要退款根据订单id更新订单状态、取消原因、取消时间Orders orders new Orders();orders.setId(ordersCancelDTO.getId());orders.setStatus(Orders.CANCELLED);orders.setCancelReason(ordersCancelDTO.getCancelReason());orders.setCancelTime(LocalDateTime.now());orderMapper.update(orders);}派送订单 将订单状态修改为4(派送中)只有状态为“待派送”的订单可以执行派送订单操作即status为3。 请求路径为/admin/order/delivery/{id}请求方法为putPath传入参数id意为订单id。
// Controller———————————————————PutMapping(/delivery/{id})ApiOperation(派送订单)public Result onTheWay(PathVariable Long id) {orderService.onTheWay(id);return Result.success();}
// Service———————————————————————void onTheWay(Long id);
// ServiceImpl———————————————————Overridepublic void onTheWay(Long id) {Orders orderDB orderMapper.getById(id);// 校验订单是否存在并且状态为3if (orderDB null || !orderDB.getStatus().equals(Orders.CONFIRMED)) {//抛出异常订单状态错误throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);}Orders orders new Orders();orders.setId(id);// 更新订单状态,状态转为4(派送中)orders.setStatus(Orders.DELIVERY_IN_PROGRESS);orderMapper.update(orders);}完成订单 将订单状态修改为5(已完成)只有状态为“派送中”(即status为3)的订单可以执行派送订单操作。 请求路径为/admin/order/complete/{id}请求方法为putPath传入参数id意为订单id。
// Controller———————————————————PutMapping(/complete/{id})ApiOperation(完成订单)public Result complete(PathVariable(id) Long id) {orderService.complete(id);return Result.success();}
// Service———————————————————————void complete(Long id);
// ServiceImpl———————————————————Overridepublic void complete(Long id) {Orders orderDB orderMapper.getById(id);// 校验订单是否存在并且状态为4if (orderDB null || !orderDB.getStatus().equals(Orders.DELIVERY_IN_PROGRESS)) {//抛出异常订单状态错误throw new OrderBusinessException(MessageConstant.ORDER_STATUS_ERROR);}Orders orders new Orders();orders.setId(id);// 更新订单状态,状态转为5(已完成)orders.setStatus(Orders.COMPLETED);orderMapper.update(orders);}下单功能优化 提示完成该模块很麻烦且不实现该功能也不影响推荐了解即可。 优化用户下单功能加入校验逻辑如果用户的收货地址距离商家门店超出配送范围配送范围为5公里内则下单失败。 进入百度地图开放平台并登陆账号、完善相关信息
百度地图开放平台https://lbsyun.baidu.com/ 进入控制台创建应用获取AK应用类型选择服务端。ip白名单尽量写0.0.0.0/0即不对ip做任何限制。 百度地图开发平台的AKAccess Key是一种用于识别用户身份并控制访问权限的密钥。它类似于一个“通行证”用于在调用百度地图开放平台提供的各种API服务时进行身份验证确保只有经过授权的用户才能使用这些服务。 回到项目配置相关信息
//application.yml——————————————————————————
sky:......shop:address: ${sky.shop.address}baidu:ak: ${sky.baidu.ak}
//application-dev.yml——————————————————————
sky:......shop:address: 北京市海淀区上地十街10号baidu:ak: 刚刚获取的ak 然后回到OrderServiceImpl中注入上面的配置项并编写校验方法
public class OrderServiceImpl implements OrderService {......Value(${sky.shop.address})private String shopAddress;Value(${sky.baidu.ak})private String ak;....../*** 检查客户的收货地址是否超出配送范围* param address*/private void checkOutOfRange(String address) {Map map new HashMap();map.put(address,shopAddress);map.put(output,json);map.put(ak,ak);//获取店铺的经纬度坐标String shopCoordinate HttpClientUtil.doGet(https://api.map.baidu.com/geocoding/v3, map);JSONObject jsonObject JSON.parseObject(shopCoordinate);if(!jsonObject.getString(status).equals(0)){throw new OrderBusinessException(店铺地址解析失败);}//数据解析JSONObject location jsonObject.getJSONObject(result).getJSONObject(location);String lat location.getString(lat);String lng location.getString(lng);//店铺经纬度坐标String shopLngLat lat , lng;map.put(address,address);//获取用户收货地址的经纬度坐标String userCoordinate HttpClientUtil.doGet(https://api.map.baidu.com/geocoding/v3, map);jsonObject JSON.parseObject(userCoordinate);if(!jsonObject.getString(status).equals(0)){throw new OrderBusinessException(收货地址解析失败);}//数据解析location jsonObject.getJSONObject(result).getJSONObject(location);lat location.getString(lat);lng location.getString(lng);//用户收货地址经纬度坐标String userLngLat lat , lng;map.put(origin,shopLngLat);map.put(destination,userLngLat);map.put(steps_info,0);//路线规划String json HttpClientUtil.doGet(https://api.map.baidu.com/directionlite/v1/driving, map);jsonObject JSON.parseObject(json);if(!jsonObject.getString(status).equals(0)){throw new OrderBusinessException(配送路线规划失败);}//数据解析JSONObject result jsonObject.getJSONObject(result);JSONArray jsonArray (JSONArray) result.get(routes);Integer distance (Integer) ((JSONObject) jsonArray.get(0)).get(distance);if(distance 5000){//配送距离超过5000米throw new OrderBusinessException(超出配送范围);}}在负责处理用户下单请求的submit方法中各种业务异常处理之后构造订单数据之前添加条件判断语句 OverrideTransactional // 事务注解确保方法内所有操作在同一个事务中public OrderSubmitVO submit(OrdersSubmitDTO ordersSubmitDTO) {//各种业务异常处理......//检查用户的收货地址是否超出配送范围checkOutOfRange(address:addressBook.getCityName() addressBook.getDistrictName() addressBook.getDetail());......// 构造订单数据} 此时如果距离过远会报错距离足够则正常下单。但小程序端因为代码问题不会出提示如果我们想要实现微信小程序的距离提醒功能可以参考该博客
苍穹外卖超出配送范围前端不提示问题解决方法https://blog.csdn.net/qq_65993561/article/details/143636095 总之就是非常麻烦晚安好梦。一篇文章写了四万字浏览器都开始卡了。