Apache ECharts
Apache ECharts介绍:
Apache ECharts 是一款基于 Javascript 的数据可视化图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。
官网地址:https://echarts.apache.org/zh/index.html
Apache ECharts入门程序:
进入官网,按照官方给的步骤:


<!DOCTYPE html>
<html><head><meta charset="utf-8" /><title>ECharts</title><!-- 引入刚刚下载的 ECharts 文件 --><script src="echarts.js"></script></head><body><!-- 为 ECharts 准备一个定义了宽高的 DOM --><div id="main" style="width: 600px;height:400px;"></div><script type="text/javascript">// 基于准备好的dom,初始化echarts实例var myChart = echarts.init(document.getElementById('main'));// 指定图表的配置项和数据var option = {title: {text: 'ECharts 入门示例'},tooltip: {},legend: {data: ['销量']},xAxis: {data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']},yAxis: {},series: [{name: '销量',type: 'bar',data: [5, 20, 36, 10, 10, 20]}]};// 使用刚指定的配置项和数据显示图表。myChart.setOption(option);</script></body>
</html>

营业额统计:
业务规则及接口设计:
- 营业额指订单状态为已完成的订单金额合计
- 基于可视化报表的折线图展示营业额数据,X轴为日期,Y轴为营业额
- 根据时间选择区间,展示每天的营业额数据

前端想展示数据,也要设计接口从后端获取数据。


返回数据这一块,因为是前端来展示数据,所以我们提供的数据是要依照前端的规则。
具体代码实现:
Controll层:
@Autowiredprivate ReportService reportService;/*** 营业额统计* @param begin* @param end* @return*/@GetMapping("/turnoverStatistics")@ApiOperation("营业额统计")public Result<TurnoverReportVO> turnoverStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){log.info("营业额统计");TurnoverReportVO turnoverReportVO = reportService.turnoverStatistics(begin,end);return Result.success(turnoverReportVO);}
这里有个小细节,就是查询统计营业额,前端传的参数是日期的起始时间和结束时间,
格式是yyyy-MM-dd这种形式,所以,我们也要用这种形式来进行接收。
@DateTimeFormat(pattern = "yyyy-MM-dd"),就用到了这个方法。
Service层:
package com.sky.service.impl;import com.sky.entity.Orders;
import com.sky.mapper.OrderMapper;
import com.sky.service.ReportService;
import com.sky.vo.TurnoverReportVO;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import javax.validation.constraints.Min;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;@Service
public class ReportServiceImpl implements ReportService {@Autowiredprivate OrderMapper orderMapper;@Overridepublic TurnoverReportVO turnoverStatistics(LocalDate begin, LocalDate end) {//封装TurnoverReportVO的dateList对象List<LocalDate> datelist = new ArrayList<>();datelist.add(begin);while(!begin.equals(end)){//将日期加到list集合中begin = begin.plusDays(1);datelist.add(begin);}String datejoin = StringUtils.join(datelist, ",");//封装TurnoverReportVO的turnoverList对象List<Double> turnoverlist = new ArrayList<>();for (LocalDate localDate : datelist) {//查询date日期对应的营业额数据,营业额是指:状态为“已完成”的订单金额合计LocalDateTime beginTime = LocalDateTime.of(localDate, LocalTime.MIN);LocalDateTime endTime = LocalDateTime.of(localDate,LocalTime.MAX);//select sum(amount) from orders where order_time > ? and order_time < ? and status = 5Map map = new HashMap();map.put("begin",beginTime);map.put("end",endTime);map.put("status", Orders.COMPLETED);Double turnover = orderMapper.TurnoverOfSum(map);turnover = turnover==null?0.0:turnover;turnoverlist.add(turnover);}for (Double v : turnoverlist) {System.out.println("今日份营业额:"+ v);}//封装TurnoverReportVO对象TurnoverReportVO turnoverReportVO = new TurnoverReportVO();turnoverReportVO.setDateList(datejoin);turnoverReportVO.setTurnoverList(StringUtils.join(turnoverlist,","));return turnoverReportVO;}
}
思路:
我们观察TurnoverReportVO对象里面有两个值,dateList,turnoverList
一个是日期列表,一个是营业额列表。
我们观察前端页面也能看成

横坐标:是日期 纵坐标:是营业额。
所以我们Service层的思路就很简单了
1:封装dateList对象
创建一个LocalDate的列表,然后通过LocalDate提供的库函数plusDay,将日期由begin一直加到end,并且TurnoverReportVO对象中的dateList对象是一个String,所以我们再调用String.utils的方法对这个列表进行逗号分割。
2:封装turnoverList对象
我们已经有了每一天的日期了,我们需要每一天的营业额(当然这里也不一定是每一天的,我们前端界面是可以选择的,有可能是一周,也有可能是一个月)。
这个turnoverList对象的封装就需要查询数据库了
select sum(amout) from orders where order_time > ? and order_time < ? and status = 5
我们创建一个map对象里面有beginTime,endTime,status,然后传到mapper层,查询数据中的数据。
3:最后再封装TurnoverReportVO对象返回。
Mapper层及注解:
/*** 动态查询营业额* @param map* @return*/Double TurnoverOfSum(Map map);
<select id="TurnoverOfSum" resultType="java.lang.Double">select sum(amount) from sky_take_out.orders<where><if test="begin != null">and order_time > #{begin}</if><if test="end != null">and order_Time < #{end}</if><if test="status != null"> and status = #{status} </if></where></select>
用户统计:
业务规则及接口设计:

业务规则:
- 基于可视化报表的折线图展示用户数据,X轴为日期,Y轴为用户数
- 根据时间选择区间,展示每天的用户总量和新增用户量数据


用户统计和上面的不同点就是我们的返回值有三个列表:
- dateList
- newUserList
- totalUserList
具体代码实现:
整体的思路和上面的营业额统计差不多
Controll层:
/*** 用户数量统计* @param begin* @param end* @return*/@GetMapping("/userStatistics")@ApiOperation("用户数量统计")public Result<UserReportVO> userStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){log.info("用户数量统计");UserReportVO userReportVO = reportService.userStatistics(begin,end);return Result.success(userReportVO);}
Service层:
/*** 用户数量统计* @param begin* @param end* @return*/@Overridepublic UserReportVO userStatistics(LocalDate begin, LocalDate end) {//封装UserReportVO的dateList对象List<LocalDate> dateList = new ArrayList<>();while (!begin.equals(end)){dateList.add(begin);begin = begin.plusDays(1);}String datejoin = StringUtils.join(dateList, ",");//封装UserReportVO的totalUserList对象//select count(id) from user where create_time < endTime//封装UserReportVO的newUserList对象//select count(id) from user where create_time > beginTime and create_time < endTimeList<Integer> totalUserList = new ArrayList<>();List<Integer> newUserList = new ArrayList<>();for (LocalDate date : dateList) {LocalDateTime beginTime = LocalDateTime.of(date,LocalTime.MIN);LocalDateTime endTime = LocalDateTime.of(date,LocalTime.MAX);Map map = new HashMap();map.put("end",endTime);Integer totalUser = userMapper.UserofSum(map);map.put("begin",beginTime);Integer newUser = userMapper.UserofSum(map);totalUserList.add(totalUser);newUserList.add(newUser);}String totaluserjoin = StringUtils.join(totalUserList, ",");String newluserjoin = StringUtils.join(newUserList, ",");UserReportVO userReportVO = new UserReportVO();userReportVO.setDateList(datejoin);userReportVO.setTotalUserList(totaluserjoin);userReportVO.setNewUserList(newluserjoin);return userReportVO;}
这里Service层的业务就是封装三个列表
要想封装这个用户,我们需要去调userMapper
我们想要知道总共的用户的sql是:
//select count(id) from user where create_time < endTime
截至到这个end时间时候的用户数量。
而新增用户的sql是:
//select count(id) from user where create_time > beginTime and create_time < endTime
我们要给这个新增确定一个日期范围。
userMapper及注解:
Integer UserofSum(Map map);
<select id="UserofSum" resultType="java.lang.Integer">select count(id) from sky_take_out.user<where><if test="begin != null">and create_time > #{begin}</if><if test="end != null">and create_time < #{end}</if></where></select>
订单统计:
业务规则及接口设计:

业务规则:
- 有效订单指状态为 “已完成” 的订单
- 基于可视化报表的折线图展示订单数据,X轴为日期,Y轴为订单数量
- 根据时间选择区间,展示每天的订单总数和有效订单数
- 展示所选时间区间内的有效订单数、总订单数、订单完成率,订单完成率 = 有效订单数 / 总订单数 * 100%
具体的代码实现:
Controll层:
/*** 订单统计* @param begin* @param end* @return*/@GetMapping("/ordersStatistics")@ApiOperation("订单统计")public Result<OrderReportVO> orderStatistics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){log.info("订单统计");OrderReportVO orderReportVO = reportService.ordersStatistics(begin,end);return Result.success(orderReportVO);}
Service层:
/*** 订单统计* @param begin* @param end* @return*/@Overridepublic OrderReportVO ordersStatistics(LocalDate begin, LocalDate end) {//封装OrderReportVO的dateList对象List<LocalDate> dateList = new ArrayList<>();while (!begin.equals(end)){dateList.add(begin);begin = begin.plusDays(1);}String datejoin = StringUtils.join(dateList, ",");//封装OrderReportVO的orderCountList对象//封装OrderReportVO的validOrderCountList对象List<Integer> orderCountList = new ArrayList<>();List<Integer> validOrderCountList = new ArrayList<>();for (LocalDate date : dateList) {LocalDateTime beginTime = LocalDateTime.of(date,LocalTime.MIN);LocalDateTime endTime = LocalDateTime.of(date,LocalTime.MAX);Map map = new HashMap();map.put("begin",beginTime);map.put("end",endTime);Integer totalorders = orderMapper.OrdersofSum(map);map.put("status",Orders.COMPLETED);Integer validorders = orderMapper.OrdersofSum(map);orderCountList.add(totalorders);validOrderCountList.add(validorders);}String totaljoin = StringUtils.join(orderCountList, ",");String validjoin = StringUtils.join(validOrderCountList,",");//计算时间区间内的订单总数量Integer totalOrderCount = orderCountList.stream().reduce(Integer::sum).get();//计算时间区间内的有效订单数量Integer validOrderCount = validOrderCountList.stream().reduce(Integer::sum).get();//计算订单完成率Double orderCompletionRate = 0.0;if(totalOrderCount!=0){orderCompletionRate = validOrderCount.doubleValue()/totalOrderCount.doubleValue();}OrderReportVO orderReportVO = new OrderReportVO();orderReportVO.setDateList(datejoin);orderReportVO.setOrderCountList(totaljoin);orderReportVO.setValidOrderCountList(validjoin);orderReportVO.setTotalOrderCount(totalOrderCount);orderReportVO.setValidOrderCount(validOrderCount);orderReportVO.setOrderCompletionRate(orderCompletionRate);return orderReportVO;}
Mapper层及注解:
/*** 订单统计* @param map* @return*/Integer OrdersofSum(Map map);
<select id="OrdersofSum" resultType="java.lang.Integer">select count(id) from sky_take_out.orders<where><if test="begin != null">and order_time > #{begin}</if><if test="end != null">and order_Time < #{end}</if><if test="status != null">and status=#{status}</if></where></select>
销量排名统计(TOP10):
业务规则及接口设计:

业务规则:
- 根据时间选择区间,展示销量前10的商品(包括菜品和套餐)
- 基于可视化报表的柱状图降序展示商品销量
- 此处的销量为商品销售的份数
具体的代码实现:
Controll层:
/*** TOP10销量统计* @param begin* @param end* @return*/@GetMapping("/top10")@ApiOperation("TOP10销量统计")public Result<SalesTop10ReportVO> Top10Statostics(@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end){log.info("TOP10销量统计");SalesTop10ReportVO salesTop10ReportVO = reportService.Top10Statostics(begin,end);return Result.success(salesTop10ReportVO);}
Service层:
/*** TOP10销量统计* @param begin* @param end* @return*/@Overridepublic SalesTop10ReportVO Top10Statostics(LocalDate begin, LocalDate end) {LocalDateTime beginTime = LocalDateTime.of(begin,LocalTime.MIN);LocalDateTime endTime = LocalDateTime.of(end,LocalTime.MAX);System.out.println("开始时间是:"+beginTime);System.out.println("结束时间是:"+endTime);List<GoodsSalesDTO> goodsSalesDTOList = orderMapper.GetTop10(beginTime,endTime);List<String> names = goodsSalesDTOList.stream().map(GoodsSalesDTO::getName).collect(Collectors.toList());String nameList = StringUtils.join(names, ",");List<Integer> numbers = goodsSalesDTOList.stream().map(GoodsSalesDTO::getNumber).collect(Collectors.toList());String numberList = StringUtils.join(numbers, ",");SalesTop10ReportVO salesTop10ReportVO = new SalesTop10ReportVO();salesTop10ReportVO.setNameList(nameList);salesTop10ReportVO.setNumberList(numberList);return salesTop10ReportVO;}
这题的从sql语句开始分析比较简单
select od.name,sum(od.number) number from order_detail od,orders o
where od.order_id = o.id and o.status = 5 and o.order_time < 2024-05-04 and o.order_time > 2024-05-10
group by od.name
order by number desc
limit 0,10
根据传入的时间范围来获取数据。
Mapper层及注解:
/*** TOP10销量统计* @param beginTime* @param endTime* @return*/List<GoodsSalesDTO> GetTop10(LocalDateTime beginTime, LocalDateTime endTime);
<select id="GetTop10" resultType="com.sky.dto.GoodsSalesDTO">select od.name,sum(od.number) numberfrom sky_take_out.order_detail od,sky_take_out.orders owhere od.order_id = o.id and o.status = 5<if test="beginTime != null">and o.order_time > #{beginTime}</if><if test="endTime != null">and o.order_Time < #{endTime}</if>group by od.nameorder by number desclimit 0,10</select>
结果展示:

相关文章:
Apache ECharts
Apache ECharts介绍: Apache ECharts 是一款基于 Javascript 的数据可视化图表库,提供直观,生动,可交互,可个性化定制的数据可视化图表。 官网地址:https://echarts.apache.org/zh/index.html Apache ECh…...
超详细的胎教级Stable Diffusion使用教程(四)
这套课程分为五节课,会系统性的介绍sd的全部功能和实操案例,让你打下坚实牢靠的基础 一、为什么要学Stable Diffusion,它究竟有多强大? 二、三分钟教你装好Stable Diffusion 三、小白快速上手Stable Diffusion 四、Stable dif…...
串口属性中的BM延时计时器问题
如果使用程序修改则需要修改注册表对应位置如下 第一个示例(217) 第二个示例(219) 需要注意的事情是修改前必须点查看串口名称(例如上图是com5) 程序修改: 有没有办法以编程方式更改USB <…...
PyQt6--Python桌面开发(8.QPlainTextEdit纯文本控件)
QPlainTextEdit纯文本控件...
Java | Leetcode Java题解之第83题删除排序链表中的重复元素
题目: 题解: class Solution {public ListNode deleteDuplicates(ListNode head) {if (head null) {return head;}ListNode cur head;while (cur.next ! null) {if (cur.val cur.next.val) {cur.next cur.next.next;} else {cur cur.next;}}return…...
重生奇迹mu再生宝石怎么用有什么用
重生奇迹mu再生宝石有2个用处: 1、在玛雅哥布林处给380装备加PVP属性4追4以上的380级装备,守护宝石一颗,再生宝石一颗,成功得到PVP装备,失败宝石消失,装备无变化; 2、给非套装点强化属性用法跟祝福,灵魂,生命一样直接往装备上敲,成功得到随机强化属性一…...
pdf 文件版面分析--pdfplumber (python 文档解析提取)
pdfplumber 的特点 1、它是一个纯 python 第三方库,适合 python 3.x 版本 2、它用来查看pdf各类信息,能有效提取文本、表格 3、它不支持修改或生成pdf,也不支持对pdf扫描件的处理 import glob import pdfplumber import re from collection…...
PostgreSQL的学习心得和知识总结(一百四十三)|深入理解PostgreSQL数据库之Support event trigger for logoff
目录结构 注:提前言明 本文借鉴了以下博主、书籍或网站的内容,其列表如下: 1、参考书籍:《PostgreSQL数据库内核分析》 2、参考书籍:《数据库事务处理的艺术:事务管理与并发控制》 3、PostgreSQL数据库仓库…...
https免费证书获取
获取免费证书的网址: Certbot 1. 进入你的linux系统,先安装snapd, yum install snapd 2. 启动snapd service snapd start 3.安装 Certbot snap install --classic certbot 注意如下出现此错误时,需要先建立snap 软连接后&am…...
C语言 | Leetcode C语言题解之第74题搜索二维矩阵
题目: 题解: bool searchMatrix(int** matrix, int matrixSize, int* matrixColSize, int target) {int m matrixSize, n matrixColSize[0];int low 0, high m * n - 1;while (low < high) {int mid (high - low) / 2 low;int x matrix[mid /…...
杰发科技AC7840——软件Sent_HAL39X
0. 序 使用PWM模拟Sent测试下7840的软件sent功能。 参考链接:SENT协议应用笔记 - TechPlus汽车工坊的文章 - 知乎 SENT协议 1. Sent功能测试 使用提供的软件Sent代码在7840上测试,接收数据OK 2. 参考资料 3. 数据解析 我们个根据上述参考资料尝试解析…...
IOS 开发 - block 使用详解
1.Blobk的定义 block的写法相对难记,不必司机应被,只需要在xcode里打出"inlineBlock"--回车, 系统会自动帮你把基础版写法给你匹配出来 //Block的基础声明//等号""之前是blobk的声明,等号“”后面是block的实现/*returnType:返回类型(void、int、String *…...
BUU-[极客大挑战 2019]Http
考察点 信息收集 http构造请求数据包 题目 解题步骤 参考文章:https://zhuanlan.zhihu.com/p/367051798 查看源代码 发现有一个a标签,但是οnclick"return false"就是点击后不会去跳转到Secret.php的页面 所以我就自己拼接url http://no…...
开发Web3 ETF的技术难点
开发Web3 ETF(Exchange-Traded Fund,交易所交易基金)软件时,需要注意以下几个关键问题。开发Web3 ETF软件是一个复杂的过程,涉及到金融、法律和技术多个领域的专业知识。开发团队需要综合考虑上述问题,以确…...
【K8s】Kubectl 常用命令梳理
Kubectl常用命令梳理 下面包含大致涵盖命令只需要替换对应的Pod \ NameSpace 查看 命名空间 是 ’worktest2‘ 下 名字包括 ’todo‘的所有 Pod kubectl -n worktest2 get pod|grep todo查看 所有命名空间下 名字包括 ’todo‘的所有 Pod kubectl get pods --all-namespace…...
机器学习-监督学习
监督学习是机器学习和人工智能中的一个重要分支,它涉及使用已标记的数据集来训练算法,以便对数据进行分类或准确预测结果。监督学习的核心在于通过输入数据(特征)和输出数据(标签或类别)之间的关系…...
搭建Docker私服镜像仓库Harbor
1、概述 Harbor是由VMware公司开源的企业级的Docker Registry管理项目,它包括权限管理(RBAC)、LDAP、日志审核、管理界面、自我注册、镜像复制和中文支持等功能。 Harbor 的所有组件都在 Dcoker 中部署,所以 Harbor 可使用 Docker Compose 快速部署。 …...
SpringBoot自定义初始化sql文件 支持多类型数据库
我在resources目录下有init.sql初始化sql语句 指定sql文件的地址 sql内容如下: /*角色表*/ INSERT INTO #{schema}ccc_base_role (id, create_time, create_user_id, is_delete, role_name, status, update_time, update_user_id) VALUES(b89e30d81acb88448d412…...
nginx--FastCGI
CGI 概念 nginx通过与第三方基于协议实现,即通过某种特定协议将客户端请求转发给第三方服务处理,第三方服务器会新建新的进程处理用户的请求,处理完成后返回数据给Nginx并回收进程(下次处理有需要新建),最后nginx在返回给客户端…...
【数据结构】浅谈
✨✨✨专栏:数据结构 🧑🎓个人主页:SWsunlight 目录 一、概念: 二、物理结构: 1、顺序存储结构: 2、链式存储结构: 3、数据索引存储结构: 4、数据散列存储结构…...
Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
React Native 开发环境搭建(全平台详解)
React Native 开发环境搭建(全平台详解) 在开始使用 React Native 开发移动应用之前,正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南,涵盖 macOS 和 Windows 平台的配置步骤,如何在 Android 和 iOS…...
抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》
在注意力分散、内容高度同质化的时代,情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现,消费者对内容的“有感”程度,正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中࿰…...
第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明
AI 领域的快速发展正在催生一个新时代,智能代理(agents)不再是孤立的个体,而是能够像一个数字团队一样协作。然而,当前 AI 生态系统的碎片化阻碍了这一愿景的实现,导致了“AI 巴别塔问题”——不同代理之间…...
Spring Boot+Neo4j知识图谱实战:3步搭建智能关系网络!
一、引言 在数据驱动的背景下,知识图谱凭借其高效的信息组织能力,正逐步成为各行业应用的关键技术。本文聚焦 Spring Boot与Neo4j图数据库的技术结合,探讨知识图谱开发的实现细节,帮助读者掌握该技术栈在实际项目中的落地方法。 …...
【RockeMQ】第2节|RocketMQ快速实战以及核⼼概念详解(二)
升级Dledger高可用集群 一、主从架构的不足与Dledger的定位 主从架构缺陷 数据备份依赖Slave节点,但无自动故障转移能力,Master宕机后需人工切换,期间消息可能无法读取。Slave仅存储数据,无法主动升级为Master响应请求ÿ…...
大学生职业发展与就业创业指导教学评价
这里是引用 作为软工2203/2204班的学生,我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要,而您认真负责的教学态度,让课程的每一部分都充满了实用价值。 尤其让我…...
iOS性能调优实战:借助克魔(KeyMob)与常用工具深度洞察App瓶颈
在日常iOS开发过程中,性能问题往往是最令人头疼的一类Bug。尤其是在App上线前的压测阶段或是处理用户反馈的高发期,开发者往往需要面对卡顿、崩溃、能耗异常、日志混乱等一系列问题。这些问题表面上看似偶发,但背后往往隐藏着系统资源调度不当…...
JavaScript基础-API 和 Web API
在学习JavaScript的过程中,理解API(应用程序接口)和Web API的概念及其应用是非常重要的。这些工具极大地扩展了JavaScript的功能,使得开发者能够创建出功能丰富、交互性强的Web应用程序。本文将深入探讨JavaScript中的API与Web AP…...
