您的位置:首页 > 数据库 > Mongodb

邮件自动化服务开发:定时拉取MongoDB数据提供给运营人员

2017-09-01 15:55 561 查看
邮件自动化服务开发:定时拉取MongoDB数据提供给运营人员

邮件自动化服务开发:定时拉取MongoDB数据提供给运营人员,一路踩着坑,用了三天时间

 


​有很多是补充上一篇文章的内容。我觉得直接粘贴代码一下,然后在分析比较好。

那就一步一步来吧。总体加在一起代码量也还好,1000行以内,老规矩,从main方法进来:

package com.start;

import java.io.BufferedInputStream;

import java.io.File;

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.text.SimpleDateFormat;

import java.util.ArrayList;

import java.util.Date;

import java.util.HashMap;

import java.util.Iterator;

import java.util.List;

import java.util.Map;

import java.util.Properties;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;

import org.apache.poi.ss.usermodel.Cell;

import org.apache.poi.ss.usermodel.Row;

import org.apache.poi.ss.usermodel.Sheet;

import org.apache.poi.ss.usermodel.Workbook;

import com.model.PropertyVO;

import com.model.ShowVO;

import com.mongodb.BasicDBList;

import com.mongodb.MongoClient;

import com.test.MailSender;

import com.start.DoThis2;

public class SendEmail {

public static void main(String[] args) {

System.out.println("-------1.获取配置文件,获取配置实体类------------");

//1.获取配置文件,获取配置实体类

String propertisPath = "/opt/mongo/program/main.properties";

//String
propertisPath = "F:\\1\\main.properties";

Map propertisPathmap = doProperties(propertisPath);

PropertyVO vo = getVo(propertisPathmap);

System.out.println("---------2.创建连接------------");

try {

System.out.println("---------3.获得时间autoDate------------");

//3.获得时间autoDate

String auto = autoDate(vo.getDateTime());

vo.setDateTime(auto);

System.out.println("---------4.得到葛远所要数据------------");

//4.得到葛远所要数据

List voList = getData(vo);

System.out.println("---------5.生成excel文件------------");

//5.生成excel文件

List workbookList=createlist(voList,vo);  

            try {  

                writeToXls(workbookList,vo);  

            } catch (Exception e) {  

                e.printStackTrace();  

            }  

System.out.println("---------6.发送邮件给殷园园------------");

            //6.发送邮件给葛远

            sendEmail(vo,auto);

} catch (Exception e) {

e.printStackTrace();

String s = e.getMessage();

System.out.println(s);



}

/**

 * 创建mongo客户端

 * @param url

 * @param port

 * @return

 */

public static MongoClient getClient(PropertyVO vo) {

String mongo_url = vo.getMongoUrl();

Integer mongo_port = vo.getMongoPort();

MongoClient client = null;

try {

client = new MongoClient(mongo_url, mongo_port);

} catch (Exception e) {

}

return client;

}

/**

 * 给入db配置,获取数据list

 * @param db

 * @param autoDate

 * @return

 */

public static List getData(PropertyVO proVO) {

//查询1,拿到所有id

BasicDBList queryList = new BasicDBList();//BasicDBList对象,用于像标准sql的in( 元素1,元素2,元素3)

DoThis2 doThis2 = new DoThis2();

queryList = doThis2.start(proVO);

//查询2,拿到所有新增数

List voList = new ArrayList();// 放入vo的list

DoThis3 doThis3 = new DoThis3();

voList= doThis3.start(queryList,proVO);

/**

 * 到了这里,我们for循环,处理里面数据,拼装成最后需要用的数据

 * 这里代码很容易理解

 */

for (int i = 0; i < voList.size(); i++) {

ShowVO vo = (ShowVO) voList.get(i);

Long lowerNewAdd = vo.getLowerNewAdd();

Long upperNewAdd = vo.getUpperNewAdd();

if (lowerNewAdd == null) {

lowerNewAdd = (long) 0;

}

if (upperNewAdd == null) {

upperNewAdd = (long) 0;

}

Long total = lowerNewAdd + upperNewAdd;

if (total == 0) {

vo.setTotal(total);// 总数

vo.setLowerRate("--");// 低于的比率

vo.setUpperRate("--");// 高于的比率

} else {

double lowerRateLong = (double) lowerNewAdd / (double) total* 100;

double upperRateLong = (double) upperNewAdd / (double) total* 100;

String lower = String.valueOf(lowerRateLong).toString();

String upper = String.valueOf(upperRateLong).toString();

//解决报错:java.lang.StringIndexOutOfBoundsException: String index out of range:
5

if(lower.length()<5) {

lower+="00";

}

if(upper.length()<5) {

upper+="00";

}

String lowerRate = lower.substring(0, 5) + "%";

String upperRate = upper.substring(0, 5) + "%";

vo.setTotal(total);// 总数

vo.setLowerRate(lowerRate);// 低于的比率

vo.setUpperRate(upperRate);// 高于的比率

System.out.println(vo.getCh()+"          "+lowerRate+"         "+upperRate+"
            "+total);

}

}

//测试打印

System.out.println(voList.size());

return voList;

}

/**

 * 创建生成的excel的list

 * @param voList

 * @return

 */

public static List createlist(List voList,PropertyVO provo){  

List resultList=new ArrayList();  

List biaotou=new ArrayList();         

        biaotou.add("渠道");  

        biaotou.add(">="+provo.getVersionValue());  

        biaotou.add("<"+provo.getVersionValue()); 

        biaotou.add("新增总数"); 

        resultList.add(biaotou);  

        //for循环拿出传入list数据

        for(int i=0;i<voList.size();i++) {

ShowVO vo = new ShowVO();

vo = (ShowVO) voList.get(i);

List neirong=new ArrayList(); 

            neirong.add(vo.getCh());  

            neirong.add(vo.getUpperRate()); 

            neirong.add(vo.getLowerRate()); 

            neirong.add(vo.getTotal()); 

            resultList.add(neirong);  

        }

        return resultList;  

    } 

/**

 * 创建excel邮件,并且保存下来

 * @param resultList

 * @throws Exception

 */

public static void writeToXls(List resultList,PropertyVO propertyVO)throws Exception{
 

        //创建一个EXCEL  

        Workbook wb = new HSSFWorkbook();  

        //创建一个SHEET  

        Sheet sheet1 = wb.createSheet("报表1");

        if(resultList!=null){ 

        for (int i = 0; i < resultList.size(); i++) {

            //创建一行  

            Row row = sheet1.createRow(i);  

            List rowList=(List)resultList.get(i);  

            //从一行中一列一列装入

            for (int j = 0; j < rowList.size(); j++) {

                Cell cell = row.createCell(j);

                //解决total为Long类型问题

                String cellLiString=String.valueOf(rowList.get(j));

                cell.setCellValue(cellLiString );

            }  

        }  

        }  

        //保存excel文件

        FileOutputStream fileOut = new FileOutputStream(propertyVO.getUrlPath()+"分版本-"+propertyVO.getDateTime()+".xls");
 

        wb.write(fileOut);  

        fileOut.close();  

    } 

/**

 * 发送邮件代码段

 */

public static void sendEmail (PropertyVO propertyVO,String auto) {

String to = propertyVO.getEmailSend();;//发给自己,就是预览信息了

String cc = propertyVO.getEmailSender();

//String
subject=propertyVO.getEmailSubject();//标题

//String
body =propertyVO.getEmailBody();

String subject="外推渠道分版本数据:"+auto;//标题

String body ="附件为外推渠道分版本数据,请知悉。";

File[] adjuctPath = new File[1];

File file=new File(propertyVO.getUrlPath()+"分版本-"+propertyVO.getDateTime()+".xls");//将刚才生成pdf的url拿过来

adjuctPath[0]=file;

String hostName = propertyVO.getEmailHostName(); // 服务器

String userName = propertyVO.getEmailUserName();

String passWd = propertyVO.getEmailPassWd();

String mailFrom = propertyVO.getEmailMailFrom();

MailSender email = new MailSender(); 

try {

email.sendMail(to, cc, subject, body, adjuctPath, hostName, userName, passWd,
mailFrom);

}catch(Exception e) {

e.printStackTrace();

}

}

/**

 * 自动获取昨天的数据,如果没有给nowDate的话,给的是auto

 * @param nowDate

 * @return

 */

public static String autoDate(String nowDate) {

if(nowDate!=null && !" ".equals(nowDate)) {

if(nowDate.equals("auto")) {

Date date = new Date(new Date().getTime()-24*60*60*1000);

SimpleDateFormat  sdf  =  new SimpleDateFormat("yyyy-MM-dd");

String time = sdf.format(date);

return time;

}else {

//SimpleDateFormat
 sdf  =  new SimpleDateFormat("yyyy-MM-dd");

//String
time = sdf.format(nowDate);

return nowDate;

}

}else {

Date date = new Date(new Date().getTime()-24*60*60*1000);

SimpleDateFormat  sdf  =  new SimpleDateFormat("yyyy-MM-dd");

String time = sdf.format(date);

return time;

}

}

/**

 * 给方法用来读取配置文件

 * @param propertisUrl

 * @return

 */

public static Map doProperties(String propertisUrl){

Properties prop = new Properties();     

Map map= new HashMap();

        try{

            //读取属性文件a.properties

//            InputStream in = new BufferedInputStream (new FileInputStream("F:\\python\\pachong.properties"));

InputStream in = new BufferedInputStream (new FileInputStream(propertisUrl));

prop.load(in);     ///加载属性列表

           Iterator<String> it=prop.stringPropertyNames().iterator();

            while(it.hasNext()){

                String key=it.next();

System.out.println(key+":"+prop.getProperty(key));

                map.put(key, prop.getProperty(key));

            }

            in.close();

        }

        catch(Exception e){

            System.out.println(e);

        }

        return map;

}

/**

 * 从配置文件中,拿到配置实体类。

 * @param propertisPathmap

 * @return

 */

public static PropertyVO getVo(Map propertisPathmap) {

String mongo_url = (String) propertisPathmap.get("mongo_url");

//Integer
mongo_port = Integer.parseInt((String) propertisPathmap.get("mongo_port"));

String mongo_db = (String) propertisPathmap.get("mongo_db");

Integer version_value = Integer.parseInt((String) propertisPathmap.get("version_value"));

String date_time = (String) propertisPathmap.get("date_time");

String role_name = (String) propertisPathmap.get("role_name");

String url_path = (String) propertisPathmap.get("url_path");

String emailSend= (String) propertisPathmap.get("emial_send");

String emailSender= (String) propertisPathmap.get("email_sender");

String emailSubject= (String) propertisPathmap.get("email_subject");

String emailBody= (String) propertisPathmap.get("email_body");

String emailHostName= (String) propertisPathmap.get("email_hostName");

String emailUserName= (String) propertisPathmap.get("email_userName");

String emailPassWd= (String) propertisPathmap.get("email_passWd");

String emailMailFrom= (String) propertisPathmap.get("email_mailFrom");

PropertyVO vo = new PropertyVO();

vo.setMongoUrl(mongo_url);

//vo.setMongoPort(mongo_port);

vo.setMongoDb(mongo_db);

vo.setVersionValue(version_value);

vo.setDateTime(date_time);

vo.setRoleName(role_name);

vo.setUrlPath(url_path);

vo.setEmailSend(emailSend);

vo.setEmailSender(emailSender);

vo.setEmailSubject(emailSubject);

vo.setEmailBody(emailBody);

vo.setEmailHostName(emailHostName);

vo.setEmailUserName(emailUserName);

vo.setEmailPassWd(emailPassWd);

vo.setEmailMailFrom(emailMailFrom);

return vo;

}

}

​好的,其实注释很清楚了,总共5步走。实现起来也有很多坑,比方说我们测试服务器的mongo版本3.2,正式服务器上面的就是2.5的,差了太多了,所以用的MongoClient在测试服务器上玩的high但是到了正式服务器上就是垃圾,一塌糊涂了。要用DBClient这个类了,好了,

 

​使用配置文件,把配置文件给看一下吧。

 

​把配置文件读成map

 

​第二步,获取autoDate,把时间给,配置类的属性,这个理解吧

 

这是详细代码:

 

​ ​如果在配置文件里面写了某一天,就会去拉取某一天,如果写的是auto,则是默认就是做一天的。这个代码好理解吧,懂我意思吧。如果时间是空呢,就是昨天了。

第三个就是得到葛远所要数据了,我们把实体传入方法。

 

​进入getData方法呢,首先上来就做了两步,

 

​因为涉及到权限,所以,这里要我们要先用doThis2,来拿到所有的权限内的渠道,即,去渠道表里拿到b渠道组下的所有渠道。我们过去看看吧:

 

​标红处,即是,建立mongo连接,继而依据条件查询,在继而遍历结果集,装入list中,返回出去,这个list里面装的都是type为016的渠道,016是b渠道组的意思,那么,我们就获得了所有要查询的渠道了。具体的mongo操作,详见cms工程下的代码样例。

好的,跳出来,跳出循环我们来看一下,doThis3,

 

​可以看到,老规矩建立连接,继而使用dt条件,条件的String来自我们之前组装的PropertyVO的属性,由我们使用函数得到。可以注意此处使用了mongo语法,$in操作,其实类似的许多用法和函数都可以使用mongo的语法,当然这里我使用的是程序来做计算拼接。

好了,来看最后一个循环,因为这个循环比较复杂,涉及到许多算法逻辑,就直接粘贴代码了,说一下大概意义:

List voList = new ArrayList();// 放入vo的list

Map voMap = new HashMap();// 用于记录不同ch的map,其实就是使用map去重

// 遍历循环从结果集拼装数据

while (cur.hasNext()) {

DBObject doc = cur.next();

String ch = (String) doc.get("ch");// 渠道

Integer version = null;

try {//处理异常,如果是“ ”,会导致无法装箱

String pass = (String) doc.get("m_cd");

if (pass == null || " ".equals(pass)) {

continue;

} else {

version = Integer.parseInt(pass);// 版本

}

} catch (Exception e) {

String s = e.getMessage();

e.printStackTrace();

System.out.println(s);

}

Long newAdd = (Long) doc.get("cnt_n");// 新增数

/**

 * 该方法用于处理,将所有数据归类,如果第一次进来,则我们添加该map,key为ch,value为showVo实体类

 */

if (voMap.containsKey(ch)) {// 如果渠道已经在里面了

ShowVO vo = (ShowVO) voMap.get(ch);

if (version >= proVo.getVersionValue()) {// 如果大于143,将新增数累加

Long countUpper = vo.getUpperNewAdd();

if(countUpper == null) {//解决报错空指针的问题

countUpper = (long) 0;

}

Long count = countUpper + newAdd;

vo.setUpperNewAdd(count);

} else {// 如果小于143,将新增数累加

Long countUpper =vo.getLowerNewAdd() ;

if(countUpper == null) {//解决报错空指针的问题

countUpper = (long) 0;

}

Long count =countUpper + newAdd;

vo.setLowerNewAdd(count);

}

} else {// 如果这条记录不再这个map里面

ShowVO vo = new ShowVO();

if (version >= proVo.getVersionValue()) {// 如果大于143,将新增数设置

vo.setUpperNewAdd(newAdd);

vo.setCh(ch);

voMap.put(ch, vo);

} else {// 如果小于143,将新增数设置

vo.setLowerNewAdd(newAdd);

vo.setCh(ch);

voMap.put(ch, vo);

}

}

}

逻辑就是,我们已经用条件查到了表user_channel_val_v数据集,然后把数据机里面的渠道,版本号,新增数拿到,当然做了为空处理,因为写的时候发现有的m_cd版本字段为空字符串,会报错无法被装箱成Integer。

接着,处理,将所有数据归类,如果第一次进来,则我们添加该map,key为ch,value为showVo实体类

渠道没有的话,就创建map,key为ch,value为ShowVO,这个vo稍后带看一下,别着急。这个算法里面做了,没有渠道创建,如果有这个渠道,区分是否大于143(需求需要,后期可能版本号需要改),如果有,那我们就进入key为ch的map,修改value里面的vo属性,这个方法我觉得比较精妙,想了很多方式,还是这个方式,使用对象最为优良。

来看一下ShowVO,只粘贴一部分哈

 

​然后我们来看doThis3最后的一个环节,将我们计算好的map里面所有values拿到,这个时候已经不需要map 的key了(当初使用map是为了聚合,hashMap的特性,key为唯一,可以hash到value,用来去重很不错),这里我们将所有的vo对象装入一个list里面返回出去了。

好了,这里看完了doThis3的操作,继续回到主的函数main里面引用的getData,还记得把。

 

​注意看,这里已经不是聚合了,这里是采用我们for循环,处理里面数据,拼装成最后需要用的数据 这里代码很容易理解,从list里面拿到刚才我们所有的唯一的vo对象,然后for循环,做了什么操作呢,在里面我们要算大于143或者小于143的比重,这个是需求的,还要累加。源数据里面只有各个版本的对应的新增数。我打比方说吧,渠道:a001       版本:123       新增数:12, 渠道:a002      版本:156     新增数:124, 渠道:a001       版本:123
      新增数:67,

那么我们就是做聚合,前面那篇文章带大家看过数据了,以前呢,需要运营人员手动的来找,然后手动算,现在由我们这边出了,我用程序算好,自动生成excel发送邮件。造福公司和人类,解放劳动力。

好了扯远了,继续来看,这里有几个坑,都被我已经在注释里面声明了,手下你是0,不能做枫木,所以,我们处理total为0,直接规避这个问题

再者,如果是0.0,将不能被切割subString方法(0,5),故而呢,在这里我们帮他加00,变成0.000,所以,这么写。

到此,getData函数全部分析完毕,然后返回

 

​好了,大功告成,数据也拿到了,现在就是要生成excel文件了,

 

​这里有两个方法,createlist(voList,vo); 

writeToXls(workbookList,vo);

 我们进去看看, createlist 方法,提供

 

​这个没毛病吧,拼接生成最后excel的方法,再进 writeToXls方法看一看

 

​这里创建行列,然后两层for循环,干出来行列之后,注意这里要解决解决total为Long类型问题,请注意使用String.valueOf(rowList.get(j));再然后,因为对文件操作并没有特殊要求,也不要求备份(因为大不了该配置重跑),所以这里,我们直接从vo属性拿到然后创建文件。

然后到了第五步,修成正果的一步了:

 

​来看一下最后一步的方法。sendEmail(vo,auto);

 

​没什么好说的吧,这个使用了emai发送类的api,点进去看一下

email.sendMail(to, cc, subject, body, adjuctPath, hostName, userName, passWd, mailFrom);

 

​上面这个api发送邮件呢,创建smtp服务,其实也有坑的,当然了,网上也有,但是大部分是不能用的,要么就是邮件服务一定记得开,还有,不要用qq的,新浪的容易出现报错。这里也就不详细说明了。

当然整个开发过程还是有特别多的坑的,那么,总的来说,现在是把坑克服了,比方说,后面重构了,代码,因为mongoClient对老版本没用,换了个api重新写了代码,再有就是上线之后乱码问题啥的,还有么就是堡垒机恶心到我了之类的。

最后说一下上线。

 

​打jar到服务器上,然后,配置文件也放过来,写到crontab里面

 

​crontab会在设置后的两分钟执行,记住了。还有如果出现jar不执行应该是profile,记得source一下,几乎所有的问题最终来自于环境变量,这个我们之前说过。在就是,记得加这个,不然会出现乱码,最后出一下结果邮件:

 

运营那边已经使用了,但是他忘了谢谢我,哼,很生气。



 

​欧了,完工。继续干活了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐