# shop **Repository Path**: wanxiaoguo/shop ## Basic Information - **Project Name**: shop - **Description**: 软件工程课程设计二级项目—— 睿乐购电商平台 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 5 - **Forks**: 0 - **Created**: 2020-06-16 - **Last Updated**: 2023-06-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ### 项目说明 + 该项目是燕山大学信息科学与工程学院(软件学院)的17级软件工程7,8班的软件工程课程设计二级项目 + 项目是2020年6月16日开始,2020年7月3日答辩结束,项目不再维护 + 项目定位在电商项目项目 ### 项目说明 ![系统架构图](img/system.jpg) ![前端业务流程图](img/front.jpg) ![后端业务流程图](img/back.jpg) ![大致执行流程](img/stream.jpg) ![全局ER图](img/database.jpg) ![订单流程图](img/shop.jpg) ### 创建git 项目 + 创建 git 仓库: ```git mkdir shop cd shop git init touch README.md git add README.md git commit -m "first commit" git remote add origin https://gitee.com/wanxiaoguo/shop.git git push -u origin master ``` + 已有仓库导入 ```git cd existing_git_repo git remote add origin https://gitee.com/wanxiaoguo/shop.git git push -u origin master ``` + git使用强推:`git push origin 分支名 --force` ### 首次使用(6.17) + 找一个目录起名shop + 使用git clone https://gitee.com/wanxiaoguo/shop.git + 第一种方式:在自己的电脑创建分支 + 第二种方式:直接再master分支开发 + 往自己的开发分支上推比如 git push origin master:dev_wan 其中origin 为远程主机名(git clone 后不需要改)master为本地分支名(直接再master就行) dev_wan:为自己需要推送到远程的分支名 + 参考提交 ```git $ git add -A $ git commit -m '提交信息' $ git push origin master:dev_wan ``` ### git bash使用参考 + tab : 密码补全 + 上键:历史命令 + 粘贴:shift+ins ### 项目结构调整与文件说明(6.17) + html文件都在 src/main/resources/static目录下 + 后台管理系统的文件位于src/main/resources/static/admin下 + 前端编写放置在 src/main/resources/static/admin/rlg下 + 接口编写再 src/main/java编写 + annotation:自定义注解 + config: 配置文件 + controller: 控制层,接口处理 + exception: 自定义异常类 + mapper: 模型层,往数据库查询数据 + popj: 数据库表对应的实体类 + service: 业务层接口 + impl: 接口实现类 + util: 工具类 + handler: 处理器 + ShopApplication: 启动器 + es.sql : 项目的数据库 + 管理员账户密码:admin ysuadmin ### 命名说明与注意事项 + 采用REST风格 + get: 获取数据 + post: 添加数据 + delete: 删除数据 + put: 修改数据 + 操作命名 + getUserById: 表示通过id获取用户 + getUserList: 表示获取用户列表 + addUser : 表示添加用户 + delUser : 表示删除用户 + updUser : 表示修改用户 + By: 表示通过某些字段进行某些操作 + Num|Count: 表示获取数据的大小 + controller说明 + 对于add操作,表里有的默认字段前端可以不传,mapper必须写,添加前需要校验,为空添加默认字段 + 在对于测试的时候:使用日志输出 ```java import org.slf4j.Logger; import org.slf4j.LoggerFactory; private Logger log = LoggerFactory.getLogger(UserController.class); log.debug("输出字段信息") ``` ### ajax参考 ```js function login() { var userName = $("#username").val(); var password = $("#password").val(); if (isNull(userName)) { alert("请输入用户名!"); return; } if (!validUserName(userName)) { alert("请输入正确的用户名!"); return; } if (isNull(password)) { alert("请输入密码!"); return; } if (!validPassword(password)) { alert("请输入正确的密码!"); return; } var data = { "username": userName, "password": password } //console.log(data); $.ajax({ type: "POST", //方法类型 dataType: "json", //预期服务器返回的数据类型 url: "http://localhost:8082/user/login", contentType: "application/json; charset=utf-8", data: JSON.stringify(data), success: function(result) { console.log(result); if (result.status == 200) { setCookie("token", result.data.userToken); setCookie("username",username); window.location.href = "index.html"; }; if (result.status == 406 ) { alert("登陆失败!请检查账号和密码!"); return; } }, error: function() { alert("接口异常,请联系管理员!"); return; } }); } ``` ### 问题记录 + 数据库的导入(6.18): `source sql文件位置` + [自定义注解解决Springboot发送post请求时string类型参数接收不到的问题(HandlerMethodArgumentResolver )](https://blog.csdn.net/CNAHYZ/article/details/105129050) + springboot2 前台传送json数据,后台接收数据(6.18) + 可以对实体类进行封装,但String不行 + 使用JSONObject 获取json 再获取键值 + 需要在参数前加上`@RequestBody`注解 ### jquery 获取表单数据 + jquery 获取 获取select标签值 + 元素内容 `$('#searchId').find('option:selected').text()` + 根据name `$("select[name='searchId'] option:selected").val();` + 根据id ` $("#searchId option:selected").val()` + 设置value为pxx的项选中 `$("#searchId").val("pxx");` + 设置text为pxx的项选中 `$("#searchId").find("option:contains('pxx')").attr("selected",true);` + ### 前后端分离方案 #### session与cookie + Cookie是浏览器(User Agent)访问一些网站后,这些网站存放在客户端的一组数据,用于使网站等跟踪用户,实现用户自定义功能。 + Cookie的Domain和Path属性标识了这个Cookie是哪一个网站发送给浏览器的;Cookie的Expires属性标识了Cookie的有 效时间,当Cookie的有效时间过了之后,这些数据就被自动删除了。 + Session是存放在服务器端的类似于HashTable结构(每一种Web开发技术的实现可能不一样,下文直接称之为HashTable)来存放用户 数据,当浏览器第一次发送请求时,服务器自动生成了一个HashTable和一个Session ID用来唯一标识这个HashTable,并将其通过响应发送到浏览器。当浏览器第二次发送请求,会将前一次服务器响应中的Session ID放在请求中一并发送到服务器上,服务器从请求中提取出Session ID,并和保存的所有Session ID进行对比,找到这个用户对应的HashTable。 #### token + 首次登录时,后端服务器判断用户账号密码正确之后,根据用户id、用户名、定义好的秘钥、过期时间 生成 token ,返回给前端 + 前端拿到后端返回的 token ,存储在 localStroage 里 + 赋值:`localStorage.setItem("token", result.data.token);` | `localStorage.token=` | `localStorage[token]=` + 读取信息:`localStorage.getItem("token")` | `localStorage.token` | `localStorage[token]` + 删除localStorage指定键对应的值:`localStorage.removeItem('token');` + 删除localStorage所有值: `localStorage.clear();` + js使用cookie保存token(cookie在http请求中,随着请求发送到服务器) + 保存token: `sessionStorage.setItem("token","value")`; + 获取token: `sessionStorage.getItem("token")` + 删除: `sessionStorage.removeItem("key"); ` + 删除保存的所有数据: `sessionStorage.clear()` + 前端每次路由跳转, 判断 localStroage 有无 token ,没有则跳转到登录页,有则请求获取用户信息,改变登录状态 + 每次请求接口,在 请求头里携带 token ```js $.ajax({ type: "post", url: areaComp, dataType: "json", contentType: "application/json", data: data, async: false, //再次添加头部信息 beforeSend: function(request) { request.setRequestHeader("token", token); }, success: function (data){}) ``` + 后端接口 判断 请求头有无 token,没有或者 token 过期,返回401 + 前端得到 401 状态码,重定向 到登录页面 + 与cookie相比较的优势: + 支持跨域访问 ,将token置于请求头中,而cookie是不支持跨域访问的; + 无状态化, 服务端无需存储token ,只需要验证token信息是否正确即可,而session需要在服务端存储,一般是通过cookie中的sessionID在服务端查找对应的session; + 无需绑定到一个特殊的身份验证 方案(传统的用户名密码登陆),只需要生成的token是符合我们预期设定的即可; + 更适用于移动端 (Android,iOS,小程序等等),像这种原生平台不支持cookie,比如说微信小程序,每一次请求都是一次会话,当然我们可以每次去手动为他添加cookie,详情请查看博主另一篇博客; + 避免CSRF跨站伪造攻击 ,还是因为不依赖cookie; + 非常适用于RESTful API ,这样可以轻易与各种后端(java,.net,python…)相结合,去耦合 ##### 参考代码 + cookie操作相关方法 ```js /** * 写入cookie * * @param name * @param value */ function setCookie(name, value) { var Days = 30; var exp = new Date(); exp.setTime(exp.getTime() + Days * 24 * 60 * 60 * 1000); document.cookie = name + "=" + escape(value) + ";expires=" + exp.toGMTString() + ";path=/"; } /** * 读取cookie * @param name * @returns {null} */ function getCookie(name) { var arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)"); if (arr = document.cookie.match(reg)) return unescape(arr[2]); else return null; } /** * 删除cookie * @param name */ function delCookie(name) { var exp = new Date(); exp.setTime(exp.getTime() - 1); if (cval != null) document.cookie = name + "=" + "xxxx;expires=" + exp.toUTCString(); } /** * 检查cookie */ function checkCookie() { if (getCookie("token") == null) { window.location.href = "login.html"; } } /** * 检查cookie */ function checkResultCode(code) { if (code == 402) { window.location.href = "login.html"; } } ``` + jquery简写 + 先引入jquery,再引入:jquery.cookie.js;下载:http://plugins.jquery.com/cookie/ ```js // 设置cookie expires是保存时间,单位是天 path : 能够访问的页面 / 表示根路径(全部页面都能访问) $.cookie('token', 'token_value', { expires: 10, path: '/' }); // 获取cookie $.cookie("token") // 删除cookie $.cookie("名称",null) $.cookie('the_cookie','the_value',{ expires:7, //expires:(Number|Date)有效期;设置一个整数时,单位是天;也可以设置一个日期对象作为Cookie的过期日期; path:'/', //path:(String)创建该Cookie的页面路径 domain:'jquery.com', //path:(String)创建该Cookie的页面路径 secure:true //(Booblean)如果设为true,那么此Cookie的传输会要求一个安全协议,例如:HTTPS; })  ``` + login ```js function login() { var userName = $("#username").val(); var password = $("#password").val(); var data = { "username": userName, "password": password } //console.log(data); $.ajax({ type: "POST", //方法类型 dataType: "json", //预期服务器返回的数据类型 url: "http://localhost:8082/user/login", contentType: "application/json; charset=utf-8", data: JSON.stringify(data), success: function(result) { console.log(result); if (result.status == 200) { setCookie("token", result.data.userToken); setCookie("username",username); window.location.href = "index.html"; }; if (result.status == 406 ) { alert("登陆失败!请检查账号和密码!"); return; } }, error: function() { alert("接口异常,请联系管理员!"); return; } }); } ``` + 对于某些页面必须登录才能访问,使用文档加载事件 ```html ``` + ajax请求是携带token,比如删除用户 ```js function userDel() { $.ajax({ type: "DELETE", url: "users/delete/1", contentType: "application/json", beforeSend: function(request) { //设置header值 request.setRequestHeader("token", getCookie("token")); }, success: function(r) { if (r.resultCode == 200) { console.log("删除成功") } else { console.log("删除失败") } } }); } ``` ### 接口注意事项 + 必须登录 `@TokenToUser User oldUser` + 先判断参数 + `getErrorResult` + `code, msg` + `1, msg` + `getSuccessResult` + 非String,传数据 (data) + 传String msg (msg) + 传data `(Object)` + 非实体类 + `JSONObject jsonObject = new JSONObject()`; `jsonObject.put("question",user.getQuestion())`; ```java @RestController public class AdminManagerController { @Resource private UserService userService; @GetMapping("/user/list") public Result getUsers(@RequestParam(required = false) Map params, @TokenToUser User oldUser){ if (StringUtils.isEmpty(params.get("page")) || StringUtils.isEmpty(params.get("limit"))) { return ResultGenerator.getErrorResult(Constants.RESULT_CODE_PARAM_ERROR, "参数异常!"); } if (oldUser==null){ return ResultGenerator.getErrorResult(Constants.RESULT_CODE_NOT_LOGIN,"用户未登录,请登录"); } if (!"A".equals(oldUser.getRole())){ return ResultGenerator.getErrorResult("没有权限"); } PageUtil pageUtil = new PageUtil(params); System.out.println(params); return ResultGenerator.getSuccessResult(userService.getUserPage(pageUtil)); } @PostMapping("/user/login") public Result login(@RequestBody JSONObject jsonParam){ String username = jsonParam.getString("username"); String password = jsonParam.getString("password"); } @PostMapping("/register") public Result register(@RequestBody User user){ } } ``` + sql语句 + update():SET 最后加 update_time=NOW() + 传参:#{username} + 命名问题:方法名不能重复 ### mybatis注解使用 ```java //单参数查询注解 @Select("select * from t_user where id = #{id}") public User getUserById(Integer id); //多参数查询注解 当 #{} 的变量与@Param()一致默认为变量名 @Select("select * from t_user where username like CONCAT('%',#{name},'%') and employeeNum like CONCAT('%',#{num},'%')") public List searchUsers(@Param("name") String name,@Param("num") String num); // 属性变量有类的注解 @Select("select * from t_job where id = #{jobId}") @Results({ @Result(property="javabean变量",column="数据库字段名",one=@One(select="全类名")) }) public Job getJobById(Integer jobId); //参数为Map的注解,返回值为类。 @Select("select m.user_name as userName from m_user m,m_job j where m.user_id=m.id and j.job_id=#{jobId}") public List getJobByMap(Map map); //参数为Map,返回值为List的注解,此时不需要bean类来对应字段 @Select("select * from m_user whereand j.job_id like CONCAT('%',#{jobId},'%')") public List getMyJobs(Map map); @Select("select t_id as id,t_username username,t_password as password from t_user") //@ResultType(User.class)// 自动识别bean类 返回值需要跟表字段名一致,或者sql 使用as @Results({@Result(property="username",column="t_username") ,@Result(property="password",column="t_password") }) //使用Results来对应属性值 public List selectAll(); ``` ### mysql问题 + 安装:需要以管理员的身份运行 + mysqld install 需要再mysql\bin下运行 + mysqld remove 是卸载 + 时间不对,相差8个小时 ```sql set global time_zone = '+8:00'; flush privileges; select now(); -- 查看当前时间是否为系统时间 ``` + java访问数据库设置时区 ``` jdbc:mysql://localhost:3306/db_es?useSSL=false&serverTimezone=Asia/Shanghai ``` + 修改密码 `alter user 'root'@'localhost' identified with mysql_native_password by '123456';` ### 支付宝沙盒模拟 + 导包 ```xml com.alipay.sdk alipay-sdk-java 3.0.0 ``` + 配置 ```properties # 支付宝网关名、partnerId和appId open_api_domain=https://openapi.alipaydev.com/gateway.do mcloud_api_domain=http://mcloudmonitor.com/gateway.do pid= app_id= # RSA私钥、公钥和支付宝公钥 merchant_private_key= public_key= #SHA1withRsa对应支付宝公钥 #alipay_public_key = #SHA256withRsa对应支付宝公钥 alipay_public_key= # 签名类型: RSA->SHA1withRsa,RSA2->SHA256withRsa sign_type=RSA2 #页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 return_url=http://192.168.1.20:8082/user/pay/return #服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 notify_url=http://192.168.1.20:8082/user/pay/notify charset=utf-8 format=json logPath=E:\\ # 当面付最大查询次数和查询间隔(毫秒) max_query_retry=5 query_duration=5000 # 当面付最大撤销次数和撤销间隔(毫秒) max_cancel_retry=3 cancel_duration=2000 # 交易保障线程第一次调度延迟和调度间隔(秒) heartbeat_delay=5 heartbeat_duration=900 ``` ```java package com.ysu.shop.config; import java.io.FileWriter; import java.io.IOException; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.PropertySource; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "") @PropertySource(value = "classpath:zfbinfo.properties", encoding = "utf-8") public class AlipayConfig { // 支付宝网关 private String openApiDomain; // 应用ID,您的APPID,收款账号既是您的APPID对应支付宝账号 private String appId; // 商户私钥,您的PKCS8格式RSA2私钥 private String merchantPrivateKey; // 支付宝公钥,查看地址:https://openhome.alipay.com/platform/keyManage.htm 对应APPID下的支付宝公钥。 private String alipayPublicKey; // 服务器异步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 private String notifyUrl; // 页面跳转同步通知页面路径 需http://格式的完整路径,不能加?id=123这类自定义参数,必须外网可以正常访问 private String returnUrl; // 签名方式 private String signType; // 字符编码格式 private String charset; private String logPath; private String format; public void logResult(String sWord) { FileWriter writer = null; try { writer = new FileWriter(logPath + "alipay_log_" + System.currentTimeMillis() + ".txt"); writer.write(sWord); } catch (Exception e) { e.printStackTrace(); } finally { if (writer != null) { try { writer.close(); } catch (IOException e) { e.printStackTrace(); } } } } public String getOpenApiDomain() { return openApiDomain; } public void setOpenApiDomain(String openApiDomain) { this.openApiDomain = openApiDomain; } public String getAppId() { return appId; } public void setAppId(String appId) { this.appId = appId; } public String getMerchantPrivateKey() { return merchantPrivateKey; } public void setMerchantPrivateKey(String merchantPrivateKey) { this.merchantPrivateKey = merchantPrivateKey; } public String getAlipayPublicKey() { return alipayPublicKey; } public void setAlipayPublicKey(String alipayPublicKey) { this.alipayPublicKey = alipayPublicKey; } public String getNotifyUrl() { return notifyUrl; } public void setNotifyUrl(String notifyUrl) { this.notifyUrl = notifyUrl; } public String getReturnUrl() { return returnUrl; } public void setReturnUrl(String returnUrl) { this.returnUrl = returnUrl; } public String getSignType() { return signType; } public void setSignType(String signType) { this.signType = signType; } public String getCharset() { return charset; } public void setCharset(String charset) { this.charset = charset; } public String getLogPath() { return logPath; } public void setLogPath(String logPath) { this.logPath = logPath; } public String getFormat() { return format; } public void setFormat(String format) { this.format = format; } } ``` + 测试 ```java package com.ysu.shop.controller; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.alipay.api.AlipayApiException; import com.alipay.api.AlipayClient; import com.alipay.api.DefaultAlipayClient; import com.alipay.api.internal.util.AlipaySignature; import com.alipay.api.request.AlipayTradePagePayRequest; import com.ysu.shop.annotation.TokenToUser; import com.ysu.shop.config.AlipayConfig; import com.ysu.shop.pojo.Orders; import com.ysu.shop.pojo.Pay; import com.ysu.shop.pojo.User; import com.ysu.shop.service.OrderService; import com.ysu.shop.service.PayService; import com.ysu.shop.util.CheckUtil; import com.ysu.shop.util.Result; import com.ysu.shop.util.ResultGenerator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; // import springfox.documentation.annotations.ApiIgnore; import io.swagger.annotations.*; import springfox.documentation.annotations.ApiIgnore; @RestController @RequestMapping("/user") @SuppressWarnings("rawtypes") @Api(value="/user/pay",tags="支付模块") public class PayController { private Logger log = LoggerFactory.getLogger(PayController.class); @Autowired private AlipayConfig alipayConfig; @Resource private OrderService orderService; @Resource private PayService payService; /** * 测试订单号15932466191022,15933410263042 localhost:8082/user/payByAli/15933410263042 * url localhost:8082/user/payByAli/15932591648892 * @param orderNo * @param request * @param response * @return * @throws Exception */ @RequestMapping("/payByAli/{orderNo}") public void payController(@PathVariable String orderNo, HttpServletRequest request, HttpServletResponse response) throws Exception { Orders order = orderService.getOrderByOrderNo(orderNo);//# AlipayClient alipayClient = new DefaultAlipayClient(alipayConfig.getOpenApiDomain(), alipayConfig.getAppId(), alipayConfig.getMerchantPrivateKey(), alipayConfig.getFormat(), alipayConfig.getCharset(), alipayConfig.getAlipayPublicKey(), alipayConfig.getSignType()); // 获得初始化的AlipayClient AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();// 创建API对应的request alipayRequest.setReturnUrl(alipayConfig.getReturnUrl()); alipayRequest.setNotifyUrl(alipayConfig.getNotifyUrl());// 在公共参数中设置回跳和通知地址 alipayRequest.setBizContent("{" + " \"out_trade_no\":\"" + orderNo + "\"," + " \"product_code\":\"FAST_INSTANT_TRADE_PAY\"," + " \"total_amount\":\"" + order.getPayment().add(order.getPostage()) + "\"," + " \"subject\":\"" + "欢迎使用睿乐购商城" + "\"," + " \"body\":\"" + "遇到问题,客服7*24为你解答问题" + "\"," + " \"passback_params\":\"merchantBizType%3d3C%26merchantBizNo%3d2016010101111\"," + " \"extend_params\":{" + " \"sys_service_provider_id\":\"2088511833207846\"" + " }" + " }");// 填充业务参数 String form = ""; try { form = alipayClient.pageExecute(alipayRequest).getBody(); // 调用SDK生成表单 } catch (AlipayApiException e) { Pay pay = new Pay(order.getUser_id(),orderNo,"1"); pay.setPlatform_status("1"); payService.addPay(pay); log.debug("支付失败"); } response.setContentType("text/html;charset=" + alipayConfig.getCharset()); response.getWriter().write(form);// 直接将完整的表单html输出到页面 response.getWriter().flush(); response.getWriter().close(); } @RequestMapping("/pay/return") public void paySuccess(String out_trade_no,HttpServletResponse response,HttpServletRequest request) throws Exception{ Map map = new HashMap(); Map requestParams = request.getParameterMap(); for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) { String name = iter.next(); String[] values = requestParams.get(name); String valueStr = ""; for (int i = 0; i < values.length; i++) { valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; } map.put(name, valueStr); } Orders order = orderService.getOrderByOrderNo(out_trade_no); Pay pay = new Pay(order.getUser_id(),out_trade_no,"1"); boolean signVerified = false; try { signVerified = AlipaySignature.rsaCheckV1(map, alipayConfig.getAlipayPublicKey(), alipayConfig.getCharset(), alipayConfig.getSignType()); } catch (com.alipay.api.AlipayApiException e) { log.info("支付回调异常",e); // 验签发生异常,则直接返回失败 pay.setPlatform_status("1"); payService.addPay(pay); response.sendRedirect("/rlg/payFail.html"); } if (signVerified) { orderService.payByOrderNo(out_trade_no); payService.addPay(pay); response.sendRedirect("http://localhost:8082/rlg/paySuccess.html"); } else { log.info("验证失败"); pay.setPlatform_status("1"); payService.addPay(pay); response.sendRedirect("http://localhost:8082/rlg/payFail.html"); } } @RequestMapping("/pay/notify") public void payNotify(String out_trade_no,HttpServletResponse response,HttpServletRequest request )throws Exception { Map map = new HashMap(); Map requestParams = request.getParameterMap(); for (Iterator iter = requestParams.keySet().iterator(); iter.hasNext(); ) { String name = iter.next(); String[] values = requestParams.get(name); String valueStr = ""; for (int i = 0; i < values.length; i++) { valueStr = (i == values.length - 1) ? valueStr + values[i] : valueStr + values[i] + ","; } map.put(name, valueStr); } Orders order = orderService.getOrderByOrderNo(out_trade_no); Pay pay = new Pay(order.getUser_id(),out_trade_no,"1"); boolean signVerified = false; try { signVerified = AlipaySignature.rsaCheckV1(map, alipayConfig.getAlipayPublicKey(), alipayConfig.getCharset(), alipayConfig.getSignType()); } catch (com.alipay.api.AlipayApiException e) { log.info("支付回调异常",e); // 验签发生异常,则直接返回失败 pay.setPlatform_status("1"); payService.addPay(pay); response.sendRedirect("/rlg/payFail.html"); } if (signVerified) { orderService.payByOrderNo(out_trade_no); payService.addPay(pay); response.sendRedirect("/rlg/paySuccess.html"); } else { log.info("验证失败"); pay.setPlatform_status("1"); payService.addPay(pay); response.sendRedirect("/rlg/payFail.html"); } } } ``` + 回调参数实例 ```json { "charset":"utf-8", "out_trade_no":"15936564583112", "method":"alipay.trade.page.pay.return", "total_amount":"112.00", "sign":"jIG9ctLKjlGW2z7v2%2FF1fhXFGwtFsidrqZzNENykIu804IvQFKzqw%2FG7TfKQE3NHNlsYaJ4FuQEUM45mK8J2sTIJy4gXDnLCsFEXwsycSP8GSWf3Jp4dCLEPteI%2F20wETa8Mqx0ySYliEcywOxc33AJTHMMKf4x5bjS3ccvqx%2BrJGLeHE8NrX%2FZEBp%2Ba1tQ7O2CRVv9ADiAMfoyyAQ47laiP5HnGKVa%2FHuBwTd%2FnLxE8dOH%2BtZp1w1MDKbm0D8Cn784gctmxGZAWUmiE3Y7U%2Bn1VNCQlSF7Oo6cd63ftJ2QJBedOWpH3RrXtGPnJlIFruAnFDUt%2F3lWv9N2qtZd9hQ%3D%3D", "trade_no":"2020070222001435400500635866", "auth_app_id":"2016102700771189", "version":"1.0", "app_id":"2016102700771189", "sign_type":"RSA2", "seller_id":"2088102181269576", "timestamp":"2020-07-02+10%3A20%3A38" } ``` ### 集成Swagger2文档 + [访问地址](http://localhost:8082/swagger-ui.html) ### 集成admin + [SpringBoot - 使用Spring Boot Admin进行服务监控教程1(监控信息可视化)](https://www.hangge.com/blog/cache/detail_2709.html) ### 参考文档 + [JS之document.cookie详解以及$.cookie的使用](https://www.cnblogs.com/zouhong/p/11821644.html) + [MySQL<表单&集合查询>](https://www.cnblogs.com/justdoitba/p/7620187.html) + [4种方法让SpringMVC接收多个对象](https://blog.csdn.net/bibili123/article/details/81123140) + [常用的swagger2 注解说明文档](https://blog.csdn.net/weixin_43703274/article/details/90900597) + [MySQL 安装](https://www.runoob.com/mysql/mysql-install.html) + [Swagger2 最全注解说明](https://www.cnblogs.com/niudaben/p/11869869.html) + [Swagger注解-@ApiModel 和 @ApiModelProperty](https://blog.csdn.net/dejunyang/article/details/89527348) + [Spring Boot使用支付宝支付](https://blog.csdn.net/qq_42005257/article/details/90638692) + [用springboot对接支付宝支付接口的详细开发步骤总结](https://blog.csdn.net/Eknaij/article/details/96324432) + [Java支付宝支付开发流程与原理【沙箱环境】【分布式事务解决方案】](https://blog.csdn.net/itcats_cn/article/details/82723889) + [session、cookie与“记住我的登录状态”的功能的实现](https://www.cnblogs.com/xlh-2014/p/8110685.html)