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();
}

SSH(2)的更多相关文章

  1. [linux]阿里云主机的免登陆安全SSH配置与思考

    公司服务器使用的第三方云端服务,即阿里云,而本地需要经常去登录到服务器做相应的配置工作,鉴于此,每次登录都要使用密码是比较烦躁的,本着极速思想,我们需要配置我们的免登陆. 一 理论概述 SSH介绍 S ...

  2. SSH实战 · 唯唯乐购项目(上)

    前台需求分析 一:用户模块 注册 前台JS校验 使用AJAX完成对用户名(邮箱)的异步校验 后台Struts2校验 验证码 发送激活邮件 将用户信息存入到数据库 激活 点击激活邮件中的链接完成激活 根 ...

  3. 记录一则Linux SSH的互信配置过程

    需求:四台Linux主机,IP地址为192.168.10.10/11/12/13,配置登录用户的互信 1.各节点ssh-keygen生成RSA密钥和公钥 ssh-keygen -q -t rsa -N ...

  4. SSH免手动输入密码和设置代理

    通过使用sshpass将密码写入命令里,直接执行,免去手动密码输入的步骤命令如下: sshpass -p password_abc ssh user_abc@ssh_host -p ssh_port ...

  5. github免输用户名/密码SSH登录的配置

    从github上获取的,自己整理了下,以备后用. Generating an SSH key mac windows SSH keys are a way to identify trusted co ...

  6. Linux 利用Google Authenticator实现ssh登录双因素认证

    1.介绍 双因素认证:双因素身份认证就是通过你所知道再加上你所能拥有的这二个要素组合到一起才能发挥作用的身份认证系统.双因素认证是一种采用时间同步技术的系统,采用了基于时间.事件和密钥三变量而产生的一 ...

  7. mac下生成ssh keys 并上传github仓储

    使用github仓储需要本机生成一个公钥key 添加到自己的git账户SSH keys中   mac 生成方法:   1. 打开终端 输入   ssh-keygen 然后系统提示输入文件保存位置等信息 ...

  8. Linux实战教学笔记05:远程SSH连接服务与基本排错(新手扫盲篇)

    第五节 远程SSH连接服务与基本排错 标签(空格分隔):Linux实战教学笔记-陈思齐 第1章 远程连接LInux系统管理 1.1 为什么要远程连接Linux系统 在实际的工作场景中,虚拟机界面或物理 ...

  9. 树莓派3B的食用方法-1(装系统 网线ssh连接)

    首先要有一个树莓派3B , 在某宝买就行, 这东西基本上找到假货都难,另外国产和英国也没什么差别,差不多哪个便宜买哪个就行. 不要买店家的套餐,一个是配的东西有些不需要,有的质量也不好. 提示:除了G ...

  10. linux启动SSH及开机自动启动

    本文地址 分享提纲: 1.查看是否启动 2. 设置自动启动 1.[查看是否启动] 启动SSH服务 “/etc/init.d/sshd start”.然后用netstat -antulp | grep ...

随机推荐

  1. Mac原生双拼布局

    先上图: 来自苹果官方网站:Chinese Input Method: 使用简体拼音输入源 下面仅列出与自然码方案不同的地方.相同的不再罗列.后面括号内红色的为自然码双拼布局对应的按键.单韵母使用o开 ...

  2. asp.net 验证正则表达式

    基本元字符: . 任意的一个非换行字符 [] 集合匹配,匹配一个[]中出现的字符. 是在多个字符中取一个. () 调整优先级的作用. 还有一个分组的作用 | 或的意思,测试|一下. 注意,或的优先级最 ...

  3. 修改linux下某一个文件夹下所有文件内容

    find /data/app_resource -type f |xargs sed -i 's/192.168.220.126/192.168.221.160/g'

  4. GZFramwork数据库层《二》单据表增删改查(自动生成单据号码)

    运行效果: 使用代码生成器(GZCodeGenerate)生成tb_EmpLeave的Model 生成器源代码下载地址: https://github.com/GarsonZhang/GZCodeGe ...

  5. Number To Indian Rupee Words in Oracle Forms / Reports

    Convert numbers to Indian Rupees format in Oracle Forms / Reports.Create the below mention function ...

  6. Redis哈希表的实现要点

    Redis哈希表的实现要点 哈希算法的选择 针对不同的key使用不同的hash算法,如对整型.字符串以及大小写敏感的字符串分别使用不同的hash算法: 整型的Hash算法使用的是Thomas Wang ...

  7. php : mysql数据库操作类演示

    设计目标: 1,该类一实例化,就可以自动连接上mysql数据库: 2,该类可以单独去设定要使用的连接编码(set names XXX) 3,该类可以单独去设定要使用的数据库(use XXX): 4,可 ...

  8. Python生成字体

      Python version 2.7 required, which was not found in the registry 参考:http://www.cnblogs.com/min0208 ...

  9. JavaScript学习笔记及知识点整理_2

    1.一般而言,在Javascript中,this指向函数执行时的当前对象.举例如下: var someone = { name: "Bob", showName: function ...

  10. APP链接请求电话

    1.使用OpenURL执行:[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"tel:07551111&qu ...