目录
- [1. 运营管理App](#1. 运营管理App)
-
- [1.1. 代码运行](#1.1. 代码运行)
- [1.2. 代码简介](#1.2. 代码简介)
- [1.3. App测试](#1.3. App测试)
- [2. 设备屏幕端](#2. 设备屏幕端)
-
- [2.1. 代码运行](#2.1. 代码运行)
- [2.2. 支付实现](#2.2. 支付实现)
1. 运营管理App
1.1. 代码运行
-
业务场景
管理员在后台创建工单后,工作人员可在运营管理App中查看并根据情况选择接受或取消分配给自己的任务

-
安装配置
-
客户端
本项目的App客户端部分已经由前端团队进行开发完成,并且以apk的方式提供出来,供我们测试使用
-
安装模拟器
-
安装APK
-
配置后端地址

-
-
后端搭建
本项目运营管理App的java后端已开发完成,在资料中已提供源码,导入idea中即可
-
1.2. 代码简介
-
代码组成
运营管理App的java后端技术栈:SpringBoot+MybatisPlus+阿里云短信
+ 员工管理(EmpController):发送短信、App登录、查询员工信息 + 工单管理(TaskController):查询工单、接受工单、拒绝/取消工单、完成工单 + 工单详情(TaskDetailsController):根据工单id查询补货详情列表  -
阿里云短信
这里提一下我们以前可能没有接触到的短信认证服务,看一下实现代码。
-
工具类(SmsTemplate)
java//短信发送工具类 @Data @Component @ConfigurationProperties(prefix = "dkd.sms") public class SmsTemplate { private String key; private String secret; private String signName; private String templateCode; // 调用阿里云平台发送短信 public void sendSms(String phoneNumbers, String code) { //设置超时时间 System.setProperty("sun.net.client.defaultConnectTimeout", "10000"); System.setProperty("sun.net.client.defaultReadTimeout", "10000"); try { //初始化acsClient,暂不支持region化 IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", key, secret); DefaultProfile.addEndpoint("cn-hangzhou", "cn-hangzhou", "Dysmsapi", "dysmsapi.aliyuncs.com"); IAcsClient acsClient = new DefaultAcsClient(profile); //组装请求对象-具体描述见控制台-文档部分内容 SendSmsRequest request = new SendSmsRequest(); request.setPhoneNumbers(phoneNumbers);//手机号 request.setSignName(signName);//短信前面 request.setTemplateCode(templateCode);//短信模板 request.setTemplateParam("{\"code\":\"" + code + "\"}");//验证码 SendSmsResponse sendSmsResponse = acsClient.getAcsResponse(request); } catch (Exception e) { e.printStackTrace(); } } } -
yml相关配置
yamlsms: key: secret: sign-name: 阿里云短信测试 template-code: SMS_154950909 -
封装返回结果
java// 封装返回结果 private EmpVo convertToVM(Emp emp) { EmpVo empVo = new EmpVo(); empVo.setMobile(emp.getMobile()); empVo.setRoleId(emp.getRoleId()); empVo.setRoleCode(emp.getRoleCode()); empVo.setLoginName(emp.getUserName()); empVo.setUserId(emp.getId()); empVo.setRoleName(roleService.getById(emp.getRoleId()).getRoleName()); empVo.setUserName(emp.getUserName()); empVo.setStatus(emp.getStatus()); empVo.setRegionId(emp.getRegionId()); empVo.setRegionName(emp.getRegionName()); empVo.setImage(emp.getImage()); return empVo; } // 封装返回结果 private EmpVo convertToVM(Emp emp) { EmpVo empVo = new EmpVo(); empVo.setMobile(emp.getMobile()); empVo.setRoleId(emp.getRoleId()); empVo.setRoleCode(emp.getRoleCode()); empVo.setLoginName(emp.getUserName()); empVo.setUserId(emp.getId()); empVo.setRoleName(roleService.getById(emp.getRoleId()).getRoleName()); empVo.setUserName(emp.getUserName()); empVo.setStatus(emp.getStatus()); empVo.setRegionId(emp.getRegionId()); empVo.setRegionName(emp.getRegionName()); empVo.setImage(emp.getImage()); return empVo; } -
Controller
java// 发送短信验证码 @GetMapping("/code/{mobile}") public void sendSms(@PathVariable("mobile") String mobile) { empService.sendSms(mobile); }java// 登录 @PostMapping("/login") public LoginVo login(@RequestBody LoginDto req) throws IOException { return empService.login(req); } -
Service实现类
java@Override public void sendSms(String mobile) { // String code = RandomUtil.randomNumbers(5); // 生成5位随机验证码 String code = "12345"; // smsTemplate.sendSms(mobile,code); // 调用阿里云发送短信 // 将验证码存入redis dkd.sms:手机号 验证码 5分钟有效 redisTemplate.opsForValue().set("dkd.sms:" + mobile, code, Duration.ofSeconds(300)); }java@Override public LoginVo login(LoginDto req) throws IOException { //1. 比对验证码 // 从redis中查询 String redisCode = redisTemplate.opsForValue().get("dkd.sms:" + req.getMobile()); if (!StrUtil.equals(req.getCode(), redisCode)) { // 注意此处有感叹号 throw new LogicException("验证码错误"); } // 2.比对手机号 LambdaQueryWrapper<Emp> qw = new LambdaQueryWrapper<>(); qw.eq(Emp::getMobile, req.getMobile()); Emp emp = this.getOne(qw); if (ObjectUtil.isEmpty(emp)) { throw new LogicException("手机号非法"); } //3. 登录成功,制作token返回结果 LoginVo resp = new LoginVo(); resp.setSuccess(true); resp.setRoleCode(emp.getRoleCode()); resp.setUserName(emp.getUserName()); resp.setUserId(emp.getId()); resp.setRegionId(emp.getRegionId().toString()); resp.setMsg("登录成功"); TokenObject tokenObject = new TokenObject(); tokenObject.setUserId(emp.getId()); tokenObject.setMobile(emp.getMobile()); tokenObject.setLoginType(req.getLoginType()); tokenObject.setUserName(emp.getUserName()); String token = JWTUtil.createJWTByObj(tokenObject); resp.setToken(token); // 是否为维修员 if (emp.getRoleCode().equals("1003")) { resp.setRepair(true); } // 删除redis中验证码 redisTemplate.delete("dkd.sms:" + req.getMobile()); return resp; }
-
1.3. App测试
-
后端测试
后端代码更改相关配置并启动(记得将java的jdk版本切换为jdk17,并修改yaml配置文件的mysql和redis的连接信息)。这里有一点我们需要改,因为我的管理端的表都没有
tb_前缀,我们这里就需要修改一下官方的代码,将app项目的后端代码的@TableName注解的value的所有tb_前缀去掉。java@Data @TableName(value = "task_details") -
前端测试
这里使用Mumu模拟器来进行App模拟,这里建议自己去官网下载最新版的模拟器,因为老版本可能会不兼容Hyper-V
修改后端Url(默认为:
https://likede2-java.itheima.net/api),这里需要改为http://10.0.2.2:9007-
关于IP和DHCP
- 静态IP:就是手动设置的固定IP地址。你自己或者系统管理员直接指定一组IP,不会变。
- DHCP(动态主机配置协议):由路由器或网络中的DHCP服务器自动分配IP,设备每次连接可能拿到不同的IP。
-
关于连接地址
你直接使用
http://10.0.2.2:9007一般来说是连接不成功的,因为这是10.0.2.2是模拟器内置的转发地址,专门用来访问 宿主机(运行模拟器的电脑) 的本地服务,而你的电脑并不会是这个地址,我这里使用网络桥接(注意IP要不一样)你可以运行命令
ipfonfig,找到无线局域网适配器 WLAN:bash无线局域网适配器 WLAN: 连接特定的 DNS 后缀 . . . . . . . : 本地链接 IPv6 地址. . . . . . . . : ****::****:****:****:**** IPv4 地址 . . . . . . . . . . . . : 192.168.124.80 子网掩码 . . . . . . . . . . . . : 255.255.255.0 默认网关. . . . . . . . . . . . . : 192.168.124.1配置MuMu模拟器配置,开启网络桥接模式(我这里已经下载好了驱动),将上面的配置一一对应。这里也不一定要和我一样,DHCP也可以,不过重启关机后ip可能会改变(但是局域网换了,ip也会改变,比如说换了WIFI),然后重新设置应用的url即可。

-
最佳实践
- 所以我最建议的还是:局域网能跑通后,
将项目放在服务器中,使用公网ip访问 - 如果服务器部署,那么不要配置域名和SSL证书,直接http访问,因为没有前端源码,就不能修改,前端是http,如果后端使用https,就会造成内容混用问题。
- 所以我最建议的还是:局域网能跑通后,
-
登录成功
运维工单如图(运营工单也差不多)

-
2. 设备屏幕端
2.1. 代码运行
-
业务场景
消费者可以在设备屏幕端查看商品列表--选择支付方式--显示支付二维码--用户扫码完成支付--商品出货

-
后端搭建
修改mysql、redis配置,以及
tb前缀的表名
-
前端搭建
先修改index.html文件中的后端url,然后直接在浏览器中打开index.html文件,再输入设备编号(innerCode)
jslet domain = "http://localhost:8005";
html项目也可以单独运行在服务器哈,只需要将将项目目录指向index.html的父文件夹

大家可以看看我发布的屏幕端:帝可得屏幕端
2.2. 支付实现

-
支付框架介绍
仓库地址: https://gitee.com/myelegent/elegent-pay
ElegentPay是封装了支付宝和微信支付的支付框架,用户使用该框架,可以用最小的学习成本,在几分钟内快速集成并在项目中使用。
- 为支付宝和微信提供了统一的调用入口。
- 支持native、小程序、H5、APP等多种支付方式,并提供统一入口。
- 提供了统一的dto类作为前端的调用参数,用户使用简便。
- 封装了回调入口和验签逻辑,简化了用户编写支付回调中繁琐的验签逻辑。
- 提供了扩展机制,用户可以自定义其它的支付方式。
- 对支付回调和退款回调提供了幂等性校验。
- 提供了回调补偿功能。
- ElegentPay是封装了支付宝和微信支付的支付框架,用户使用该框架,可以用最小的学习成本,在几分钟内快速集成并在项目中使用。
-
支付框架使用
-
引入依赖
xml<!--微信支付--> <dependency> <groupId>cn.elegent.pay</groupId> <artifactId>elegent-pay-wxpay</artifactId> <version>1.0.0</version> </dependency> <!--支付宝支付--> <dependency> <groupId>cn.elegent.pay</groupId> <artifactId>elegent-pay-alipay</artifactId> <version>1.0.0</version> </dependency> -
添加配置
yamlelegent: pay: wxpay: mchId: 1561414331 appId: wx6592a2db3f85ed25 appSecret: d9a9ff00a633cd7353a8925119063b01 mchSerialNo: 25FBDE3EFD31B03A4377EB9A4A47C517969E6620 apiV3Key: CZBK51236435wxpay435434323FFDuv3 alipay: appId: 2021003141676135 callback: domain: https://2d3ac179.r5.cpolar.top watch: true cycle: 10 -
业务代码编写
去仓库Readme看吧,这里就不过多叙述了。
-
-
支付流程
-
生成二维码流程

-
支付流程

-