背景:员工时间段内工作汇报以及考勤信息统计,邮件提醒(每周一和周五邮件提醒)
数据库部分不做说明了,这里仅总结记录java部分的实现。重点是记录自己在java方面的实践。
DAO层设计以及代码
NoteMailMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" ><mapper namespace="com.**.smarteam.web.background.dao.NoteMailMapper"><resultMap type="java.util.Map" id="map"></resultMap><!-- 查询数据 --><select id="getData" resultType="map" parameterType="map">SELECT a.username username, a.deptname deptname, a.userid userid, a.unit_id unitId,IFNULL(b.costs,0) costs,IFNULL(c.standard_times,0) standardTimes,concat(CASE WHEN IFNULL(c.standard_times,0) > 0 THEN ROUND((b.costs/c.standard_times)*100,2)ELSE ''END,'%') AS notesRate, IFNULL(c.oacosts,0) oacosts,concat(CASE WHEN IFNULL(c.standard_times,0) > 0 THEN ROUND((c.oacosts/c.standard_times)*100,2)ELSE ''END,'%') AS oaRate,concat(CASE WHEN IFNULL(c.oacosts,0) > 0 THEN ROUND((b.costs/c.oacosts)*100,2)ELSE ''END,'%') AS notesoaRateFROM(SELECT m.NAME username, u.NAME deptname, p.ID userid, u.id unit_idFROM org_member m LEFT JOIN org_principal pON p.MEMBER_ID = m.IDLEFT JOIN org_unit uON u.id = m.ORG_DEPARTMENT_IDWHERE u.PATH LIKE '0000000300030006%' AND p.id IS NOT NULL AND p.IS_ENABLE = 1 ORDER BY 2) aLEFT JOIN (SELECT created_by, IFNULL(SUM(cost),0) costsFROM project_notes_tbWHERE notes_time <![CDATA[>=]]> #{startDate}AND notes_time <![CDATA[<=]]> #{endDate}GROUP BY created_by) bON a.userid = b.created_byLEFT JOIN (SELECT IFNULL(ROUND(SUM(oacosts),1),0) oacosts , login_id , (SELECT SUM(a.standard_time) standard_timesFROM (SELECT holiday, holiday_type, CASE holiday_type WHEN 0 THEN 8ELSE 0END AS standard_timeFROM kpi_holidays) aWHERE a.holiday <![CDATA[>=]]> #{startDate}AND a.holiday <![CDATA[<=]]> #{endDate}) standard_timesFROM oa_costs_viewWHERE card_date <![CDATA[>=]]> #{startDate}AND card_date <![CDATA[<=]]> #{endDate}GROUP BY login_id)cON c.login_id = b.created_by<where><if test="deptId!=null and deptId!=''">a.unit_id = #{deptId}AND a.userid NOT IN (SELECT p.ID FROM org_principal pLEFT JOINorg_member mON p.MEMBER_ID = m.IDLEFT JOIN org_level lON l.ID = m.ORG_LEVEL_IDWHERE m.ORG_DEPARTMENT_ID = #{deptId}AND l.LEVEL_ID = (SELECT MIN(l.LEVEL_ID)FROM org_principal pLEFT JOINorg_member mON p.MEMBER_ID = m.IDLEFT JOIN org_level lON l.ID = m.ORG_LEVEL_IDWHERE m.ORG_DEPARTMENT_ID = #{deptId}GROUP BY m.ORG_DEPARTMENT_ID))</if></where>ORDER BY notesRate DESC</select><!-- 查询数据记录数 --><select id="countData" resultType="int" parameterType="map">SELECT count(*)FROM(SELECT m.NAME username, u.NAME deptname, p.ID userid, u.id unit_idFROM org_member m LEFT JOIN org_principal pON p.MEMBER_ID = m.IDLEFT JOIN org_unit uON u.id = m.ORG_DEPARTMENT_IDWHERE u.PATH LIKE '0000000300030006%' AND p.id IS NOT NULL AND p.IS_ENABLE = 1 ORDER BY 2) aLEFT JOIN (SELECT created_by, IFNULL(SUM(cost),0) costsFROM project_notes_tbWHERE notes_time <![CDATA[>=]]> #{startDate}AND notes_time <![CDATA[<=]]> #{endDate}GROUP BY created_by) bON a.userid = b.created_byLEFT JOIN (SELECT IFNULL(ROUND(SUM(oacosts),1),0) oacosts , login_id , (SELECT SUM(a.standard_time) standard_timesFROM (SELECT holiday, holiday_type, CASE holiday_type WHEN 0 THEN 8ELSE 0END AS standard_timeFROM kpi_holidays) aWHERE a.holiday <![CDATA[>=]]> #{startDate}AND a.holiday <![CDATA[<=]]> #{endDate}) standard_timesFROM oa_costs_viewWHERE card_date <![CDATA[>=]]> #{startDate}AND card_date <![CDATA[<=]]> #{endDate}GROUP BY login_id)cON c.login_id = b.created_by<where><if test="deptId!=null and deptId!=''">a.unit_id = #{deptId}</if></where></select><!-- 获取技术体系所有的叶子部门 --><select id="getAllTechLeafDept" resultMap="map">SELECT id , nameFROM org_unitWHERE PATH LIKE '0000000300030006%'AND IS_ENABLE = 1AND CHAR_LENGTH(path) >= 28</select><!-- 获取部门领导数量 若数据不等于1 则该部门没有领导 --><select id="countLeaders" parameterType="long" resultType="int">SELECT count(*)FROM org_principal pLEFT JOINorg_member mON p.MEMBER_ID = m.IDLEFT JOIN org_level lON l.ID = m.ORG_LEVEL_IDWHERE m.ORG_DEPARTMENT_ID = #{deptId}AND l.LEVEL_ID = (SELECT MIN(l.LEVEL_ID)FROM org_principal pLEFT JOINorg_member mON p.MEMBER_ID = m.IDLEFT JOIN org_level lON l.ID = m.ORG_LEVEL_IDWHERE m.ORG_DEPARTMENT_ID = #{deptId}GROUP BY m.ORG_DEPARTMENT_ID) </select><!-- 获取部门领导信息 --><select id="getLeaderInfo" resultMap="map" parameterType="long"> SELECT p.ID userid, m.ORG_DEPARTMENT_ID deptId, l.LEVEL_ID levelId, p.LOGIN_NAME username, m.EXT_ATTR_2 usermailFROM org_principal pLEFT JOINorg_member mON p.MEMBER_ID = m.IDLEFT JOIN org_level lON l.ID = m.ORG_LEVEL_IDWHERE m.ORG_DEPARTMENT_ID = #{deptId}AND l.LEVEL_ID = (SELECT MIN(l.LEVEL_ID)FROM org_principal pLEFT JOINorg_member mON p.MEMBER_ID = m.IDLEFT JOIN org_level lON l.ID = m.ORG_LEVEL_IDWHERE m.ORG_DEPARTMENT_ID = #{deptId}GROUP BY m.ORG_DEPARTMENT_ID)</select></mapper>Serveice层设计以及代码
NoteMailMapper.java
package com.**.smarteam.web.background.dao;
import java.util.List;import java.util.Map;public interface NoteMailMapper {/** * 获取汇报统计数据 * @param map * @return */List<Map> getData(Map map);/** * 获取汇报统计数据的记录数 * @param map * @return */int countData(Map map);/** * 获取技术体系所有的叶子部门 id name * @return */List<Map> getAllTechLeafDept();/** * 获取部门领导数量 若数据不等于1 则该部门没有领导 * @param deptId * @return */int countLeaders(long deptId);/** * 获取部门领导信息 * @param deptId * @return */Map getLeaderInfo(long deptId);}Service层设计代码
接口NoteMailService.java
package com.**.smarteam.web.background.service;
import java.util.List;import java.util.Map;import javax.mail.Session;import javax.mail.internet.MimeMessage;public interface NoteMailService {/** * 获取技术体系所有的叶子部门列表信息 id name * @return */List<Map> getAllDeptList();/** * 判断该部门是不是有部门领导 * @param deptId * @return */boolean hasLeader(Long deptId);/** *获取领导信息 * @param deptId * @return */Map getLeaderInfo(long deptId);/** * 获取部门的汇报考勤统计数据 * @param deptId * @return */List<Map> getData(long deptId);/** * 获取部门的汇报考勤统计数据 de 记录数 * @param long deptId * @return */int countData(long deptId);/** * 生成邮件正文 * @param deptId * @return */String mailBody(Map params);/** * 生成邮件MimeMessage * @param session * @param deptId * @return * @throws Exception */MimeMessage createMail(Session session,Map map)throws Exception;boolean sendMail(Map map);}
NoteMailServiceImpl.java 实现
package com.**.smarteam.web.background.service.impl;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.mail.Message;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import org.apache.http.impl.client.TunnelRefusedException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.**.smarteam.core.common.DateUtils;
import com.**.smarteam.web.background.dao.NoteMailMapper;
import com.**.smarteam.web.background.service.NoteMailService;
@Service
public class NoteMailServiceImpl implements NoteMailService {
@Autowired
private NoteMailMapper noteMailMapper;
@Override
public List<Map> getAllDeptList() {
return noteMailMapper.getAllTechLeafDept();
}
@Override
public boolean hasLeader(Long deptId) {
return 1 == noteMailMapper.countLeaders(deptId) ? true : false;
}
@Override
public Map getLeaderInfo(long deptId) {
// TODO Auto-generated method stub
return noteMailMapper.getLeaderInfo(deptId);
}
@Override
public List<Map> getData(long deptId) {
Map map = new HashMap();
map.put("deptId", deptId);
//date
List<String> startAndEndDate = DateUtils.getStartAndEndDate();
map.put("startDate", startAndEndDate.get(0));
map.put("endDate", startAndEndDate.get(1));
return noteMailMapper.getData(map);
}
@Override
public int countData(long deptId) {
Map map = new HashMap();
map.put("deptId", deptId);
//date
List<String> startAndEndDate = DateUtils.getStartAndEndDate();
map.put("startDate", startAndEndDate.get(0));
map.put("endDate", startAndEndDate.get(1));
return noteMailMapper.countData(map);
}
@Override
public boolean sendMail(Map map) {
System.out.println("===========sendMail============");
boolean flag = false;
try {
Properties prop = new Properties();
prop.setProperty("mail.host", "smtp.voole.com");
prop.setProperty("mail.transport.protocol", "smtp");
prop.setProperty("mail.smtp.auth", "true");
Session session = Session.getInstance(prop);
Transport ts = session.getTransport();
ts.connect("mail.voole.com", "[email protected]", "DevOps2017voole");
Message message = createMail(session,map);
ts.sendMessage(message, message.getAllRecipients());
ts.close();
flag = true;
} catch (Exception e) {
flag = false;
e.printStackTrace();
System.out.println("邮件发送失败!");
}
return flag;
}
@Override
public MimeMessage createMail(Session session,Map map) throws Exception{
System.out.println("===========createMail============");
MimeMessage message = new MimeMessage(session);
long deptId = (long) map.get("deptId");
String mForm = (String) map.get("mFrom");
String mSubject = (String) map.get("mSubject");
String deptName = (String) map.get("deptName");
Map leader = getLeaderInfo(deptId);
message.setFrom(new InternetAddress(mForm));
StringBuffer bfBuffer = new StringBuffer();
bfBuffer.append(leader.get("usermail").toString());
bfBuffer.append(",");
bfBuffer.append("***@voole.com");
InternetAddress[] address = new InternetAddress().parse(bfBuffer.toString());
message.setRecipients(Message.RecipientType.TO,address);
// message.setRecipients(Message.RecipientType.TO,"***@voole.com");
// message.setRecipients(Message.RecipientType.TO,leader.get("usermail").toString());
message.setSubject(mSubject);
message.setContent(mailBody(map), "text/html;charset=UTF-8");
return message;
}
@Override
public String mailBody(Map params) {
long deptId = (long) params.get("deptId");
String deptName = (String) params.get("deptName");
System.out.println("===========mailBody============");
StringBuffer bodyBuffer = new StringBuffer();
String[] head = {"deptname", "username", "costs", "standardTimes", "notesRate", "oacosts", "oaRate", "notesoaRate"};
List<Map> dataList = getData(deptId);
List<String> startAndEndDate = DateUtils.getStartAndEndDate();
bodyBuffer.append("<html><head></head><body>"
+ "你好,</br>"
+ "您的部门<b>"+deptName+"</b>人员在"
+ startAndEndDate.get(0) + "--" + startAndEndDate.get(1)
+ "的devops汇报工时以及考勤统计如下:</br>"
+ "<table border=1>"
+ "<tr>"
+ "<th>部门名称</th>"
+ "<th>姓名</th>"
+ "<th>汇报工时</th>"
+ "<th>标准工时</th>"
+ "<th>汇报率(汇报工时/标准工时)</th>"
+ "<th>考勤工时</th>"
+ "<th>出勤率(考勤工时/标准工时)</th>"
+ "<th>考勤汇报率(汇报工时/考勤工时)</th>"
+ "</tr>");
for(int i=0; i<dataList.size(); i++){
bodyBuffer.append("<tr>");
Map map = dataList.get(i);
for(int j=0; j<head.length; j++){
bodyBuffer.append("<td>");
bodyBuffer.append(map.get(head[j]));
bodyBuffer.append("</td>");
}
bodyBuffer.append("</tr>");
}
bodyBuffer.append("</table>");
bodyBuffer.append("</br></br>"
+ "Devops系统提醒邮件!"
+ "</br>");
bodyBuffer.append("</body></html>");
return bodyBuffer.toString();
}
}
Service层功能Junit测试
package com.eliteams.quick4j.test.service;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.**.smarteam.web.background.service.NoteMailService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:applicationContext-shiro.xml","classpath:applicationContext.xml",
"classpath:spring-mvc.xml","classpath:mybatis-config.xml"})
public class NoteMailServiceTest {
@Autowired
private NoteMailService noteMailService;
@Test
public void test(){
List<Map> deptList = noteMailService.getAllDeptList();
System.out.println("------------------size--------" + deptList.size());
for (int i=0; i<deptList.size(); i++) {
System.out.println("================deptList.get(i)==============" + deptList.get(i));
Map map = deptList.get(i);
long deptId = (long) map.get("id");
String deptName = (String) map.get("name");
if (!noteMailService.hasLeader(deptId)) {
continue;
}
Map mailMap = new HashMap();
mailMap.put("deptId", deptId);
mailMap.put("deptName", deptName);
mailMap.put("mFrom", "**@**.com");
mailMap.put("mSubject", "**部门汇报及考勤统计提醒");
if(noteMailService.sendMail(mailMap)){
continue;
}else {
--i;
continue;
}
}
}
}
如果serviceTest跑通调试功能没问题,继续下一步Controller层代码设计开发。实际controller层代码几乎和ServiceTest一样。
Controller层设计以及代码
package com.**.smarteam.web.background.controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
/**
* 技术体系汇报考勤统计邮件提醒
*/
import org.springframework.stereotype.Controller;
import com.**.smarteam.web.background.service.NoteMailService;
import com.**.smarteam.web.project.service.NoteOAReportService;
@Controller
public class NoteMailController {
@Autowired
private NoteMailService noteMailService;
public void sendNoteMail() {
List<Map> deptList = noteMailService.getAllDeptList();
for (int i=0; i<deptList.size(); i++) {
System.out.println("================deptList.get(i)==============");
Map map = deptList.get(i);
long deptId = (long) map.get("id");
String deptName = (String) map.get("name");
if (!noteMailService.hasLeader(deptId)) {
continue;
}
Map mailMap = new HashMap();
mailMap.put("deptId", deptId);
mailMap.put("deptName", deptName);
mailMap.put("mFrom", "**@**.com");
mailMap.put("mSubject", "**部门汇报及考勤统计提醒");
if(noteMailService.sendMail(mailMap)){
continue;
}else {
--i;
continue;
}
}
}
}
工具代码
package com.**.smarteam.core.common;
/**
* by zfy 2018-05-10
*/
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
public class DateUtils {
private static final int START_DATE = 20;
/**
* 获取起始和结束时间
* 结束时间为当前的日期
* 起始时间为:若当前日期大于19号 则起始时间为当前月份的20号;
* 若当前日期小于等于19号,则起始时间为上月的20号;
* 注意年份的变更
* @return List index 0 is startDate
* index 1 is endDate
*/
public static List<String> getStartAndEndDate() {
List<String> list = new ArrayList<String>();
LocalDate ld = LocalDate.now();
LocalDate startDate = null;
int monDay = ld.getDayOfMonth();
int mon = ld.getMonthValue();
int year = ld.getYear();
if (monDay<=19) {
if (12 == mon) {
mon = 1;
year = year - 1;
}else {
mon = mon - 1;
}
startDate = LocalDate.of(year, mon, START_DATE);
}else{
startDate.of(year, mon, START_DATE);
}
list.add(startDate.toString());
list.add(ld.toString());
return list;
}
}
配置spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 扫描controller(controller层注入) -->
<context:component-scan base-package="com.**.smarteam.web.background.controller"/>
<!-- 启用spring mvc 注解 -->
<mvc:annotation-driven />
<!-- 会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,是spring MVC为@Controllers分发请求所必须的 -->
<!-- 对模型视图添加前后缀 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/views/" p:suffix=".jsp"/>
<!-- 配置springMVC处理上传文件的信息 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8"/>
<property name="maxUploadSize" value="10485760000"/>
<property name="maxInMemorySize" value="40960"/>
</bean>
<!-- 启用shrio授权注解拦截方式 -->
<aop:config proxy-target-class="true"></aop:config>
<bean class="org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor">
<property name="securityManager" ref="securityManager"/>
</bean>
<!-- 定时器 线程池-->
<bean id="executor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="10" />
<property name="maxPoolSize" value="100" />
<property name="queueCapacity" value="500" />
</bean>
<!-- 另外一个邮件定时任务 -->
<bean id="bizObject" class="com.voole.smarteam.web.background.controller.MailController"/>
<bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="bizObject" />
<property name="targetMethod" value="sendMail" />
</bean>
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="jobDetail" />
<property name="cronExpression" value="10 0/1 * * * ?" />
</bean>
<!-- 汇报考勤统计定时提醒邮件(本次总结的定时任务配置) -->
<bean id="noteMailController" class="com.voole.smarteam.web.background.controller.NoteMailController"/>
<bean id="noteMailDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="noteMailController" />
<property name="targetMethod" value="sendNoteMail" />
</bean>
<bean id="noteMailCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="noteMailDetail" />
<property name="cronExpression" value="0 0 9 ? * MON,FRI" /> <!-- 每周一和周五的九点执行 -->
</bean>
<!-- -->
<bean id="taskTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
<property name="jobDetail" ref="jobDetail" />
<property name="startDelay" value="10000" />
<property name="repeatInterval" value="60000" />
</bean>
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger" />
<ref bean="noteMailCronTrigger" />
</list>
</property>
<property name="taskExecutor" ref="executor" />
</bean>
</beans>
总结:
主要是用要spring的定时邮件功能。
参考: