课程名称: | 企业级开发框架专题 | 学期:2020秋季 |
---|---|---|
实验名称: | 期末大作业:基于微信小程序的新零售移动电商系统设计与实验 | 实验序号:期末大作业 |
组长姓名:xxx | 组长学号:xxx | 班级:xxx |
实验地址:xxx | 实验日期:2020-12-10 | 指导老师:xxx |
教师评语:XXX | 实验成绩:XXX | 同组同学:xxx |
基于小程序和Spring Boot的奶茶点单系统
受新冠疫情影响,消费者闭门不出,线下门店纷纷关闭,实体经济收到重大冲击。除了疫情的挑战,传统
零售与传统电商,一直被效率、场景、管控等问题困扰。
新零售赋能传统零售转型升级。线上商城与线下门店的交易无缝缝合,通过技术改变消费者的习惯,集合
结合高效的物流配送,极大提高消费者的购物体验。基于微信的生态圈用户流量,让平台更快的传播获客,吸引
海量用户资源。
本项目需要分为两个客户端:
微信小程序
React,Ant组件库
SpringBoot,Mysql
客户端采用小程序为客户提供图形界面,方便客户查看商品,下订单,查看订单状态
客户可以使用微信一键登录进入小程序,小程序会保存用户的登录信息
后端主要提供的功能:
在首页中随机显示8款奶茶
@GetMapping("recommend")
@ApiOperation(value = " 首页“为你推荐”")
public List<Milktea> recommend() {
随机选择8款奶茶向用户推荐
Integer nums = 8;
Integer start = 1;
Integer end = countMilktea();
//1.创建集合容器对象
List list = new ArrayList();
List ret = new ArrayList();
//2.创建Random对象
Random r = new Random();
//循环将得到的随机数进行判断,如果随机数不存在于集合中,则将随机数放入集合中,如果存在,则将随机数丢弃不做操作,进行下一次循环,直到集合长度等于nums
while (list.size() != nums) {
Integer num = r.nextInt(end - start) + start;
if (!list.contains(num)) {
list.add(num);
}
}
for (Object l : list) {
NumberFormat formatter = NumberFormat.getNumberInstance();
String s = formatter.format(Integer.parseInt(l.toString()));
// System.out.println(s);
ret.add(selectOneMilktea(s));
}
return ret;
}
mapper语句:
<select id="selectMilkteaById" resultType="com.demo01.demo.entity.Milktea">
select *
from milktea
where id = #{id}
</select>
用于小程序点单页面按照种类显示奶茶。先获取奶茶的种类数,然后根据种类id将全部奶茶按照种类装到各个数组中,再将这些数组装到一个大数组中
@GetMapping("selectAllByType")
@ApiOperation(value = "返回全部的奶茶 会按奶茶种类排序")
public List<List<Milktea>> selectAllByType(String type) {
List<List<Milktea>> res= new ArrayList<List<Milktea>>();
int countType = countType();
for (int i = 0; i < countType; i++) {
List<Milktea> temp = selectByType(Integer.toString(i+1) );
res.add(temp);
}
return res;
}
mapper语句:
<select id="selectByType" resultType="com.demo01.demo.entity.Milktea">
select *
from milktea
where type = #{type}
</select>
用于用户使用微信进行登录的接口。先判断从小程序获得的openid是否为空,为空则登录失败返回空值,否则查询数据库该openid是否已存入数据库,未存入则将该openid与用户昵称插入数据库并且后端返回存有openid该用户对象,否则只更新用户昵称并返回存有对应用户信息的用户对象。
@PostMapping("login")
@ApiOperation(value = "登录")
public User login(@RequestBody LoginInfoDTO loginInfoDTO) {
String openid = loginInfoDTO.getOpenid();
String nickname = loginInfoDTO.getNickname();
User res = userService.login(openid);
if (res.getOpenid() != null) {
res.setNickname(nickname);
userService.setNickName(res);
}
return res;
}
//userService.login(String openid)方法
@Override
public User login(String openid) {
if (openid.equals("")) {
System.out.println("ID是空的,无法登陆");
return new User();
}
User successUser = userMapper.login(openid);
if (null == successUser) {
this.logon(openid);
User user = new User();
user.setOpenid(openid);
return user;
}
return successUser;
}
主要mapper语句:
<select id="login" resultType="User">
select * from cusaccinfo where openid=#{openid}
</select>
<insert id="logon" parameterType="String">
insert into cusaccinfo(openid) values(#{openid})
</insert>
用于用户下单的接口。后端先将订单时间、总价格、用户openid和用户地址信息插入数据库中的orderinfo表(订单信息表),获取自增的orderid后将从小程序获取的奶茶信息拆分开来,按照奶茶的id组成为单条条目,依照orderid插入数据库中的comselectinfo表(订单详情表)中
@PostMapping("addOneOrderByStr")
@ApiOperation(value = "增加一个订单条目")
public boolean addOneOrderByStr(String openid,String drinkStr,String address,String phoneNum,String name)
{
//转对象集合
JSONArray json = JSONArray.fromObject(drinkStr);
List<OrderDrink> drinkList = (List<OrderDrink>) JSONArray.toCollection(json, OrderDrink.class);
//加入时间
Timestamp time = new Timestamp(System.currentTimeMillis());
double totalPrice = 0;
//统计价格
for (OrderDrink d : drinkList) {
totalPrice = totalPrice + d.getDrinkPrice();
}
//先插入orderinfo表
Order order = new Order();
order.setOpenid(openid);
order.setTime(time);
order.setTotal(totalPrice);
order.setStatus(0);
order.setAddress(address);
order.setPhonenum(phoneNum);
order.setName(name);
if (orderMapper.addOneOrderInfo(order) == 0)
return false;
//后插入selectinfo表
SelectInfo selectInfo;
//获取orderinof表最大的orderid
int orderId = orderMapper.findLastOrderId();
//拆分drinklist,组成为单条条目,插入数据库
for (OrderDrink d : drinkList) {
selectInfo = new SelectInfo();
selectInfo.setId(d.getDrinkId());
selectInfo.setDescription(d.getDrinkInfo());
selectInfo.setNumber(d.getDrinkNum());
selectInfo.setPrice(d.getDrinkPrice());
selectInfo.setOrderId(orderId);
totalPrice = totalPrice + d.getDrinkPrice();
if (orderMapper.addOneSelectInfo(selectInfo) == 0)
return false;
}
return true;
}
mapper语句:
<insert id="addOneSelectInfo" parameterType="com.demo01.demo.entity.SelectInfo">
insert into comselectinfo value(#{orderId},#{id},#{number},#{description},#{price})
</insert>
<insert id="addOneOrderInfo" parameterType="com.demo01.demo.entity.Order">
insert into OrderInfo value(null,#{openid},#{time},#{total},#{status},#{address},#{phonenum},#{name})
</insert>
@GetMapping("findMakingMiniOrder")
@ApiOperation(value = "查询用户制作中订单缩略信息")
public List<MiniOrder> findMakingMiniOrder(String openid) {
List<MiniOrderEntry> entryList = orderMapper.findMakingMiniOrder(openid);
List<MiniOrder> orderList = new ArrayList<>();
List<String> drinkIdList = new ArrayList<>();
List<String> imageList = new ArrayList<>();
MiniOrder orderTmp = new MiniOrder();
int drinkId;
int orderId = -1;
double total = 0;
//查询结果为空
if (entryList.size() == 0)
return null;
//根据订单编号合并订单条目,组成一个完整订单
for (MiniOrderEntry e : entryList) {
//当前条目属于一个新订单
if (orderId != e.getOrderId()) {
//不是第一个订单才需要添加到orderList
if (orderId != -1) {
orderTmp.setTotal(total);
orderTmp.setDrinkIdList(drinkIdList);
orderTmp.setImageList(imageList);
orderList.add(orderTmp);
}
//清空上个订单用到的变量
orderTmp = new MiniOrder();
drinkIdList = new ArrayList<>();
imageList = new ArrayList<>();
total = 0;
//不是第一个订单才需要清空drinkList,imageList
if (orderId != -1) {
drinkIdList.clear();
imageList.clear();
}
//
orderId = e.getOrderId();
orderTmp.setOrderId(e.getOrderId());
orderTmp.setOrderTime(e.getTime());
orderTmp.setOpenid(e.getOpenid());
total += e.getPrice();
//加入一种饮品
drinkIdList.add(e.getId());
imageList.add(e.getImage());
}
//当前条目与上条条目属于同一个订单,仅添加饮品
else {
total += e.getPrice();
drinkIdList.add(e.getId());
imageList.add(e.getImage());
}
}
orderTmp.setTotal(total);
orderTmp.setDrinkIdList(drinkIdList);
orderTmp.setImageList(imageList);
orderList.add(orderTmp);
return orderList;
}
mapper语句:
<select id="findMakingMiniOrder" resultType="com.demo01.demo.entity.MiniOrderEntry">
select a.orderId,a.openid,a.total,a.time,b.id,b.number,b.price,c.image
from OrderInfo a, comselectinfo b, milktea c
where openid=#{openid} and a.orderId = b.orderId and b.id = c.id and Status = 0;
</select>
@GetMapping("findCompletedMiniOrder")
@ApiOperation(value = "查询用户已完成订单缩略信息")
public List<MiniOrder> findCompletedMiniOrder(String openid) {
List<MiniOrderEntry> entryList = orderMapper.findCompletedMiniOrder(openid);
List<MiniOrder> orderList = new ArrayList<>();
List<String> drinkIdList = new ArrayList<>();
List<String> imageList = new ArrayList<>();
MiniOrder orderTmp = new MiniOrder();
int drinkId;
int orderId = -1;
double total = 0;
//查询结果为空
if (entryList.size() == 0)
return null;
//根据订单编号合并订单条目,组成一个完整订单
for (MiniOrderEntry e : entryList) {
//当前条目属于一个新订单
if (orderId != e.getOrderId()) {
//不是第一个订单才需要添加到orderList
if (orderId != -1) {
orderTmp.setTotal(total);
orderTmp.setDrinkIdList(drinkIdList);
orderTmp.setImageList(imageList);
orderList.add(orderTmp);
}
//清空上个订单用到的变量
orderTmp = new MiniOrder();
drinkIdList = new ArrayList<>();
imageList = new ArrayList<>();
total = 0;
//不是第一个订单才需要清空drinkList,imageList
if (orderId != -1) {
drinkIdList.clear();
imageList.clear();
}
//
orderId = e.getOrderId();
orderTmp.setOrderId(e.getOrderId());
orderTmp.setOrderTime(e.getTime());
orderTmp.setOpenid(e.getOpenid());
total += e.getPrice();
//加入一种饮品
drinkIdList.add(e.getId());
imageList.add(e.getImage());
}
//当前条目与上条条目属于同一个订单,仅添加饮品
else {
total += e.getPrice();
drinkIdList.add(e.getId());
imageList.add(e.getImage());
}
}
orderTmp.setTotal(total);
orderTmp.setDrinkIdList(drinkIdList);
orderTmp.setImageList(imageList);
orderList.add(orderTmp);
return orderList;
}
mapper语句:
<select id="findCompletedMiniOrder" resultType="com.demo01.demo.entity.MiniOrderEntry">
select a.orderId,a.openid,a.total,a.time,b.id,b.number,b.price,c.image
from OrderInfo a, comselectinfo b, milktea c
where openid=#{openid} and a.orderId = b.orderId and b.id = c.id and Status = 1;
</select>
前端主页面主要有几个模块组成:左上角是LOGO,右上角是账户信息,左侧栏是菜单,占据页面大部分内容的是内容展示框
前端的菜单有
首页向用户展示一些重要统计数据的图表信息,让经营者能够清楚知道自己的营收情况,订单数目,下单人数,品类销量排行
给经营者管理订单,或是给制作者查看订单信息,根据信息制作相应奶茶
制作人员可以点击制作完成,这样用户就可以得到取餐号在前台取餐
可以在已完成订单中找到
用户则可以在自己的小程序端看到:
经营者可以在这个页面添加或者删除商品
添加商品操作:
其中商品图标是动态获取的
通过COSBrowser,用户可以放自己的图片进去,然后就可以在添加奶茶的时候使用
banner指的是小程序首页的展示图片,可以从此页面进行更换
后端主要提供的功能:
管理员使用账号密码登录,后端判断是否可以登录,如果不能登录则返回错误信息,成功则可以得到相关权限并跳转到后台主界面
控制器:
//AdminLoginController
@RestController
@Api(tags = "管理员登录控制器")
public class AdminLoginController {
@Autowired
private AdminService adminService;
@PostMapping("/admin/login")
@ApiOperation(value = "管理员登录")
public Result<?> login(@RequestParam String username,
@RequestParam String password_md5,
HttpSession session) {
Admin admin = adminService.checkAdmin(username, password_md5);
if (admin != null) {
// admin.setPassword_md5("");
session.setAttribute("admin",admin);
return ResultUtils.success(admin);
}else
{
return ResultUtils.error(-14,"用户名或密码错误");
}
}
}
实体类:
public class Admin extends BaseRowModel {
@ExcelProperty(value = "id",index = 0)
private int id;
@ExcelProperty(value = "账号",index = 1)
private String username;
@ExcelProperty(value = "密码",index = 2)
private String password_md5;
public Admin() {
}
public Admin(int id, String username, String password_md5) {
this.id = id;
this.username = username;
this.password_md5 = password_md5;
}
public Admin(int id, String username) {
this.id = id;
this.username = username;
}
public Admin(String username, String password_md5) {
this.username = username;
this.password_md5 = password_md5;
}
public Admin(String username) {
this.username = username;
//……省略getter and setter
}}
相关SQL语句
select *
from admin
where username = #{username}
and password = #{password_md5}
select *
from milktea
实体类:
public class Milktea extends BaseRowModel {
@ExcelProperty(value = "编号",index = 0)
String id;
@ExcelProperty(value = "品名",index = 1)
String name;
@ExcelProperty(value = "单价",index = 2)
String price;
@ExcelProperty(value = "类型编号",index = 3)
String type;
@ExcelProperty(value = "类型",index = 4)
String typeName;
@ExcelProperty(value = "展示图片",index = 5)
String image;
//……省略Getter and Setter
}
Controller:
@RestController
@Api(tags = "管理员控制器")
public class AdminController {
//…………
@GetMapping("/admin/order/getTodayInfo")
@ApiOperation(value = "统计今日订单数")
public Integer getTodayOrderNum() {
return orderService.getTodayOrderNum();
}
//…………
}
奶茶列表导出Excel:
数据库中的所有奶茶可以导出一份Excel方便管理员管理查看
实体类依旧是同上,不同的是接口实现:
Controller:
@Controller
@Api(tags="Excle导出")
public class getExcleController {
//……
MilkteaService milkteaService;
//……
@GetMapping("/getMilkTeaExcle")
@ApiOperation(value = "下载全部奶茶信息")
public void getAllMilkTea(HttpServletResponse response){
List<Milktea> milkteas= milkteaService.selectAllMilktea();
SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMdd");
String time=sdf.format(new Date());
time=time.replaceAll("-","").replaceAll(":","").replaceAll(" ","");
String fileName="全部奶茶信息";
ExcelUtil.writeExcel(response,milkteas,fileName+time,fileName,new Milktea());
}
//……
}
商品(奶茶)的增加操作
演示且看后台前端部分的内容
SQL语句:
INSERT INTO milktea (id, type, price, TypeName, image, name) VALUES (#{id},#{type},#{price},#{typeName},#{image},#{name})
对应实体类与2.一致
Controller:
@RestController
@Api(tags = "管理员控制器")
public class AdminController {
//…………
@PostMapping("/admin/milktea")
@ApiOperation(value = "新增奶茶信息")
public Result<?> saveMilkteaInfo(@RequestBody Milktea milktea) {
return ResultUtils.success(milkteaService.saveMilktea(milktea));
}
//…………
}
MilkteaMapper.xml:
……
update milktea
<trim prefix="set" suffixOverrides=",">
<if test="type != null">type = #{type},</if>
<if test="price != null">price = #{price},</if>
<if test="typeName != null">TypeName = #{typeName},</if>
<if test="image != null">image = #{image},</if>
<if test="name != null">name = #{name},</if>
</trim>
<where>id = #{id}</where>
……
Controller:
@RestController
@Api(tags = "管理员控制器")
public class AdminController {
//…………
@PutMapping("/admin/milktea")
@ApiOperation(value = "根据ID修改奶茶")
public Result<?> updateMilkteaInfo(@RequestBody Milktea milktea) {
return ResultUtils.success(milkteaService.updateMilktea(milktea));
}
//…………
}
对应实体类与2.一致
Controller:
@RestController
@Api(tags = "管理员控制器")
public class AdminController {
//…………
@GetMapping("/admin/milktea/{milkteaId}")
@ApiOperation(value = "根据ID查询奶茶")
public Result<?> getMilkteaById(@PathVariable("milkteaId") String milkteaId) {
return ResultUtils.success(milkteaService.selectOneMilktea(milkteaId));
}
//…………
}
略
public class OrderInfoChart extends BaseRowModel {
@ExcelProperty(value = "时间",index = 0)
String time;
@ExcelProperty(value = "订单数",index = 1)
int orderNum;
//省略Getter and Setter
}
Controller:
@RestController
@Api(tags = "管理员控制器")
public class AdminController {
//…………
@GetMapping("/admin/orders/getOrderInfoAnyTime")
@ApiOperation(value = "统计历史每日订单数")
public Result<?> getOrderInfoAnyTime(int days) {
return ResultUtils.success(orderService.getOrderInfo_anyTime(days));
}
//…………
}
SQL语句:
```mysql
SELECT DATE_FORMAT( Time, '%Y-%m-%d' ) time, count(*) orderNum
FROM orderinfo
where DATE_SUB(CURDATE(), INTERVAL #{days} DAY) <= date(Time)
group by DATE_FORMAT( Time, '%Y-%m-%d' )
@Controller
@Api(tags="Excle导出")
public class getExcleController {
//……
@Resource
OrderService orderService;
//……
@GetMapping("/getOrderInfoChart")
@ApiOperation(value = "下载订单数目统计")
public void getAllMilkTea(HttpServletResponse response,int days){
List<OrderInfoChart> OrderInfo= orderService.getOrderInfo_anyTime(days);
System.out.println(OrderInfo.get(0).getTime());
SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMdd");
String time=sdf.format(new Date());
time=time.replaceAll("-","").replaceAll(":","").replaceAll(" ","");
String fileName="订单数目统计";
ExcelUtil.writeExcel(response,OrderInfo,fileName+time,fileName,new OrderInfoChart());
}
//……
}
提供给制作人员的接口:
制作人员可以在后台查看未制作订单以及订单描述信息,根据后台给出的提示进行制作商品
本学期的课程以及本次实验让我们实践了本学期学习的SpringBoot和使用gitee协同开发,并在本次大实验中学习了小程序开发和前端的开发框架的使用。
本次实验困难重重,好在给足了时间让我们去学习,让我们在本次实验中收获颇丰。