SSH(2)
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)的更多相关文章
- [linux]阿里云主机的免登陆安全SSH配置与思考
公司服务器使用的第三方云端服务,即阿里云,而本地需要经常去登录到服务器做相应的配置工作,鉴于此,每次登录都要使用密码是比较烦躁的,本着极速思想,我们需要配置我们的免登陆. 一 理论概述 SSH介绍 S ...
- SSH实战 · 唯唯乐购项目(上)
前台需求分析 一:用户模块 注册 前台JS校验 使用AJAX完成对用户名(邮箱)的异步校验 后台Struts2校验 验证码 发送激活邮件 将用户信息存入到数据库 激活 点击激活邮件中的链接完成激活 根 ...
- 记录一则Linux SSH的互信配置过程
需求:四台Linux主机,IP地址为192.168.10.10/11/12/13,配置登录用户的互信 1.各节点ssh-keygen生成RSA密钥和公钥 ssh-keygen -q -t rsa -N ...
- SSH免手动输入密码和设置代理
通过使用sshpass将密码写入命令里,直接执行,免去手动密码输入的步骤命令如下: sshpass -p password_abc ssh user_abc@ssh_host -p ssh_port ...
- github免输用户名/密码SSH登录的配置
从github上获取的,自己整理了下,以备后用. Generating an SSH key mac windows SSH keys are a way to identify trusted co ...
- Linux 利用Google Authenticator实现ssh登录双因素认证
1.介绍 双因素认证:双因素身份认证就是通过你所知道再加上你所能拥有的这二个要素组合到一起才能发挥作用的身份认证系统.双因素认证是一种采用时间同步技术的系统,采用了基于时间.事件和密钥三变量而产生的一 ...
- mac下生成ssh keys 并上传github仓储
使用github仓储需要本机生成一个公钥key 添加到自己的git账户SSH keys中 mac 生成方法: 1. 打开终端 输入 ssh-keygen 然后系统提示输入文件保存位置等信息 ...
- Linux实战教学笔记05:远程SSH连接服务与基本排错(新手扫盲篇)
第五节 远程SSH连接服务与基本排错 标签(空格分隔):Linux实战教学笔记-陈思齐 第1章 远程连接LInux系统管理 1.1 为什么要远程连接Linux系统 在实际的工作场景中,虚拟机界面或物理 ...
- 树莓派3B的食用方法-1(装系统 网线ssh连接)
首先要有一个树莓派3B , 在某宝买就行, 这东西基本上找到假货都难,另外国产和英国也没什么差别,差不多哪个便宜买哪个就行. 不要买店家的套餐,一个是配的东西有些不需要,有的质量也不好. 提示:除了G ...
- linux启动SSH及开机自动启动
本文地址 分享提纲: 1.查看是否启动 2. 设置自动启动 1.[查看是否启动] 启动SSH服务 “/etc/init.d/sshd start”.然后用netstat -antulp | grep ...
随机推荐
- PeopleTools预警程序制作
预警程序的概念:在主页上显示一个查询的结果.这个查询就是一个Record. 一.在Application Designer建一个项目,包含所有需要的Record. CUX_REC_BLRY Recor ...
- listener.ora增加监听端口
一个简单的listener.ora # listener.ora Network Configuration File: d:\app\zyd\product\11.2.0\dbhome_1\netw ...
- js 的闭包
今天看了关于js闭包方面的文章,还是有些云里雾里,对于一个菜鸟来说,学习闭包确实有一定的难度,不说别的,能够在网上找到一篇优秀的是那样的不易. 当然之所以闭包难理解,个人觉得是基础知识掌握的不牢,因为 ...
- eclipse快捷方式
虽说右键可以直接发送快捷方式到桌面,但是点击桌面图标确提示错误,偶然发现右键选个什么,配置下启动文件就ok了(就是链接到安装目录里面那个可以启动的exe),后来怎么复现不了,伤感了,不过是可以用了,还 ...
- Jenkins入门系列之——01第一章 Jenkins是什么?
第一章 Jenkins是什么? Jenkins 是一个可扩展的持续集成引擎. 主要用于: l 持续.自动地构建/测试软件项目. l 监控一些定时执行的任务. Jenkins拥有的特性包括: l 易于安 ...
- python :页面布局 ,后台管理页面之左侧菜单跟着滚动条动
左侧菜单跟着滚动条动 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http:// ...
- linux初始化配置---主机名、关闭防火墙、关闭selinux
一.修改主机名 1.零时修改 [root@localhost network-scripts]# hostname jw07 然后就可以看到我们的主机名被修改了
- 【Office Word】论文排版有关技巧
本文分两部分,第一部分呢是Word中标题的编号以及图表的编号:第二部分是MathType中公式编号的右对齐方法. 1. word中标题的编号以及图表的编号 本部分转载自:http://blog ...
- python异常和错误(syntax errors 和 exceptions)
语法错误 语法错误又被称解析错误 >>> for i in range(1..10):print(i) File "<stdin>", line 1 ...
- SQL SA密码丢失
------------记不清了,以下似乎是这样操作的-----NET STOP MSSQLSERVER Net Start MSSQLServer /m"SQLCMD" 安装并使 ...