SSH(2)

时间:2020-12-08 01:08:56

1、用户登录

index页面跳转到登录页面

<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
response.sendRedirect(basePath+"sys/login_toLoginUI.action");
%>

登录验证

    //跳转到登录页
public String toLoginUI(){
return "loginUI";
}
public String login(){
if(user !=null)
{
if(StringUtils.isNotBlank(user.getAccount())&&StringUtils.isNotBlank(user.getPassword()))
{
List<User> list = userService.findUserByAccountAndPass(user.getAccount(),user.getPassword());
if(list!=null && list.size()>0)
{
//将用户信息放到session中
User user = list.get(0);
ServletActionContext.getRequest().getSession().setAttribute(Constant.USER,user);
Log log = LogFactory.getLog(getClass());
log.info("用户名称为"+user.getName()+"登录了系统");
return "home";
}else {
loginResult="账号或密码错误!";
}
}
else {
loginResult="账号和你妈不能为空!";
}
}else{
loginResult="请输入账号和密码!";
}
return toLoginUI();
}

登录拦截器,在web.xml中放在struts拦截器前面

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
String url = request.getRequestURI();
System.out.println(request.getRequestURI());
System.out.println(request.getRequestURL());
//访问地址不是登录的
if(!url.contains("sys/login_")){
//从session中获取登录信息
if(request.getSession().getAttribute(Constant.USER) !=null){
chain.doFilter(request,response);
}else {
//重定向到登录页面
response.sendRedirect(request.getContextPath()+"/sys/login_toLoginUI.action");
}
}else {
//访问地址是登录的
chain.doFilter(request,response);
}
}

权限鉴定,给用户添加一个属性存放UserRole的列表,由于该属性不影响映射文件,因此可以添加,在用户登录时将user信息放置到session之前将角色列表设置到user中。

    public String login(){
if(user !=null)
{
if(StringUtils.isNotBlank(user.getAccount())&&StringUtils.isNotBlank(user.getPassword()))
{
List<User> list = userService.findUserByAccountAndPass(user.getAccount(),user.getPassword());
if(list!=null && list.size()>0)
{
User user = list.get(0);
//根据用户查询所有的角色
user.setUserRoleList(userService.findRolesByUserId(user.getId()));
//将用户信息放到session中
ServletActionContext.getRequest().getSession().setAttribute(Constant.USER,user);
Log log = LogFactory.getLog(getClass());
log.info("用户名称为"+user.getName()+"登录了系统");
return "home";
}else {
loginResult="账号或密码错误!";
}
}
else {
loginResult="账号和密码不能为空!";
}
}else{
loginResult="请输入账号和密码!";
}
return toLoginUI();
}

在过滤器中验证用户权限

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
String url = request.getRequestURI();
// System.out.println(request.getRequestURI());
// System.out.println(request.getRequestURL());
//访问地址不是登录的
if(!url.contains("sys/login_")){
//从session中获取登录信息
User user = (User) request.getSession().getAttribute(Constant.USER);
if(user !=null){
//判断是否访问纳税服务子系统,进行权限检查
if(url.contains("/nsfw/")){
WebApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(request.getSession().getServletContext());
PermissionCheck pc = (PermissionCheck) applicationContext.getBean("permissionCheck");
if(pc.isAccessible(user,"nsfw")){
//有权限,放行
chain.doFilter(request, response);
}
else{
//没有权限,跳转到没有权限
response.sendRedirect(request.getContextPath()+"/sys/login_toNoPermissionUI.action");
}
}else {
chain.doFilter(request, response);
}
}else {
//重定向到登录页面
response.sendRedirect(request.getContextPath()+"/sys/login_toLoginUI.action");
}
}else {
//访问地址是登录的
chain.doFilter(request,response);
}
}

PermissionCheckImpl类

public class PermissionCheckImpl implements PermissionCheck{
@Resource
private UserService userService;
@Override
public boolean isAccessible(User user, String code) {
//获取用户的所有角色
List<UserRole> roles = user.getUserRoleList();
if(roles == null){
roles = userService.findRolesByUserId(user.getId());
}
if(roles!= null && roles.size()>0)
{
for(UserRole userRole:roles){
//遍历用户的每个角色
Role role = userRole.getId().getRole();
//遍历每个角色的权限
for(RolePrivilege rp:role.getRolePrivileges())
{
if(code.equals(rp.getId().getCode())) {
//具有权限
return true;
}
}
}
}
return false;
}
}

在ioc容器中设置bean

    <bean id="permissionCheck" class="com.juaner.core.permission.impl.PermissionCheckImpl"></bean>

2.解决子框架嵌套的问题

在login.jsp中,如果当前页有父亲页面,刷新该页面

//解决子框架嵌套的问题
if(window != window.parent){
window.parent.location.reload(true);
}

3.异步更新信息状态

                                <td id="show_<s:property value='infoId'/>" align="center"><s:property value="state==1?'发布':'停用'"/></td>
<td align="center">
<span id="oper_<s:property value='infoId'/>" >
<s:if test="state == 1">
<a href="javascript:doPublic('<s:property value='infoId'/>',0)")>停用</a>
</s:if>
<s:else>
<a href="javascript:doPublic('<s:property value='infoId'/>',1)")>发布</a>
</s:else>
</span>
<a href="javascript:doEdit('<s:property value='infoId'/>')">编辑</a>
<a href="javascript:doDelete('<s:property value='infoId'/>')">删除</a>
</td>

js

        //异步发布信息
function doPublic(infoId,state){
$.ajax({
url:"${basePth}nsfw/info_publicInfo.action",
data:{"info.infoId":infoId,"info.state":state},
type:"post",
success:function(msg){
alert(msg);
if("更新状态成功" == msg){
if(state == 1){
$("#show_"+infoId).html("发布");
$("#oper_"+infoId).html('<a href="javascript:doPublic(\''+infoId+'\',0)")>停用</a>')
}
else {
$("#show_"+infoId).html("停用");
$("#oper_"+infoId).html('<a href="javascript:doPublic(\''+infoId+'\',0)")>发布</a>')
}
}else {
alert("更新信息状态失败!");
}
},
error:function(){
alert("更新信息状态失败!请检查网络连接状态!");
}
})
}

action

    //信息发布、停用,不能直接更新info,因为会设置除了state和id之外的属性为空
public void publicInfo() {
try {
Info old = infoService.findObjectById(info.getInfoId());
old.setState(info.getState());
infoService.update(old);
ServletOutputStream writer = ServletActionContext.getResponse().getOutputStream();
writer.write("更新状态成功".getBytes("utf-8"));
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}

ueditor

    <script type="text/javascript" charset="utf-8" src="${basePath}js/ueditor/ueditor.config.js"></script>
<script type="text/javascript" charset="utf-8" src="${basePath}js/ueditor/ueditor.all.min.js"> </script>
<script type="text/javascript" charset="utf-8" src="${basePath}js/ueditor/lang/zh-cn/zh-cn.js"></script>
<script>
window.UEDITOR_HOME_URL = "${basePath}js/ueditor"
var ue = UE.getEditor('editor');//id为editor的组件
</script>

4.抽取BaseService、BaseServiceImpl

如何注入BaseServiceImpl中的dao

public class BaseServiceImpl<T> implements BaseService<T>{
private BaseDao<T> baseDao; @Override
public void save(T entity) {
baseDao.save(entity);
} @Override
public void delete(Serializable id) {
baseDao.delete(id);
} @Override
public void update(T entity) {
baseDao.update(entity);
} @Override
public T findObjectById(Serializable id) {
return baseDao.findObjectById(id);
} @Override
public List<T> findObjects() {
return baseDao.findObjects();
} public void setBaseDao(BaseDao<T> baseDao) {
this.baseDao = baseDao;
}
}
public class UserServiceImpl extends BaseServiceImpl<User> implements UserService{
private UserDao userDao; @Resource
public void setUserDao(UserDao userDao) {
super.setBaseDao(userDao);
this.userDao = userDao;
}
...
}

5.模糊查询

QueryHelper类

public class QueryHelper {
private List<Object> parameters;
private String fromClause="";
private String whereClause="";
private String orderByClause=""; public static String ORDER_BY_DESC = "DESC";
public static String ORDER_BY_ASC = "ASC";
/**
*
* @param clazz 类名
* @param alias 别名
*/
public QueryHelper(Class clazz,String alias){
fromClause = "from "+clazz.getSimpleName();
if(StringUtils.isNotBlank(alias))
{
fromClause+=" "+alias;
}
} /**
* @param condition 条件语句,例如 i.title like ?
* @param params 查询条件语句中?对应的查询条件值,例如 %标题%
*/
public void addCondition(String condition,Object... params){
//非第一次添加第一个查询条件
if(whereClause.length()>0)
{
whereClause += " and"+condition;
}else{
whereClause = " where "+condition;
}
//设置查询条件值集合
if(this.parameters == null)
this.parameters = new ArrayList<>();
if(params!=null)
{
for(Object o:params)
this.parameters.add(o);
}
} /**
* 构造orderby子句
* @param property
* @param order
*/
public void addOrderByProperty(String property,String order){
if(StringUtils.isNotBlank(orderByClause)){
orderByClause += ","+property+" "+order;
}else {
orderByClause = " order by "+property+" " +order;
}
}
//查询hql语句
public String getQueryListHql(){
return fromClause+whereClause+orderByClause;
}
//查询条件集合
public List<Object> getParameters(){
return this.parameters;
}
}

BaseDaoImpl中的实现

    @Override
public List<T> findObjects(QueryHelper helper){
String hql = helper.getQueryListHql();
List<Object> parameters = helper.getParameters();
Session session = getSession();
Query query = session.createQuery(hql);
if(parameters !=null && parameters.size()>0)
{
for(int i=0;i<parameters.size();i++){
query.setParameter(i,parameters.get(i));
}
}
return query.list();
}

回显乱码问题

        info.setTitle(URLDecoder.decode(info.getTitle(),"UTF-8"));    

参数丢失问题

在编辑、添加之后,info.title会自动被覆盖掉,所以需要临时变量保存title值,在删除时,需要保存一下title值。

在重定向时,需要将临时变量传递过去

            <result name="list" type="redirectAction">
<param name="actionName">info_listUI</param>
<param name="info.title">${searTitle}</param>
<param name="encode">true</param>
</result>

6.分页

分页bean

查询函数

    @Override
public void getPageResult(QueryHelper helper, PageResult pageResult) {
String hql = helper.getQueryListHql();
List<Object> parameters = helper.getParameters();
Session session = getSession();
Query query = session.createQuery(hql);
if(parameters !=null && parameters.size()>0)
{
for(int i=0;i<parameters.size();i++){
query.setParameter(i,parameters.get(i));
}
}
if(pageResult.getPageNo()<1)
pageResult.setPageNo(1);
//需要总记录数
Query queryCount = getSession().createQuery(helper.getQueryCountHql());
if(parameters !=null && parameters.size()>0)
{
for(int i=0;i<parameters.size();i++){
queryCount.setParameter(i,parameters.get(i));
}
}
pageResult.setTotalCount((Long) queryCount.uniqueResult()); if(pageResult.getPageNo()>pageResult.getTotalPageCount())
pageResult.setPageNo(pageResult.getTotalPageCount());
//得出从哪一条记录开始
int start = (pageResult.getPageNo() - 1) * pageResult.getPageSize();
query.setFirstResult(start);
query.setMaxResults(pageResult.getPageSize());
pageResult.setItems(query.list());
}

记录当前页

编辑时记录当前页

页面中用隐藏域,重定向中用struts参数

搜索时重置当前页为0

删除时更新页面(例如最后一页最后一条数据)

7.投诉信息

条件查询中的日期

用string接收startTime和endTime,然后使用日期转换类转换。

        if(StringUtils.isNotBlank(startTime)) {
startTime = URLDecoder.decode(startTime,"utf-8");
helper.addConditions("compTime>=",DateUtils.parseDate(startTime, new String[]{"yyyy-MM-dd HH:mm"}));
}
if(StringUtils.isNotBlank(endTime)) {
endTime = URLDecoder.decode(endTime,"utf-8");
helper.addConditions("compTime<=", DateUtils.parseDate(endTime, new String[]{"yyyy-MM-dd HH:mm"}));
}

匿名显示手机号

        <tr>
<td class="tdBg">投诉人单位:</td>
<td><s:if test="!complain.isNm"><s:property value="complain.compCompany"/></s:if></td> </tr>
<tr>
<td class="tdBg">投诉人姓名:</td>
<td><s:if test="!complain.isNm"><s:property value="complain.compName"/></s:if></td>
</tr>
<tr>
<td class="tdBg">投诉人手机:</td>
<td>
<s:if test="complain.isNm">
<s:if test="%{complain.compMobile.length()>10}">
<s:property value="%{complain.compMobile.substring(0,3)+'****'+complain.compMobile.substring(7,11)}"/>
</s:if>
</s:if>
<s:else>
<s:property value="complain.compMobile"/>
</s:else>
</td>
</tr>

受理投诉信息

    public String deal(){
if(reply!=null){
complain = complainService.findObjectById(complain.getCompId());
if(reply!=null&&StringUtils.isNotBlank(reply.getReplyContent())){
//修改受理状态
if(!complain.getState().equals(Complain.COMPLAIN_STATE_DONE))
complain.setState(Complain.COMPLAIN_STATE_DONE);
//修改受理状态的时间
reply.setComplain(complain);
reply.setReplyTime(new Timestamp(new Date().getTime()));
complain.getComplainReplies().add(reply);
}
complainService.update(complain);
}
return SUCCESS;
}

获取受理信息时,由于使用hashset存储受理信息,所以显示时,受理信息是乱序的,因此可以在Complain配置文件中设置受理信息的排序属性

        <set name="complainReplies" inverse="true" cascade="save-update,delete" lazy="false" order-by="reply_time">
<key>
<column name="comp_id" length="32" not-null="true" />
</key>
<one-to-many class="com.juaner.nsfw.complain.entity.ComplainReply" />
</set>

异步获取部门信息

        //根据部门查询部门下的用户列表
function doSelectDept(){
//获取部门
var deptName = $("#toCompDept").val();
$("#toCompName").empty();
//根据部门查询列表
if(deptName != "")
{
//清空下拉列表
//设置被投诉人下拉框
$.ajax({
url:"${basePath}sys/home_getUserJson.action",
type:"post",
data:{"dept":deptName},
dataType:"json",
success:function(data){
if(data !=null &&data!=""&& data!=undefined)
{
if("success" == data.msg)
{
$.each(data.userList, function (index,user){
$("#toCompName").append("<option value='"+user.name+"'>"+user.name+"</option>");
})
}else
alert("获取被投诉人列表失败!")
}else
alert("获取被投诉人列表失败!")
},
error:function(){
alert("获取被投诉人列表失败!")
}
})
}

引入jar包

commons-beanutils-1.8.0 ezmorph-1.0.6 struts2-json-plugin-2.3.20

action方法

不使用struts框架:

    public void getUserJson(){
//获取部门
String dept = ServletActionContext.getRequest().getParameter("dept");
if(StringUtils.isNotBlank(dept)) {
//根据部门查询用户列表
QueryHelper helper = new QueryHelper(User.class, "u");
helper.addCondition("u.dept like ?",dept);
List<User> userList = userService.findObjects(helper);
//以json格式输出用户列表
JSONObject json = new JSONObject();
json.put("msg","success");
json.accumulate("userList",userList); try{
ServletOutputStream writer = ServletActionContext.getResponse().getOutputStream();
writer.write(json.toString().getBytes("utf-8"));
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
    public void  getUserNameByDept() throws IOException {
QueryHelper helper = new QueryHelper(User.class,null);
helper.addConditions("dept=",deptName);
List<User> userList =userService.findObjects(helper);
HashMap<String, Object> result = new HashMap<>();
result.put("msg","success");
result.put("userList",userList);
JSONArray jsonArray = JSONArray.fromObject(result);
HttpServletResponse response = ServletActionContext.getResponse();
ServletOutputStream outputStream = response.getOutputStream();
outputStream.write(jsonArray.toString().getBytes("utf-8"));
outputStream.flush();
outputStream.close();
}

使用struts框架:

设置属性private Map<String,Object> return_map及其get、set方法

    public String getUserJson2(){
//获取部门
String dept = ServletActionContext.getRequest().getParameter("dept");
if(StringUtils.isNotBlank(dept)) {
//根据部门查询用户列表
QueryHelper helper = new QueryHelper(User.class, "u");
helper.addCondition("u.dept like ?","%"+dept+"%");
List<User> userList = userService.findObjects(helper);
return_map = new HashMap<>();
return_map.put("msg","success");
return_map.put("userList",userList);
}
return SUCCESS;
}

配置

    <package name="homeJson-action" namespace="/sys" extends="json-default">
<action name="home_getUserJson2" class="com.juaner.home.action.HomeAction" method="getUserJson2">
<result type="json">
<param name="root">return_map</param>
</result>

</action>
</package>

8.保存投诉信息

需要使用同步ajax保存投诉信息,然后刷新父窗口,关闭当前窗口

        function doSubmit(){
//ajax同步提交表单
$.ajax({
url: "${basePath}sys/home_complainAdd.action",
data: $("#form").serialize(),
type:"post",
async:false,
success:function(msg){
if("success" == msg)
{
//提示用户投诉成功
alert("投诉成功!");
//刷新父窗口
window.opener.parent.location.reload(true);
// 关闭当前窗口
window.close();
}else {
alert("投诉失败!")
}
},
error:function(){
alert("投诉失败!")
}
})
}

action中方法

    public void complainAdd(){
try {
if(comp!=null){
//投诉人信息
//受理状态
comp.setState(Complain.COMPLAIN_STATE_UNDONE);
//投诉时间
comp.setCompTime(new Timestamp(new Date().getTime()));
complainService.save(comp);
ServletOutputStream writer = ServletActionContext.getResponse().getOutputStream();
writer.write("success".toString().getBytes("utf-8"));
writer.flush();
writer.close();
}
}catch (Exception e){
e.printStackTrace();
}
}

9.quartz

spring支持quartz需要导入的包:org.springframework.context.support-3.0.2.RELEASE

指定任务信息bean MethodInvokingJobDetailFactoryBean

  设置执行对象

  设置指定对象中对应的执行方法

  设置是否同步执行

制定任务执行时机(执行触发器)bean

  简单触发器 SimpleTriggerBean

    设置任务详细信息对象

    设置任务延迟执行时间

    设置任务执行频率

  任务触发器 CronTrigger

    设置任务详细信息对象

    设置执行时机cron表达式

设置任务调度工厂bean SchedulerFactoryBean

  设置触发器们

每月最后一天对本月之前的投诉进行自动处理,将投诉信息的状态改为已失效

cronExpression:0 0 2 L * ?  秒 分 时 日 月 周 年 ?表示不设置,年是可选的,*表示任意的

'*' 字符可以用于所有字段,在“分”字段中设为"*"表示"每一分钟"的含义。

'?' 字符可以用在“日”和“周几”字段. 它用来指定 '不明确的值'. 这在你需要指定这两个字段中的某一个值而不是另外一个的时候会被用到。在后面的例子中可以看到其含义。

'-' 字符被用来指定一个值的范围,比如在“小时”字段中设为"10-12"表示"10点到12点".

',' 字符指定数个值。比如在“周几”字段中设为"MON,WED,FRI"表示"the days Monday, Wednesday, and Friday".

'/' 字符用来指定一个值的的增加幅度. 比如在“秒”字段中设置为"0/15"表示"第0, 15, 30, 和 45秒"。而 "5/15"则表示"第5, 20, 35, 和 50". 在'/'前加"*"字符相当于指定从0秒开始. 每个字段都有一系列可以开始或结束的数值。对于“秒”和“分”字段来说,其数值范围为0到59,对于“小时”字段来说其为0到23, 对于“日”字段来说为0到31, 而对于“月”字段来说为1到12。"/"字段仅仅只是帮助你在允许的数值范围内从开始"第n"的值。 因此对于“月”字段来说"7/6"只是表示7月被开启而不是“每六个月”, 请注意其中微妙的差别。

'L'字符可用在“日”和“周几”这两个字段。它是"last"的缩写, 但是在这两个字段中有不同的含义。例如,“日”字段中的"L"表示"一个月中的最后一天" —— 对于一月就是31号对于二月来说就是28号(非闰年)。而在“周几”字段中, 它简单的表示"7" or "SAT",但是如果在“周几”字段中使用时跟在某个数字之后, 它表示"该月最后一个星期×" —— 比如"6L"表示"该月最后一个周五"。当使用'L'选项时,指定确定的列表或者范围非常重要,否则你会被结果搞糊涂的。

'W' 可用于“日”字段。用来指定历给定日期最近的工作日(周一到周五) 。比如你将“日”字段设为"15W",意为: "离该月15号最近的工作日"。因此如果15号为周六,触发器会在14号即周五调用。如果15号为周日, 触发器会在16号也就是周一触发。如果15号为周二,那么当天就会触发。然而如果你将“日”字段设为"1W", 而一号又是周六, 触发器会于下周一也就是当月的3号触发,因为它不会越过当月的值的范围边界。'W'字符只能用于“日”字段的值为单独的一天而不是一系列值的时候。

'L'和'W'可以组合用于“日”字段表示为'LW',意为"该月最后一个工作日"。

'#' 字符可用于“周几”字段。该字符表示“该月第几个周×”,比如"6#3"表示该月第三个周五( 6表示周五而"#3"该月第三个)。再比如: "2#1" = 表示该月第一个周一而 "4#5" = 该月第五个周三。注意如果你指定"#5"该月没有第五个“周×”,该月是不会触发的。

'C' 字符可用于“日”和“周几”字段,它是"calendar"的缩写。它表示为基于相关的日历所计算出的值(如果有的话)。如果没有关联的日历, 那它等同于包含全部日历。“日”字段值为"5C"表示"日历中的第一天或者5号及其以后",“周几”字段值为"1C"则表示"日历中的第一天或者周日及其以后"。
    <!--指定任务详细信息-->
<bean id="complainJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="complainService"></property>
<property name="targetMethod" value="autoDeal"></property>
<property name="concurrent" value="false"></property>
</bean>
<!--任务执行时机-->
<bean id="complainCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="complainJobDetail"></property>
<property name="cronExpression" value="0 0 2 L * ?"></property>
</bean>
<!--设置任务执行时间点-->
<bean id="complainScheduler" class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="complainCronTrigger"/>
</list>
</property>
</bean>

小于本月0时0分0秒的数据

    @Override
public void autoDeal() {
Calendar cal = Calendar.getInstance();
// cal.add(Calendar.MONTH,1);//月份加1
        cal.set(Calendar.DAY_OF_MONTH,1);
cal.set(Calendar.HOUR_OF_DAY,0);
cal.set(Calendar.MINUTE,0);
cal.set(Calendar.SECOND,0);
//查询本月之前待受理的投诉列表
QueryHelper helper = new QueryHelper(Complain.class,"c");
helper.addCondition("c.state = ?",Complain.COMPLAIN_STATE_UNDONE);
helper.addCondition("c.compTime< ?", cal.getTime());
List<Complain> complainList = findObjects(helper);
//更新信息状态为已失效
if(complainList!=null&&complainList.size()>0)
{
for(Complain comp:complainList)
{
comp.setState(Complain.COMPLAIN_STATE_INVALID);
update(comp);
System.out.println("修改了状态");
}
}
}

10.投诉统计图表

统计年度每个月的投诉数

页面中可选择近5年的年份,并根据选择的年份显示投诉统计图表

<%@ page language="java" import="java.util.*" pageEncoding="utf-8"%>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
//获取当前年份
Calendar cal = Calendar.getInstance();
int curYear = cal.get(Calendar.YEAR);
request.setAttribute("curYear",curYear);
List yearList = new ArrayList();
for(int i=0;i<5;i++){
yearList.add(curYear-i);
}
request.setAttribute("yearList",yearList);
%> <!DOCTYPE HTML>
<html>
<head>
<%@include file="/common/header.jsp"%>
<title>年度投诉统计图</title>
<script type="text/javascript" src="${basePath}js/fusioncharts/fusioncharts.js"></script>
<script type="text/javascript" src="${basePath}js/fusioncharts/fusioncharts.charts.js"></script>
<script type="text/javascript" src="${basePath}js/fusioncharts/themes/fusioncharts.theme.fint.js"></script>
<script type="text/javascript">
//加载完元素后执行
$(document).ready(function(){
         doAnnualStatistic()
      });
function doAnnualStatistic(){
//获取年份
var year = $("#year option:selected").val();
if(year == null || year == undefined)
{
year = "${curYear}";
}
$.ajax(
{
url:"${basePath}nsfw/complain_getAnnualStatisticData.action",
type:"post",
data:{"year":year},
dataType:json,
success:function(data){
if(data !=null&&data!=undefined)
{
//获取统计数据
FusionCharts.ready(function(){
var revenueChart = new FusionCharts({
"type": "line",
"renderAt": "chartContainer",
"width": "600",
"height": "400",
"dataSource": {
"chart": {
"caption": year+"年年度投诉数统计图",
"xAxisName": "月份",
"yAxisName": "投诉数",
"theme": "fint"
},
"data":data.chartData
}
});
revenueChart.render();
})
}else
alert("获取统计数据失败!")
},
error:function(){
alert("获取统计数据失败!")
}
}
) } </script>
</head> <body>
<br>
<s:select id="year" list="#request.yearList" onchange="doAnnualStatistic()"></s:select>
<br>
<br>
<div id="chartContainer"></div>
</body>
</html>

根据年份获取各个月份的投诉数,新建tmonth表,作为左表进行左连接

SELECT imonth,COUNT(comp_id)
FROM tmonth LEFT JOIN complain ON imonth = MONTH(comp_time)
AND YEAR(comp_time)=2016
GROUP BY imonth
ORDER BY imonth

优化

SELECT imonth,c2
FROM tmonth LEFT JOIN (SELECT MONTH(comp_time) c1,COUNT(comp_id) c2
FROM complain
WHERE YEAR(comp_time)=2016
GROUP BY MONTH(comp_time)
)t ON imonth = t.c1
ORDER BY imonth

如果年份为非当前年份,则月份统计数为null的设置为0

如果年份为当前年份,则需要根据当前月设置月份统计数为null的值

action中方法

    //异步获取统计数据
public String getAnnualStatisticData(){
//获取年份
HttpServletRequest request = ServletActionContext.getRequest();
int year;
if(request.getParameter("year")!=null)
{
year = Integer.parseInt(request.getParameter("year"));
}else{
year = Calendar.getInstance().get(Calendar.YEAR);
}
statisticMap = new HashMap<String, Object>();
statisticMap.put("msg","success");
statisticMap.put("chartData",complainService.getAnnualStatisticDataByYear(year));
return "annualStatisticData";
}

配置struts

<struts>
<package name="complain-action" namespace="/nsfw" extends="struts-default,json-default">
<action name="complain_*" class="com.juaner.nsfw.complain.action.ComplainAction" method="{1}">
<result type="json" name="annualStatisticData">
<param name="root">statisticMap</param>
</result>
</action>
</package>
</struts>

service方法

    public List<Map> getAnnualStatisticDataByYear(int year) {
List<Map> resList = new ArrayList<>();
//获取统计数据
List<Object[]> list = complainDao.getAnnualStatisticDataByYear(year);
//格式化统计结果
if(list!= null && list.size()>0) {
//是不是当前年份
Calendar cal = Calendar.getInstance();
boolean isCurYear = (cal.get(Calendar.YEAR)==year);
int curMonth = cal.get(Calendar.MONTH);
Map<String,Object> map;
int month=0;
int value=0;
for(Object[] obj:list)
{
month = (int) obj[0];
map = new HashMap<>();
map.put("label",month+"月");
if(isCurYear){
if(month>curMonth){
map.put("value",null);
}else {
value = obj[1]==null?0:(int) obj[1];
map.put("value",value);
}
}else {
value = obj[1]==null?0:(int) obj[1];
map.put("value",value);
}
resList.add(map);
}
}
return resList;
}

dao中方法

    public List getAnnualStatistics(int year) {
StringBuffer sb = new StringBuffer();
sb.append("SELECT month, COUNT(comp_id)")
.append(" FROM t_month LEFT JOIN t_complain ON month=MONTH(comp_time)")
.append(" AND YEAR(comp_time)=?")
.append(" GROUP BY month ")
.append(" ORDER BY month");
SQLQuery sqlQuery = getSession().createSQLQuery(sb.toString());
sqlQuery.setParameter(0, year);
return sqlQuery.list();
}