提交 09d6d929 编写于 作者: 智布道's avatar 智布道 👁

💚 - 新增:

              	- 图片转存功能支持CSDN
              	- “文章搬运工”可选的停止方式:
              		- 默认:不做限制,抓取所有匹配到的文章,慎用
                      - 持续时间:按照爬虫运行的时间,理想状态时1s抓取一条,受实际网速影响
                      - 链接条数:按照指定的条数抓取,满足条数后程序自动停止
              	- 日志管理,记录用户的操作日志
              - 修改:
              	- 建站日期提到配置文件中,可手动配置。通过buildWebsiteDate指定建站起始日期,默认2018-01-01
              	- 文章管理页调整,去掉不重要的列,增加一键开关,使之更加便于管理
              	- 重构后台管理的首页,显示重要的信息:文章数、标签数等数量统计和文章分类统计、爬虫统计等
              	- 部分页面调优
              	- 文章分类页的排序
              - 删除:
              	- 删除CnblogModel等无用的测试实体类
              - 修复其他一些bug
上级 a475caf2
...@@ -65,7 +65,7 @@ public class PassportController { ...@@ -65,7 +65,7 @@ public class PassportController {
@BussinessLog("进入登录页面") @BussinessLog("进入登录页面")
@GetMapping("/login") @GetMapping("/login")
public ModelAndView login(Model model) { public ModelAndView login(Model model) {
model.addAttribute("enableKaptcha", config.getEnableKaptcha()); model.addAttribute("enableKaptcha", config.isEnableKaptcha());
return ResultUtil.view("/login"); return ResultUtil.view("/login");
} }
...@@ -79,7 +79,7 @@ public class PassportController { ...@@ -79,7 +79,7 @@ public class PassportController {
@PostMapping("/signin") @PostMapping("/signin")
@ResponseBody @ResponseBody
public ResponseVO submitLogin(String username, String password, boolean rememberMe, String kaptcha) { public ResponseVO submitLogin(String username, String password, boolean rememberMe, String kaptcha) {
if (config.getEnableKaptcha()) { if (config.isEnableKaptcha()) {
if (StringUtils.isEmpty(kaptcha) || !kaptcha.equals(SessionUtil.getKaptcha())) { if (StringUtils.isEmpty(kaptcha) || !kaptcha.equals(SessionUtil.getKaptcha())) {
return ResultUtil.error("验证码错误!"); return ResultUtil.error("验证码错误!");
} }
......
...@@ -33,6 +33,7 @@ import com.zyd.blog.business.annotation.BussinessLog; ...@@ -33,6 +33,7 @@ import com.zyd.blog.business.annotation.BussinessLog;
import com.zyd.blog.business.entity.Article; import com.zyd.blog.business.entity.Article;
import com.zyd.blog.business.service.BizArticleService; import com.zyd.blog.business.service.BizArticleService;
import com.zyd.blog.core.websocket.server.ZydWebsocketServer; import com.zyd.blog.core.websocket.server.ZydWebsocketServer;
import com.zyd.blog.spider.enums.ExitWayEnum;
import com.zyd.blog.util.ResultUtil; import com.zyd.blog.util.ResultUtil;
import org.apache.shiro.authz.annotation.RequiresAuthentication; import org.apache.shiro.authz.annotation.RequiresAuthentication;
import org.apache.shiro.authz.annotation.RequiresPermissions; import org.apache.shiro.authz.annotation.RequiresPermissions;
...@@ -98,21 +99,21 @@ public class RenderController { ...@@ -98,21 +99,21 @@ public class RenderController {
} }
@RequiresPermissions("article:publish") @RequiresPermissions("article:publish")
@BussinessLog(value = "发表文章页[html]") @BussinessLog(value = "进入发表文章页[html]")
@GetMapping("/article/publish") @GetMapping("/article/publish")
public ModelAndView publish() { public ModelAndView publish() {
return ResultUtil.view("article/publish"); return ResultUtil.view("article/publish");
} }
@RequiresPermissions("article:publish") @RequiresPermissions("article:publish")
@BussinessLog(value = "发表文章页[markdown]") @BussinessLog(value = "进入发表文章页[markdown]")
@GetMapping("/article/publishMd") @GetMapping("/article/publishMd")
public ModelAndView publishMd() { public ModelAndView publishMd() {
return ResultUtil.view("article/publish-md"); return ResultUtil.view("article/publish-md");
} }
@RequiresPermissions("article:publish") @RequiresPermissions("article:publish")
@BussinessLog(value = "修改文章页[id={1}]") @BussinessLog(value = "进入修改文章页[id={1}]")
@GetMapping("/article/update/{id}") @GetMapping("/article/update/{id}")
public ModelAndView edit(@PathVariable("id") Long id, Model model) { public ModelAndView edit(@PathVariable("id") Long id, Model model) {
model.addAttribute("id", id); model.addAttribute("id", id);
...@@ -179,27 +180,15 @@ public class RenderController { ...@@ -179,27 +180,15 @@ public class RenderController {
return ResultUtil.view("update/list"); return ResultUtil.view("update/list");
} }
@RequiresPermissions("plays")
@BussinessLog("进入歌单管理页")
@GetMapping("/plays")
public ModelAndView plays() {
return ResultUtil.view("play/list");
}
@RequiresPermissions("sysWebpage")
@BussinessLog("进入静态页面管理页")
@GetMapping("/sysWebpage")
public ModelAndView sysWebpage() {
return ResultUtil.view("sysWebpage/list");
}
@RequiresPermissions("icons") @RequiresPermissions("icons")
@BussinessLog(value = "进入icons页")
@GetMapping("/icons") @GetMapping("/icons")
public ModelAndView icons(Model model) { public ModelAndView icons(Model model) {
return ResultUtil.view("icons"); return ResultUtil.view("icons");
} }
@RequiresPermissions("shiro") @RequiresPermissions("shiro")
@BussinessLog(value = "进入shiro示例页")
@GetMapping("/shiro") @GetMapping("/shiro")
public ModelAndView shiro(Model model) { public ModelAndView shiro(Model model) {
return ResultUtil.view("shiro"); return ResultUtil.view("shiro");
...@@ -217,6 +206,7 @@ public class RenderController { ...@@ -217,6 +206,7 @@ public class RenderController {
@BussinessLog("进入搬运工页面") @BussinessLog("进入搬运工页面")
@GetMapping("/remover") @GetMapping("/remover")
public ModelAndView remover(Model model) { public ModelAndView remover(Model model) {
model.addAttribute("exitWayList", ExitWayEnum.values());
return ResultUtil.view("remover/list"); return ResultUtil.view("remover/list");
} }
} }
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
*/ */
package com.zyd.blog.controller; package com.zyd.blog.controller;
import com.zyd.blog.business.annotation.BussinessLog;
import com.zyd.blog.business.entity.Config; import com.zyd.blog.business.entity.Config;
import com.zyd.blog.business.enums.QiniuUploadType; import com.zyd.blog.business.enums.QiniuUploadType;
import com.zyd.blog.business.service.BizArticleService; import com.zyd.blog.business.service.BizArticleService;
...@@ -103,6 +104,7 @@ public class RestApiController { ...@@ -103,6 +104,7 @@ public class RestApiController {
*/ */
@RequiresPermissions("notice") @RequiresPermissions("notice")
@PostMapping("/notice") @PostMapping("/notice")
@BussinessLog("通过websocket向前台用户发送通知")
public ResponseVO notice(String msg) throws UnsupportedEncodingException { public ResponseVO notice(String msg) throws UnsupportedEncodingException {
WebSocketUtil.sendNotificationMsg(msg, websocketServer.getOnlineUsers()); WebSocketUtil.sendNotificationMsg(msg, websocketServer.getOnlineUsers());
return ResultUtil.success("消息发送成功", articleService.listMaterial()); return ResultUtil.success("消息发送成功", articleService.listMaterial());
......
...@@ -21,9 +21,9 @@ package com.zyd.blog.controller; ...@@ -21,9 +21,9 @@ package com.zyd.blog.controller;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import com.zyd.blog.business.annotation.BussinessLog;
import com.zyd.blog.business.entity.Article; import com.zyd.blog.business.entity.Article;
import com.zyd.blog.business.entity.Config; import com.zyd.blog.business.entity.Config;
import com.zyd.blog.business.enums.ArticleStatusEnum;
import com.zyd.blog.business.enums.BaiduPushTypeEnum; import com.zyd.blog.business.enums.BaiduPushTypeEnum;
import com.zyd.blog.business.enums.ResponseStatus; import com.zyd.blog.business.enums.ResponseStatus;
import com.zyd.blog.business.service.BizArticleService; import com.zyd.blog.business.service.BizArticleService;
...@@ -71,6 +71,7 @@ public class RestArticleController { ...@@ -71,6 +71,7 @@ public class RestArticleController {
@RequiresPermissions(value = {"article:batchDelete", "article:delete"}, logical = Logical.OR) @RequiresPermissions(value = {"article:batchDelete", "article:delete"}, logical = Logical.OR)
@PostMapping(value = "/remove") @PostMapping(value = "/remove")
@BussinessLog("删除文章[{1}]")
public ResponseVO remove(Long[] ids) { public ResponseVO remove(Long[] ids) {
if (null == ids) { if (null == ids) {
return ResultUtil.error(500, "请至少选择一条记录"); return ResultUtil.error(500, "请至少选择一条记录");
...@@ -83,12 +84,14 @@ public class RestArticleController { ...@@ -83,12 +84,14 @@ public class RestArticleController {
@RequiresPermissions("article:get") @RequiresPermissions("article:get")
@PostMapping("/get/{id}") @PostMapping("/get/{id}")
@BussinessLog("获取文章[{1}]详情")
public ResponseVO get(@PathVariable Long id) { public ResponseVO get(@PathVariable Long id) {
return ResultUtil.success(null, this.articleService.getByPrimaryKey(id)); return ResultUtil.success(null, this.articleService.getByPrimaryKey(id));
} }
@RequiresPermissions(value = {"article:edit", "article:publish"}, logical = Logical.OR) @RequiresPermissions(value = {"article:edit", "article:publish"}, logical = Logical.OR)
@PostMapping("/save") @PostMapping("/save")
@BussinessLog("发布文章")
public ResponseVO edit(Article article, Long[] tags, MultipartFile file) { public ResponseVO edit(Article article, Long[] tags, MultipartFile file) {
articleService.publish(article, tags, file); articleService.publish(article, tags, file);
return ResultUtil.success(ResponseStatus.SUCCESS); return ResultUtil.success(ResponseStatus.SUCCESS);
...@@ -96,6 +99,7 @@ public class RestArticleController { ...@@ -96,6 +99,7 @@ public class RestArticleController {
@RequiresPermissions(value = {"article:top", "article:recommend"}, logical = Logical.OR) @RequiresPermissions(value = {"article:top", "article:recommend"}, logical = Logical.OR)
@PostMapping("/update/{type}") @PostMapping("/update/{type}")
@BussinessLog("修改文章[{2}]的状态[{1}]")
public ResponseVO update(@PathVariable("type") String type, Long id) { public ResponseVO update(@PathVariable("type") String type, Long id) {
articleService.updateTopOrRecommendedById(type, id); articleService.updateTopOrRecommendedById(type, id);
return ResultUtil.success(ResponseStatus.SUCCESS); return ResultUtil.success(ResponseStatus.SUCCESS);
...@@ -103,6 +107,7 @@ public class RestArticleController { ...@@ -103,6 +107,7 @@ public class RestArticleController {
@RequiresPermissions(value = {"article:batchPush", "article:push"}, logical = Logical.OR) @RequiresPermissions(value = {"article:batchPush", "article:push"}, logical = Logical.OR)
@PostMapping(value = "/pushToBaidu/{type}") @PostMapping(value = "/pushToBaidu/{type}")
@BussinessLog("推送文章[{2}]到百度站长平台")
public ResponseVO pushToBaidu(@PathVariable("type") BaiduPushTypeEnum type, Long[] ids) { public ResponseVO pushToBaidu(@PathVariable("type") BaiduPushTypeEnum type, Long[] ids) {
if (null == ids) { if (null == ids) {
return ResultUtil.error(500, "请至少选择一条记录"); return ResultUtil.error(500, "请至少选择一条记录");
...@@ -139,6 +144,7 @@ public class RestArticleController { ...@@ -139,6 +144,7 @@ public class RestArticleController {
@RequiresPermissions(value = {"article:publish"}, logical = Logical.OR) @RequiresPermissions(value = {"article:publish"}, logical = Logical.OR)
@PostMapping(value = "/batchPublish") @PostMapping(value = "/batchPublish")
@BussinessLog("批量推送文章[{1}]到百度站长平台")
public ResponseVO batchPublish(Long[] ids) { public ResponseVO batchPublish(Long[] ids) {
if (null == ids) { if (null == ids) {
return ResultUtil.error(500, "请至少选择一条记录"); return ResultUtil.error(500, "请至少选择一条记录");
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
package com.zyd.blog.controller; package com.zyd.blog.controller;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import com.zyd.blog.business.annotation.BussinessLog;
import com.zyd.blog.business.entity.Comment; import com.zyd.blog.business.entity.Comment;
import com.zyd.blog.business.enums.ResponseStatus; import com.zyd.blog.business.enums.ResponseStatus;
import com.zyd.blog.business.enums.TemplateKeyEnum; import com.zyd.blog.business.enums.TemplateKeyEnum;
...@@ -66,6 +67,7 @@ public class RestCommentController { ...@@ -66,6 +67,7 @@ public class RestCommentController {
@RequiresPermissions("comment:reply") @RequiresPermissions("comment:reply")
@PostMapping(value = "/reply") @PostMapping(value = "/reply")
@BussinessLog("回复评论")
public ResponseVO reply(Comment comment) { public ResponseVO reply(Comment comment) {
try { try {
commentService.commentForAdmin(comment); commentService.commentForAdmin(comment);
...@@ -77,6 +79,7 @@ public class RestCommentController { ...@@ -77,6 +79,7 @@ public class RestCommentController {
@RequiresPermissions(value = {"comment:batchDelete", "comment:delete"}, logical = Logical.OR) @RequiresPermissions(value = {"comment:batchDelete", "comment:delete"}, logical = Logical.OR)
@PostMapping(value = "/remove") @PostMapping(value = "/remove")
@BussinessLog("删除评论[{1}]")
public ResponseVO remove(Long[] ids) { public ResponseVO remove(Long[] ids) {
if (null == ids) { if (null == ids) {
return ResultUtil.error(500, "请至少选择一条记录"); return ResultUtil.error(500, "请至少选择一条记录");
...@@ -89,12 +92,14 @@ public class RestCommentController { ...@@ -89,12 +92,14 @@ public class RestCommentController {
@RequiresPermissions("comments") @RequiresPermissions("comments")
@PostMapping("/get/{id}") @PostMapping("/get/{id}")
@BussinessLog("获取评论[{1}]详情")
public ResponseVO get(@PathVariable Long id) { public ResponseVO get(@PathVariable Long id) {
return ResultUtil.success(null, this.commentService.getByPrimaryKey(id)); return ResultUtil.success(null, this.commentService.getByPrimaryKey(id));
} }
@RequiresPermissions("comments") @RequiresPermissions("comments")
@PostMapping("/edit") @PostMapping("/edit")
@BussinessLog("编辑评论")
public ResponseVO edit(Comment comment) { public ResponseVO edit(Comment comment) {
try { try {
commentService.updateSelective(comment); commentService.updateSelective(comment);
...@@ -105,14 +110,9 @@ public class RestCommentController { ...@@ -105,14 +110,9 @@ public class RestCommentController {
return ResultUtil.success(ResponseStatus.SUCCESS); return ResultUtil.success(ResponseStatus.SUCCESS);
} }
/**
* 审核
*
* @param comment
* @return
*/
@RequiresPermissions("comment:audit") @RequiresPermissions("comment:audit")
@PostMapping("/audit") @PostMapping("/audit")
@BussinessLog("审核评论")
public ResponseVO audit(Comment comment, String contentText, Boolean sendEmail) { public ResponseVO audit(Comment comment, String contentText, Boolean sendEmail) {
try { try {
commentService.updateSelective(comment); commentService.updateSelective(comment);
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
*/ */
package com.zyd.blog.controller; package com.zyd.blog.controller;
import com.zyd.blog.business.annotation.BussinessLog;
import com.zyd.blog.business.entity.Config; import com.zyd.blog.business.entity.Config;
import com.zyd.blog.business.enums.QiniuUploadType; import com.zyd.blog.business.enums.QiniuUploadType;
import com.zyd.blog.business.service.SysConfigService; import com.zyd.blog.business.service.SysConfigService;
...@@ -56,6 +57,7 @@ public class RestConfigController { ...@@ -56,6 +57,7 @@ public class RestConfigController {
@RequiresRoles("role:root") @RequiresRoles("role:root")
@PostMapping("/edit") @PostMapping("/edit")
@BussinessLog("修改系统配置")
public ResponseVO edit(Config config, public ResponseVO edit(Config config,
@RequestParam(required = false) MultipartFile wxPraiseCodeFile, @RequestParam(required = false) MultipartFile wxPraiseCodeFile,
@RequestParam(required = false) MultipartFile zfbPraiseCodeFile) { @RequestParam(required = false) MultipartFile zfbPraiseCodeFile) {
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
package com.zyd.blog.controller; package com.zyd.blog.controller;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import com.zyd.blog.business.annotation.BussinessLog;
import com.zyd.blog.business.entity.Link; import com.zyd.blog.business.entity.Link;
import com.zyd.blog.business.enums.LinkSourceEnum; import com.zyd.blog.business.enums.LinkSourceEnum;
import com.zyd.blog.business.enums.ResponseStatus; import com.zyd.blog.business.enums.ResponseStatus;
...@@ -64,6 +65,7 @@ public class RestLinkController { ...@@ -64,6 +65,7 @@ public class RestLinkController {
@RequiresPermissions("link:add") @RequiresPermissions("link:add")
@PostMapping(value = "/add") @PostMapping(value = "/add")
@BussinessLog("添加友情链接")
public ResponseVO add(Link link) { public ResponseVO add(Link link) {
link.setSource(LinkSourceEnum.ADMIN); link.setSource(LinkSourceEnum.ADMIN);
linkService.insert(link); linkService.insert(link);
...@@ -73,6 +75,7 @@ public class RestLinkController { ...@@ -73,6 +75,7 @@ public class RestLinkController {
@RequiresPermissions(value = {"link:batchDelete", "link:delete"}, logical = Logical.OR) @RequiresPermissions(value = {"link:batchDelete", "link:delete"}, logical = Logical.OR)
@PostMapping(value = "/remove") @PostMapping(value = "/remove")
@BussinessLog("删除友情链接")
public ResponseVO remove(Long[] ids) { public ResponseVO remove(Long[] ids) {
if (null == ids) { if (null == ids) {
return ResultUtil.error(500, "请至少选择一条记录"); return ResultUtil.error(500, "请至少选择一条记录");
...@@ -85,12 +88,14 @@ public class RestLinkController { ...@@ -85,12 +88,14 @@ public class RestLinkController {
@RequiresPermissions("link:get") @RequiresPermissions("link:get")
@PostMapping("/get/{id}") @PostMapping("/get/{id}")
@BussinessLog("获取友情链接详情")
public ResponseVO get(@PathVariable Long id) { public ResponseVO get(@PathVariable Long id) {
return ResultUtil.success(null, this.linkService.getByPrimaryKey(id)); return ResultUtil.success(null, this.linkService.getByPrimaryKey(id));
} }
@RequiresPermissions("link:edit") @RequiresPermissions("link:edit")
@PostMapping("/edit") @PostMapping("/edit")
@BussinessLog("编辑友情链接")
public ResponseVO edit(Link link) { public ResponseVO edit(Link link) {
try { try {
linkService.updateSelective(link); linkService.updateSelective(link);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
package com.zyd.blog.controller; package com.zyd.blog.controller;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import com.zyd.blog.business.annotation.BussinessLog;
import com.zyd.blog.business.entity.Notice; import com.zyd.blog.business.entity.Notice;
import com.zyd.blog.business.entity.User; import com.zyd.blog.business.entity.User;
import com.zyd.blog.business.enums.NoticeStatusEnum; import com.zyd.blog.business.enums.NoticeStatusEnum;
...@@ -62,6 +63,7 @@ public class RestNoticeController { ...@@ -62,6 +63,7 @@ public class RestNoticeController {
@RequiresPermissions("notice:add") @RequiresPermissions("notice:add")
@PostMapping(value = "/add") @PostMapping(value = "/add")
@BussinessLog("添加公告通知")
public ResponseVO add(Notice notice) { public ResponseVO add(Notice notice) {
User user = SessionUtil.getUser(); User user = SessionUtil.getUser();
if (null != user) { if (null != user) {
...@@ -73,6 +75,7 @@ public class RestNoticeController { ...@@ -73,6 +75,7 @@ public class RestNoticeController {
@RequiresPermissions(value = {"notice:batchDelete", "notice:delete"}, logical = Logical.OR) @RequiresPermissions(value = {"notice:batchDelete", "notice:delete"}, logical = Logical.OR)
@PostMapping(value = "/remove") @PostMapping(value = "/remove")
@BussinessLog("删除公告通知")
public ResponseVO remove(Long[] ids) { public ResponseVO remove(Long[] ids) {
if (null == ids) { if (null == ids) {
return ResultUtil.error(500, "请至少选择一条记录"); return ResultUtil.error(500, "请至少选择一条记录");
...@@ -85,12 +88,14 @@ public class RestNoticeController { ...@@ -85,12 +88,14 @@ public class RestNoticeController {
@RequiresPermissions("notice:get") @RequiresPermissions("notice:get")
@PostMapping("/get/{id}") @PostMapping("/get/{id}")
@BussinessLog("获取公告通知详情")
public ResponseVO get(@PathVariable Long id) { public ResponseVO get(@PathVariable Long id) {
return ResultUtil.success(null, this.noticeService.getByPrimaryKey(id)); return ResultUtil.success(null, this.noticeService.getByPrimaryKey(id));
} }
@RequiresPermissions("notice:edit") @RequiresPermissions("notice:edit")
@PostMapping("/edit") @PostMapping("/edit")
@BussinessLog("编辑公告通知")
public ResponseVO edit(Notice notice) { public ResponseVO edit(Notice notice) {
try { try {
noticeService.updateSelective(notice); noticeService.updateSelective(notice);
...@@ -103,6 +108,7 @@ public class RestNoticeController { ...@@ -103,6 +108,7 @@ public class RestNoticeController {
@RequiresPermissions("notice:release") @RequiresPermissions("notice:release")
@PostMapping("/release/{id}") @PostMapping("/release/{id}")
@BussinessLog("发布公告通知")
public ResponseVO release(@PathVariable Long id) { public ResponseVO release(@PathVariable Long id) {
try { try {
Notice notice = new Notice(); Notice notice = new Notice();
...@@ -118,6 +124,7 @@ public class RestNoticeController { ...@@ -118,6 +124,7 @@ public class RestNoticeController {
@RequiresPermissions("notice:withdraw") @RequiresPermissions("notice:withdraw")
@PostMapping("/withdraw/{id}") @PostMapping("/withdraw/{id}")
@BussinessLog("撤回公告通知")
public ResponseVO withdraw(@PathVariable Long id) { public ResponseVO withdraw(@PathVariable Long id) {
try { try {
Notice notice = new Notice(); Notice notice = new Notice();
......
...@@ -20,8 +20,13 @@ ...@@ -20,8 +20,13 @@
package com.zyd.blog.controller; package com.zyd.blog.controller;
import com.zyd.blog.business.annotation.BussinessLog;
import com.zyd.blog.business.consts.DateConst;
import com.zyd.blog.business.service.RemoverService; import com.zyd.blog.business.service.RemoverService;
import com.zyd.blog.framework.object.ResponseVO;
import com.zyd.blog.spider.model.BaseModel; import com.zyd.blog.spider.model.BaseModel;
import com.zyd.blog.util.DateUtil;
import com.zyd.blog.util.ResultUtil;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
...@@ -30,6 +35,7 @@ import org.springframework.web.bind.annotation.RestController; ...@@ -30,6 +35,7 @@ import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.util.Date;
/** /**
* Remover:搬运工(英语渣渣,实在想不出好玩的名字了) * Remover:搬运工(英语渣渣,实在想不出好玩的名字了)
...@@ -49,8 +55,21 @@ public class RestRemoverController { ...@@ -49,8 +55,21 @@ public class RestRemoverController {
@PostMapping("/run") @PostMapping("/run")
@ResponseBody @ResponseBody
@BussinessLog("运行文章搬运工")
public void run(Long typeId, BaseModel model, HttpServletResponse response) throws IOException, InterruptedException { public void run(Long typeId, BaseModel model, HttpServletResponse response) throws IOException, InterruptedException {
removerService.run(typeId, model, response.getWriter()); removerService.run(typeId, model, response.getWriter());
} }
@PostMapping("/stop")
@ResponseBody
@BussinessLog("停止文章搬运工")
public ResponseVO stop() {
try {
removerService.stop();
} catch (Exception e) {
return ResultUtil.error(e.getMessage());
}
return ResultUtil.success("程序已停止运行,当前时间 " + DateUtil.date2Str(new Date(), DateConst.YYYY_MM_DD_HH_MM_SS_EN));
}
} }
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
package com.zyd.blog.controller; package com.zyd.blog.controller;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import com.zyd.blog.business.annotation.BussinessLog;
import com.zyd.blog.business.entity.Resources; import com.zyd.blog.business.entity.Resources;
import com.zyd.blog.business.enums.ResponseStatus; import com.zyd.blog.business.enums.ResponseStatus;
import com.zyd.blog.business.service.SysResourcesService; import com.zyd.blog.business.service.SysResourcesService;
...@@ -71,6 +72,7 @@ public class RestResourcesController { ...@@ -71,6 +72,7 @@ public class RestResourcesController {
@RequiresPermissions("resource:add") @RequiresPermissions("resource:add")
@PostMapping(value = "/add") @PostMapping(value = "/add")
@BussinessLog("添加资源")
public ResponseVO add(Resources resources) { public ResponseVO add(Resources resources) {
resourcesService.insert(resources); resourcesService.insert(resources);
//更新权限 //更新权限
...@@ -80,6 +82,7 @@ public class RestResourcesController { ...@@ -80,6 +82,7 @@ public class RestResourcesController {
@RequiresPermissions(value = {"resource:batchDelete", "resource:delete"}, logical = Logical.OR) @RequiresPermissions(value = {"resource:batchDelete", "resource:delete"}, logical = Logical.OR)
@PostMapping(value = "/remove") @PostMapping(value = "/remove")
@BussinessLog("删除资源")
public ResponseVO remove(Long[] ids) { public ResponseVO remove(Long[] ids) {
if (null == ids) { if (null == ids) {
return ResultUtil.error(500, "请至少选择一条记录"); return ResultUtil.error(500, "请至少选择一条记录");
...@@ -95,12 +98,14 @@ public class RestResourcesController { ...@@ -95,12 +98,14 @@ public class RestResourcesController {
@RequiresPermissions("resource:get") @RequiresPermissions("resource:get")
@PostMapping("/get/{id}") @PostMapping("/get/{id}")
@BussinessLog("获取资源详情")
public ResponseVO get(@PathVariable Long id) { public ResponseVO get(@PathVariable Long id) {
return ResultUtil.success(null, this.resourcesService.getByPrimaryKey(id)); return ResultUtil.success(null, this.resourcesService.getByPrimaryKey(id));
} }
@RequiresPermissions("resource:edit") @RequiresPermissions("resource:edit")
@PostMapping("/edit") @PostMapping("/edit")
@BussinessLog("编辑资源")
public ResponseVO edit(Resources resources) { public ResponseVO edit(Resources resources) {
try { try {
resourcesService.updateSelective(resources); resourcesService.updateSelective(resources);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
package com.zyd.blog.controller; package com.zyd.blog.controller;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import com.zyd.blog.business.annotation.BussinessLog;
import com.zyd.blog.business.entity.Role; import com.zyd.blog.business.entity.Role;
import com.zyd.blog.business.enums.ResponseStatus; import com.zyd.blog.business.enums.ResponseStatus;
import com.zyd.blog.business.service.SysRoleResourcesService; import com.zyd.blog.business.service.SysRoleResourcesService;
...@@ -74,6 +75,7 @@ public class RestRoleController { ...@@ -74,6 +75,7 @@ public class RestRoleController {
@RequiresPermissions("role:allotResource") @RequiresPermissions("role:allotResource")
@PostMapping("/saveRoleResources") @PostMapping("/saveRoleResources")
@BussinessLog("分配角色拥有的资源")
public ResponseVO saveRoleResources(Long roleId, String resourcesId) { public ResponseVO saveRoleResources(Long roleId, String resourcesId) {
if (StringUtils.isEmpty(roleId)) { if (StringUtils.isEmpty(roleId)) {
return ResultUtil.error("error"); return ResultUtil.error("error");
...@@ -86,6 +88,7 @@ public class RestRoleController { ...@@ -86,6 +88,7 @@ public class RestRoleController {
@RequiresPermissions("role:add") @RequiresPermissions("role:add")
@PostMapping(value = "/add") @PostMapping(value = "/add")
@BussinessLog("添加角色")
public ResponseVO add(Role role) { public ResponseVO add(Role role) {
roleService.insert(role); roleService.insert(role);
return ResultUtil.success("成功"); return ResultUtil.success("成功");
...@@ -93,6 +96,7 @@ public class RestRoleController { ...@@ -93,6 +96,7 @@ public class RestRoleController {
@RequiresPermissions(value = {"role:batchDelete", "role:delete"}, logical = Logical.OR) @RequiresPermissions(value = {"role:batchDelete", "role:delete"}, logical = Logical.OR)
@PostMapping(value = "/remove") @PostMapping(value = "/remove")
@BussinessLog("删除角色")
public ResponseVO remove(Long[] ids) { public ResponseVO remove(Long[] ids) {
if (null == ids) { if (null == ids) {
return ResultUtil.error(500, "请至少选择一条记录"); return ResultUtil.error(500, "请至少选择一条记录");
...@@ -106,12 +110,14 @@ public class RestRoleController { ...@@ -106,12 +110,14 @@ public class RestRoleController {
@RequiresPermissions("role:get") @RequiresPermissions("role:get")
@PostMapping("/get/{id}") @PostMapping("/get/{id}")
@BussinessLog("获取角色详情")
public ResponseVO get(@PathVariable Long id) { public ResponseVO get(@PathVariable Long id) {
return ResultUtil.success(null, this.roleService.getByPrimaryKey(id)); return ResultUtil.success(null, this.roleService.getByPrimaryKey(id));
} }
@RequiresPermissions("role:edit") @RequiresPermissions("role:edit")
@PostMapping("/edit") @PostMapping("/edit")
@BussinessLog("编辑角色")
public ResponseVO edit(Role role) { public ResponseVO edit(Role role) {
try { try {
roleService.updateSelective(role); roleService.updateSelective(role);
......
package com.zyd.blog.controller;
import com.zyd.blog.business.service.BizStatisticsService;
import com.zyd.blog.business.service.SysConfigService;
import com.zyd.blog.framework.object.ResponseVO;
import com.zyd.blog.util.ResultUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @date 2018/5/22 16:47
* @since 1.0
*/
@Slf4j
@RestController
@RequestMapping("/statistics")
public class RestStatisticsController {
@Autowired
private SysConfigService configService;
@Autowired
private BizStatisticsService statisticsService;
@RequestMapping("/siteInfo")
public ResponseVO getSiteInfo(){
return ResultUtil.success("", configService.getSiteInfo());
}
@RequestMapping("/listSpider")
public ResponseVO listSpider(){
return ResultUtil.success("", statisticsService.listSpider(10));
}
@RequestMapping("/listType")
public ResponseVO listType(){
return ResultUtil.success("", statisticsService.listType(10));
}
}
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
package com.zyd.blog.controller; package com.zyd.blog.controller;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import com.zyd.blog.business.annotation.BussinessLog;
import com.zyd.blog.business.entity.Tags; import com.zyd.blog.business.entity.Tags;
import com.zyd.blog.business.enums.ResponseStatus; import com.zyd.blog.business.enums.ResponseStatus;
import com.zyd.blog.business.service.BizTagsService; import com.zyd.blog.business.service.BizTagsService;
...@@ -59,6 +60,7 @@ public class RestTagController { ...@@ -59,6 +60,7 @@ public class RestTagController {
@RequiresPermissions("tag:add") @RequiresPermissions("tag:add")
@PostMapping(value = "/add") @PostMapping(value = "/add")
@BussinessLog("添加标签")
public ResponseVO add(Tags tags) { public ResponseVO add(Tags tags) {
tagsService.insert(tags); tagsService.insert(tags);
return ResultUtil.success("标签添加成功!新标签 - " + tags.getName()); return ResultUtil.success("标签添加成功!新标签 - " + tags.getName());
...@@ -66,6 +68,7 @@ public class RestTagController { ...@@ -66,6 +68,7 @@ public class RestTagController {
@RequiresPermissions(value = {"tag:batchDelete", "tag:delete"}, logical = Logical.OR) @RequiresPermissions(value = {"tag:batchDelete", "tag:delete"}, logical = Logical.OR)
@PostMapping(value = "/remove") @PostMapping(value = "/remove")
@BussinessLog("删除标签")
public ResponseVO remove(Long[] ids) { public ResponseVO remove(Long[] ids) {
if (null == ids) { if (null == ids) {
return ResultUtil.error(500, "请至少选择一条记录"); return ResultUtil.error(500, "请至少选择一条记录");
...@@ -78,12 +81,14 @@ public class RestTagController { ...@@ -78,12 +81,14 @@ public class RestTagController {
@RequiresPermissions("tag:get") @RequiresPermissions("tag:get")
@PostMapping("/get/{id}") @PostMapping("/get/{id}")
@BussinessLog("获取标签详情")
public ResponseVO get(@PathVariable Long id) { public ResponseVO get(@PathVariable Long id) {
return ResultUtil.success(null, this.tagsService.getByPrimaryKey(id)); return ResultUtil.success(null, this.tagsService.getByPrimaryKey(id));
} }
@RequiresPermissions("tag:edit") @RequiresPermissions("tag:edit")
@PostMapping("/edit") @PostMapping("/edit")
@BussinessLog("编辑标签")
public ResponseVO edit(Tags tags) { public ResponseVO edit(Tags tags) {
try { try {
tagsService.updateSelective(tags); tagsService.updateSelective(tags);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
package com.zyd.blog.controller; package com.zyd.blog.controller;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import com.zyd.blog.business.annotation.BussinessLog;
import com.zyd.blog.business.entity.Template; import com.zyd.blog.business.entity.Template;
import com.zyd.blog.business.enums.ResponseStatus; import com.zyd.blog.business.enums.ResponseStatus;
import com.zyd.blog.business.service.SysTemplateService; import com.zyd.blog.business.service.SysTemplateService;
...@@ -59,6 +60,7 @@ public class RestTemplateController { ...@@ -59,6 +60,7 @@ public class RestTemplateController {
@RequiresPermissions("template:add") @RequiresPermissions("template:add")
@PostMapping(value = "/add") @PostMapping(value = "/add")
@BussinessLog("添加模板")
public ResponseVO add(Template template) { public ResponseVO add(Template template) {
templateService.insert(template); templateService.insert(template);
return ResultUtil.success("成功"); return ResultUtil.success("成功");
...@@ -66,6 +68,7 @@ public class RestTemplateController { ...@@ -66,6 +68,7 @@ public class RestTemplateController {
@RequiresPermissions(value = {"template:batchDelete", "template:delete"}, logical = Logical.OR) @RequiresPermissions(value = {"template:batchDelete", "template:delete"}, logical = Logical.OR)
@PostMapping(value = "/remove") @PostMapping(value = "/remove")
@BussinessLog("删除模板")
public ResponseVO remove(Long[] ids) { public ResponseVO remove(Long[] ids) {
if (null == ids) { if (null == ids) {
return ResultUtil.error(500, "请至少选择一条记录"); return ResultUtil.error(500, "请至少选择一条记录");
...@@ -78,12 +81,14 @@ public class RestTemplateController { ...@@ -78,12 +81,14 @@ public class RestTemplateController {
@RequiresPermissions("template:get") @RequiresPermissions("template:get")
@PostMapping("/get/{id}") @PostMapping("/get/{id}")
@BussinessLog("获取模板详情")
public ResponseVO get(@PathVariable Long id) { public ResponseVO get(@PathVariable Long id) {
return ResultUtil.success(null, this.templateService.getByPrimaryKey(id)); return ResultUtil.success(null, this.templateService.getByPrimaryKey(id));
} }
@RequiresPermissions("template:edit") @RequiresPermissions("template:edit")
@PostMapping("/edit") @PostMapping("/edit")
@BussinessLog("编辑模板")
public ResponseVO edit(Template template) { public ResponseVO edit(Template template) {
try { try {
templateService.updateSelective(template); templateService.updateSelective(template);
......
...@@ -19,8 +19,8 @@ ...@@ -19,8 +19,8 @@
*/ */
package com.zyd.blog.controller; package com.zyd.blog.controller;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import com.zyd.blog.business.annotation.BussinessLog;
import com.zyd.blog.business.entity.Type; import com.zyd.blog.business.entity.Type;
import com.zyd.blog.business.enums.ResponseStatus; import com.zyd.blog.business.enums.ResponseStatus;
import com.zyd.blog.business.service.BizTypeService; import com.zyd.blog.business.service.BizTypeService;
...@@ -60,6 +60,7 @@ public class RestTypeController { ...@@ -60,6 +60,7 @@ public class RestTypeController {
@RequiresPermissions("type:add") @RequiresPermissions("type:add")
@PostMapping(value = "/add") @PostMapping(value = "/add")
@BussinessLog("添加分类")
public ResponseVO add(Type type) { public ResponseVO add(Type type) {
typeService.insert(type); typeService.insert(type);
return ResultUtil.success("文章类型添加成功!新类型 - " + type.getName()); return ResultUtil.success("文章类型添加成功!新类型 - " + type.getName());
...@@ -67,6 +68,7 @@ public class RestTypeController { ...@@ -67,6 +68,7 @@ public class RestTypeController {
@RequiresPermissions(value = {"type:batchDelete", "type:delete"}, logical = Logical.OR) @RequiresPermissions(value = {"type:batchDelete", "type:delete"}, logical = Logical.OR)
@PostMapping(value = "/remove") @PostMapping(value = "/remove")
@BussinessLog("删除分类")
public ResponseVO remove(Long[] ids) { public ResponseVO remove(Long[] ids) {
if (null == ids) { if (null == ids) {
return ResultUtil.error(500, "请至少选择一条记录"); return ResultUtil.error(500, "请至少选择一条记录");
...@@ -79,12 +81,14 @@ public class RestTypeController { ...@@ -79,12 +81,14 @@ public class RestTypeController {
@RequiresPermissions("type:get") @RequiresPermissions("type:get")
@PostMapping("/get/{id}") @PostMapping("/get/{id}")
@BussinessLog("获取分类详情")
public ResponseVO get(@PathVariable Long id) { public ResponseVO get(@PathVariable Long id) {
return ResultUtil.success(null, this.typeService.getByPrimaryKey(id)); return ResultUtil.success(null, this.typeService.getByPrimaryKey(id));
} }
@RequiresPermissions("type:edit") @RequiresPermissions("type:edit")
@PostMapping("/edit") @PostMapping("/edit")
@BussinessLog("编辑分类")
public ResponseVO edit(Type type) { public ResponseVO edit(Type type) {
try { try {
typeService.updateSelective(type); typeService.updateSelective(type);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
package com.zyd.blog.controller; package com.zyd.blog.controller;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import com.zyd.blog.business.annotation.BussinessLog;
import com.zyd.blog.business.entity.UpdateRecorde; import com.zyd.blog.business.entity.UpdateRecorde;
import com.zyd.blog.business.enums.ResponseStatus; import com.zyd.blog.business.enums.ResponseStatus;
import com.zyd.blog.business.service.SysUpdateRecordeService; import com.zyd.blog.business.service.SysUpdateRecordeService;
...@@ -59,6 +60,7 @@ public class RestUpdateController { ...@@ -59,6 +60,7 @@ public class RestUpdateController {
@RequiresPermissions("updateLog:add") @RequiresPermissions("updateLog:add")
@PostMapping(value = "/add") @PostMapping(value = "/add")
@BussinessLog("添加更新日志")
public ResponseVO add(UpdateRecorde updateRecorde) { public ResponseVO add(UpdateRecorde updateRecorde) {
updateRecordeService.insert(updateRecorde); updateRecordeService.insert(updateRecorde);
return ResultUtil.success("成功"); return ResultUtil.success("成功");
...@@ -66,6 +68,7 @@ public class RestUpdateController { ...@@ -66,6 +68,7 @@ public class RestUpdateController {
@RequiresPermissions(value = {"updateLog:batchDelete", "updateLog:delete"}, logical = Logical.OR) @RequiresPermissions(value = {"updateLog:batchDelete", "updateLog:delete"}, logical = Logical.OR)
@PostMapping(value = "/remove") @PostMapping(value = "/remove")
@BussinessLog("删除更新日志")
public ResponseVO remove(Long[] ids) { public ResponseVO remove(Long[] ids) {
if (null == ids) { if (null == ids) {
return ResultUtil.error(500, "请至少选择一条记录"); return ResultUtil.error(500, "请至少选择一条记录");
...@@ -78,12 +81,14 @@ public class RestUpdateController { ...@@ -78,12 +81,14 @@ public class RestUpdateController {
@RequiresPermissions("updateLog:get") @RequiresPermissions("updateLog:get")
@PostMapping("/get/{id}") @PostMapping("/get/{id}")
@BussinessLog("获取更新日志详情")
public ResponseVO get(@PathVariable Long id) { public ResponseVO get(@PathVariable Long id) {
return ResultUtil.success(null, this.updateRecordeService.getByPrimaryKey(id)); return ResultUtil.success(null, this.updateRecordeService.getByPrimaryKey(id));
} }
@RequiresPermissions("updateLog:edit") @RequiresPermissions("updateLog:edit")
@PostMapping("/edit") @PostMapping("/edit")
@BussinessLog("编辑更新日志")
public ResponseVO edit(UpdateRecorde updateRecorde) { public ResponseVO edit(UpdateRecorde updateRecorde) {
try { try {
updateRecordeService.updateSelective(updateRecorde); updateRecordeService.updateSelective(updateRecorde);
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
package com.zyd.blog.controller; package com.zyd.blog.controller;
import com.github.pagehelper.PageInfo; import com.github.pagehelper.PageInfo;
import com.zyd.blog.business.annotation.BussinessLog;
import com.zyd.blog.business.entity.User; import com.zyd.blog.business.entity.User;
import com.zyd.blog.business.enums.ResponseStatus; import com.zyd.blog.business.enums.ResponseStatus;
import com.zyd.blog.business.service.SysUserRoleService; import com.zyd.blog.business.service.SysUserRoleService;
...@@ -73,6 +74,7 @@ public class RestUserController { ...@@ -73,6 +74,7 @@ public class RestUserController {
*/ */
@RequiresPermissions("user:allotRole") @RequiresPermissions("user:allotRole")
@PostMapping("/saveUserRoles") @PostMapping("/saveUserRoles")
@BussinessLog("分配用户角色")
public ResponseVO saveUserRoles(Long userId, String roleIds) { public ResponseVO saveUserRoles(Long userId, String roleIds) {
if (StringUtils.isEmpty(userId)) { if (StringUtils.isEmpty(userId)) {
return ResultUtil.error("error"); return ResultUtil.error("error");
...@@ -83,6 +85,7 @@ public class RestUserController { ...@@ -83,6 +85,7 @@ public class RestUserController {
@RequiresPermissions("user:add") @RequiresPermissions("user:add")
@PostMapping(value = "/add") @PostMapping(value = "/add")
@BussinessLog("添加用户")
public ResponseVO add(User user) { public ResponseVO add(User user) {
User u = userService.getByUserName(user.getUsername()); User u = userService.getByUserName(user.getUsername());
if (u != null) { if (u != null) {
...@@ -100,6 +103,7 @@ public class RestUserController { ...@@ -100,6 +103,7 @@ public class RestUserController {
@RequiresPermissions(value = {"user:batchDelete", "user:delete"}, logical = Logical.OR) @RequiresPermissions(value = {"user:batchDelete", "user:delete"}, logical = Logical.OR)
@PostMapping(value = "/remove") @PostMapping(value = "/remove")
@BussinessLog("删除用户")
public ResponseVO remove(Long[] ids) { public ResponseVO remove(Long[] ids) {
if (null == ids) { if (null == ids) {
return ResultUtil.error(500, "请至少选择一条记录"); return ResultUtil.error(500, "请至少选择一条记录");
...@@ -113,12 +117,14 @@ public class RestUserController { ...@@ -113,12 +117,14 @@ public class RestUserController {
@RequiresPermissions("user:get") @RequiresPermissions("user:get")
@PostMapping("/get/{id}") @PostMapping("/get/{id}")
@BussinessLog("获取用户详情")
public ResponseVO get(@PathVariable Long id) { public ResponseVO get(@PathVariable Long id) {
return ResultUtil.success(null, this.userService.getByPrimaryKey(id)); return ResultUtil.success(null, this.userService.getByPrimaryKey(id));
} }
@RequiresPermissions("user:edit") @RequiresPermissions("user:edit")
@PostMapping("/edit") @PostMapping("/edit")
@BussinessLog("编辑用户")
public ResponseVO edit(User user) { public ResponseVO edit(User user) {
try { try {
userService.updateSelective(user); userService.updateSelective(user);
......
...@@ -43,4 +43,11 @@ logging: ...@@ -43,4 +43,11 @@ logging:
app: app:
# 是否启用kaptcha验证码 # 是否启用kaptcha验证码
enableKaptcha: false enableKaptcha: false
# shiro配置项
shiro:
loginUrl: "/passport/login/"
successUrl: "/"
unauthorizedUrl: "/error/403"
# 创建网站的时间,用于计算已建站的天数,默认为2018-01-01
buildWebsiteDate: 2018-01-01 00:00:00
####################################自定义配置########################################## ####################################自定义配置##########################################
\ No newline at end of file
...@@ -42,6 +42,8 @@ logging: ...@@ -42,6 +42,8 @@ logging:
app: app:
# 是否启用kaptcha验证码 # 是否启用kaptcha验证码
enableKaptcha: false enableKaptcha: false
# 创建网站的时间,用于计算已建站的天数,默认为2018-01-01
buildWebsiteDate: 2018-01-01 00:00:00
# shiro配置项 # shiro配置项
shiro: shiro:
loginUrl: "/passport/login/" loginUrl: "/passport/login/"
......
...@@ -964,7 +964,8 @@ a:hover { ...@@ -964,7 +964,8 @@ a:hover {
.toggle a { .toggle a {
padding: 15px 15px 0; padding: 15px 15px 0;
margin: 0; margin: 0;
cursor: pointer cursor: pointer;
color: #73879C;
} }
.toggle a i { .toggle a i {
...@@ -2137,14 +2138,14 @@ h4.graph_title { ...@@ -2137,14 +2138,14 @@ h4.graph_title {
line-height: 1.65857 line-height: 1.65857
} }
.tile-stats .count, .tile-stats h3, .tile-stats p { .tile-stats .count, .tile-stats h3, .tile-stats h4, .tile-stats p {
position: relative; position: relative;
margin: 0 0 0 10px; margin: 0 0 0 10px;
z-index: 5; z-index: 5;
padding: 0 padding: 0
} }
.tile-stats h3 { .tile-stats h3, .tile-stats h4 {
color: #BAB8B8 color: #BAB8B8
} }
......
...@@ -287,6 +287,17 @@ var zhyd = window.zhyd || { ...@@ -287,6 +287,17 @@ var zhyd = window.zhyd || {
}); });
}) })
} }
},
initSwitchery: function (delay) {
setTimeout(function () {
var elems = Array.prototype.slice.call(document.querySelectorAll('.js-switch'));
elems.forEach(function (html) {
var switchery = new Switchery(html, {
color: '#26B99A',
size: 'small'
});
});
}, delay || 0);
} }
}; };
...@@ -396,7 +407,7 @@ $(document).ready(function () { ...@@ -396,7 +407,7 @@ $(document).ready(function () {
$box.before(html); $box.before(html);
return; return;
} }
var tpl = '{{#data}}<li><a href="/comments"><span class="image"><img src="{{avatar}}" alt="user avatar"></span> <span><span>{{nickname}}</span> <span class="time">{{createTimeString}}</span></span> <span class="message">点击查看&审核</span></a></li>{{/data}}'; var tpl = '{{#data}}<li><a href="/comments"><span class="image"><img src="{{#avatar}}{{avatar}}{{/avatar}}{{^avatar}}/assets/images/user.png{{/avatar}}" alt="user avatar"></span> <span><span>{{nickname}}</span> <span class="time">{{createTimeString}}</span></span> <span class="message">点击查看&审核</span></a></li>{{/data}}';
var html = Mustache.render(tpl, json); var html = Mustache.render(tpl, json);
$box.before(html); $box.before(html);
$(".noticeNum").text(json.data.length); $(".noticeNum").text(json.data.length);
......
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @date 2018/5/21 11:05
* @since 1.0
*/
var zhyd = window.zhyd || {};
zhyd.chartConfig = {
color: ["#26B99A", "#34495E", "#BDC3C7", "#3498DB", "#9B59B6", "#8abb6f", "#759c6a", "#bfd3b7"],
title: {
itemGap: 8,
textStyle: {
fontWeight: "normal",
color: "#408829"
}
},
dataRange: {
color: ["#1f610a", "#97b58d"]
},
toolbox: {
color: ["#408829", "#408829", "#408829", "#408829"]
},
tooltip: {
backgroundColor: "rgba(0,0,0,0.5)",
axisPointer: {
type: "line",
lineStyle: {
color: "#408829",
type: "dashed"
},
crossStyle: {
color: "#408829"
},
shadowStyle: {
color: "rgba(200,200,200,0.3)"
}
}
},
dataZoom: {
dataBackgroundColor: "#eee",
fillerColor: "rgba(64,136,41,0.2)",
handleColor: "#408829"
},
grid: {
borderWidth: 0
},
categoryAxis: {
axisLine: {
lineStyle: {
color: "#408829"
}
},
splitLine: {
lineStyle: {
color: ["#eee"]
}
}
},
valueAxis: {
axisLine: {
lineStyle: {
color: "#408829"
}
},
splitArea: {
show: !0,
areaStyle: {
color: ["rgba(250,250,250,0.1)", "rgba(200,200,200,0.1)"]
}
},
splitLine: {
lineStyle: {
color: ["#eee"]
}
}
},
timeline: {
lineStyle: {
color: "#408829"
},
controlStyle: {
normal: {
color: "#408829"
},
emphasis: {
color: "#408829"
}
}
},
k: {
itemStyle: {
normal: {
color: "#68a54a",
color0: "#a9cba2",
lineStyle: {
width: 1,
color: "#408829",
color0: "#86b379"
}
}
}
},
map: {
itemStyle: {
normal: {
areaStyle: {
color: "#ddd"
},
label: {
textStyle: {
color: "#c12e34"
}
}
},
emphasis: {
areaStyle: {
color: "#99d2dd"
},
label: {
textStyle: {
color: "#c12e34"
}
}
}
}
},
force: {
itemStyle: {
normal: {
linkStyle: {
strokeColor: "#408829"
}
}
}
},
chord: {
padding: 4,
itemStyle: {
normal: {
lineStyle: {
width: 1,
color: "rgba(128, 128, 128, 0.5)"
},
chordStyle: {
lineStyle: {
width: 1,
color: "rgba(128, 128, 128, 0.5)"
}
}
},
emphasis: {
lineStyle: {
width: 1,
color: "rgba(128, 128, 128, 0.5)"
},
chordStyle: {
lineStyle: {
width: 1,
color: "rgba(128, 128, 128, 0.5)"
}
}
}
}
},
gauge: {
startAngle: 225,
endAngle: -45,
axisLine: {
show: !0,
lineStyle: {
color: [[.2, "#86b379"], [.8, "#68a54a"], [1, "#408829"]],
width: 8
}
},
axisTick: {
splitNumber: 10,
length: 12,
lineStyle: {
color: "auto"
}
},
axisLabel: {
textStyle: {
color: "auto"
}
},
splitLine: {
length: 18,
lineStyle: {
color: "auto"
}
},
pointer: {
length: "90%",
color: "auto"
},
title: {
textStyle: {
color: "#333"
}
},
detail: {
textStyle: {
color: "auto"
}
}
},
textStyle: {
fontFamily: "Arial, Verdana, sans-serif"
}
};
zhyd.createChart = function (options) {
var op = $.extend({
id: '',
theme: zhyd.chartConfig,
title: null,
subtext: '',
legendData: [],
series: {
name: '数值',
type: 'line',
seriesData: []
}
}, options);
var myChart = echarts.init(document.getElementById(op.id), op.theme);
var option = {
tooltip: {
trigger: "item",
formatter: "{a} <br/>{b} : {c} ({d}%)"
},
legend: {
x: "center",
y: "bottom",
data: op.legendData
},
calculable: !0,
series: [{
name: op.series.name,
type: op.series.type,
radius: "55%",
center: ["50%", "35%"],
label: {
show: false
},
data: op.series.seriesData
}]
};
// 使用刚指定的配置项和数据显示图表。
myChart.setOption(option);
};
function init_echarts() {
if ("undefined" != typeof echarts) {
if ($("#echart_line").length) {
var f = echarts.init(document.getElementById("echart_line"), zhyd.chartConfig);
f.setOption({
tooltip: {
trigger: "axis"
},
legend: {
x: 220,
y: 40,
data: ["Intent", "Pre-order", "Deal"]
},
calculable: !0,
xAxis: [{
type: "category",
boundaryGap: !1,
data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
}],
yAxis: [{
type: "value"
}],
series: [{
name: "Deal",
type: "line",
smooth: !0,
itemStyle: {
normal: {
areaStyle: {
type: "default"
}
}
},
data: [10, 12, 21, 54, 260, 830, 710]
}, {
name: "Pre-order",
type: "line",
smooth: !0,
itemStyle: {
normal: {
areaStyle: {
type: "default"
}
}
},
data: [30, 182, 434, 791, 390, 30, 10]
}, {
name: "Intent",
type: "line",
smooth: !0,
itemStyle: {
normal: {
areaStyle: {
type: "default"
}
}
},
data: [1320, 1132, 601, 234, 120, 90, 20]
}]
})
}
}
}
\ No newline at end of file
...@@ -36,7 +36,8 @@ ...@@ -36,7 +36,8 @@
init: function (options) { init: function (options) {
$.tableUtil._option = options; $.tableUtil._option = options;
// console.log(options.url); // console.log(options.url);
$('#tablelist').bootstrapTable('destroy').bootstrapTable({ var $tablelist = $('#tablelist');
$tablelist.bootstrapTable('destroy').bootstrapTable({
url: options.url, url: options.url,
method: 'post', //请求方式(*) method: 'post', //请求方式(*)
toolbar: '#toolbar', //工具按钮用哪个容器 toolbar: '#toolbar', //工具按钮用哪个容器
...@@ -92,6 +93,9 @@ ...@@ -92,6 +93,9 @@
rowStyle: options.rowStyle || function (row, index){return {};}, rowStyle: options.rowStyle || function (row, index){return {};},
columns: options.columns columns: options.columns
}); });
$tablelist.on('load-success.bs.table',function(data){
zhyd.initSwitchery();
});
}, },
queryParams: function (params) { queryParams: function (params) {
params = $.extend({}, params); params = $.extend({}, params);
......
...@@ -23,7 +23,7 @@ ...@@ -23,7 +23,7 @@
<#-- 由草稿状态批量修改为已发布状态 --> <#-- 由草稿状态批量修改为已发布状态 -->
<@shiro.hasPermission name="article:publish"> <@shiro.hasPermission name="article:publish">
<button id="btn_update_status" type="button" class="btn btn-default" title="批量发布"> <button id="btn_update_status" type="button" class="btn btn-default" title="批量发布">
<i class="fa fa-trash-o"></i> 批量发布 <i class="fa fa-bullhorn"></i> 批量发布
</button> </button>
</@shiro.hasPermission> </@shiro.hasPermission>
<@shiro.hasPermission name="article:batchPush"> <@shiro.hasPermission name="article:batchPush">
...@@ -51,14 +51,14 @@ ...@@ -51,14 +51,14 @@
*/ */
function operateFormatter(code, row, index) { function operateFormatter(code, row, index) {
var trId = row.id; var trId = row.id;
var recommended = row.recommended ? '<i class="fa fa-thumbs-o-down"></i>取消推荐' : '<i class="fa fa-thumbs-o-up"></i>推荐'; // var recommended = row.recommended ? '<i class="fa fa-thumbs-o-down"></i>取消推荐' : '<i class="fa fa-thumbs-o-up"></i>推荐';
var top = row.top ? '<i class="fa fa-arrow-circle-down"></i>取消置顶' : '<i class="fa fa-arrow-circle-up"></i>置顶'; // var top = row.top ? '<i class="fa fa-arrow-circle-down"></i>取消置顶' : '<i class="fa fa-arrow-circle-up"></i>置顶';
var operateBtn = [ var operateBtn = [
'<@shiro.hasPermission name="article:push"><a class="btn btn-xs btn-info btn-push" title="推送" data-id="' + trId + '"><i class="fa fa-send-o"></i>推送</a></@shiro.hasPermission>', '<@shiro.hasPermission name="article:push"><a class="btn btn-xs btn-info btn-push" title="推送" data-id="' + trId + '"><i class="fa fa-send-o"></i></a></@shiro.hasPermission>',
'<@shiro.hasPermission name="article:edit"><a class="btn btn-xs btn-primary" href="/article/update/' + trId + '"><i class="fa fa-edit"></i>编辑</a></@shiro.hasPermission>', '<@shiro.hasPermission name="article:edit"><a class="btn btn-xs btn-primary" href="/article/update/' + trId + '"><i class="fa fa-edit"></i></a></@shiro.hasPermission>',
'<@shiro.hasPermission name="article:delete"><a class="btn btn-xs btn-danger btn-remove" data-id="' + trId + '"><i class="fa fa-trash-o"></i>删除</a></@shiro.hasPermission>', '<@shiro.hasPermission name="article:delete"><a class="btn btn-xs btn-danger btn-remove" data-id="' + trId + '"><i class="fa fa-trash-o"></i></a></@shiro.hasPermission>',
'<@shiro.hasPermission name="article:top"><a class="btn btn-xs btn-success btn-top" data-id="' + trId + '">' + top + '</a></@shiro.hasPermission>', <#--'<@shiro.hasPermission name="article:top"><a class="btn btn-xs btn-success btn-top" data-id="' + trId + '">' + top + '</a></@shiro.hasPermission>',-->
'<@shiro.hasPermission name="article:recommend"><a class="btn btn-xs btn-success btn-recommend" data-id="' + trId + '">' + recommended + '</a></@shiro.hasPermission>' <#--'<@shiro.hasPermission name="article:recommend"><a class="btn btn-xs btn-success btn-recommend" data-id="' + trId + '">' + recommended + '</a></@shiro.hasPermission>'-->
]; ];
return operateBtn.join(''); return operateBtn.join('');
} }
...@@ -75,11 +75,11 @@ ...@@ -75,11 +75,11 @@
}, { }, {
field: 'title', field: 'title',
title: '标题', title: '标题',
width: '200px', width: '270px',
editable: false, editable: false,
formatter: function (code, row, index) { formatter: function (code, row, index) {
var title = code; var title = code;
title = title.length > 10 ? (title.substr(0, 10) + '...') : title; title = title.length > 20 ? (title.substr(0, 20) + '...') : title;
var id = row.id; var id = row.id;
// var original= row.original ? "原创" : "转载"; // var original= row.original ? "原创" : "转载";
// return '<strong>['+original+']</strong> <a href="' + appConfig.wwwPath + '/article/' + id + '" target="_blank">' + code + '</a>'; // return '<strong>['+original+']</strong> <a href="' + appConfig.wwwPath + '/article/' + id + '" target="_blank">' + code + '</a>';
...@@ -88,45 +88,31 @@ ...@@ -88,45 +88,31 @@
return status + '<a href="' + appConfig.wwwPath + '/article/' + id + '" target="_blank" title="' + code + '">' + title + '</a>'; return status + '<a href="' + appConfig.wwwPath + '/article/' + id + '" target="_blank" title="' + code + '">' + title + '</a>';
} }
}, { }, {
field: 'type', field: 'comment',
title: '分类', title: '评论',
width: '80px', width: '50px',
editable: false, editable: false,
formatter: function (code) { formatter: function (code, row, index) {
var type = code; var checked = code ? 'checked' : '';
return '<a href="' + appConfig.wwwPath + '/type/' + type.id + '" target="_blank"> ' + type.name + '</a> '; return '<input type="checkbox" name="comment" class="js-switch btn-comment" data-id="' + row.id + '" data-type="comment" ' + checked + '>';
} }
}, { }, {
field: 'tags', field: 'recommended',
title: '标签', title: '推荐 <i class="fa fa-question-circle-o" title="推荐的文章会在首页滚动显示"></i>',
width: '140px',
editable: false, editable: false,
formatter: function (code) {
var tags = code;
var tagHtml = '';
if (tags) {
for (var i = 0, len = tags.length; i < len; i++) {
var tag = tags[i];
tagHtml += ' <a class="" href="' + appConfig.wwwPath + '/tag/' + tag.id + '" target="_blank"> ' + tag.name + '</a> |';
}
}
return tagHtml.substr(0, tagHtml.length - 1);
}
}, {
field: 'comment',
title: '评论',
width: '50px', width: '50px',
editable: false, formatter: function (code, row, index) {
formatter: function (code) { var checked = code ? 'checked' : '';
return code ? '<span class="label label-success">开启</span>' : '<span class="label label-danger">关闭</span>'; return '<input type="checkbox" name="recommended" class="js-switch btn-recommended" data-id="' + row.id + '" data-type="recommend" ' + checked + '>';
} }
}, { }, {
field: 'createTime', field: 'top',
title: '发布时间', title: '置顶',
editable: false, editable: false,
width: '100px', width: '50px',
formatter: function (code) { formatter: function (code, row, index) {
return new Date(code).format("yyyy-MM-dd hh:mm:ss") var checked = code ? 'checked' : '';
return '<input type="checkbox" name="top" class="js-switch btn-top" data-id="' + row.id + '" data-type="top" ' + checked + '>';
} }
}, { }, {
field: 'lookCount', field: 'lookCount',
...@@ -143,10 +129,18 @@ ...@@ -143,10 +129,18 @@
title: '喜欢', title: '喜欢',
editable: false, editable: false,
width: '50px' width: '50px'
}, {
field: 'createTime',
title: '发布时间',
editable: false,
width: '130px',
formatter: function (code) {
return new Date(code).format("yyyy-MM-dd hh:mm:ss")
}
}, { }, {
field: 'operate', field: 'operate',
title: '操作', title: '操作',
width: '200px', width: '100px',
formatter: operateFormatter //自定义方法,添加操作按钮 formatter: operateFormatter //自定义方法,添加操作按钮
} }
] ]
...@@ -156,36 +150,25 @@ ...@@ -156,36 +150,25 @@
//2.初始化Button的点击事件 //2.初始化Button的点击事件
$.buttonUtil.init(options); $.buttonUtil.init(options);
/** $('#tablelist').on('click', '.switchery', function () {
* 推荐 var $input = $(this).prev();
*/
$('#tablelist').on('click', '.btn-recommend', function () {
var $this = $(this);
var id = $this.attr("data-id");
update("recommend", id);
});
/** var id = $input.data("id");
* 置顶 var type = $input.data("type");
*/
$('#tablelist').on('click', '.btn-top', function () {
var $this = $(this);
var id = $this.attr("data-id");
update("top", id);
});
function update(type, id) {
$.ajax({ $.ajax({
type: "post", type: "post",
url: "/article/update/" + type, url: "/article/update/" + type,
traditional: true, traditional: true,
data: {'id': id}, data: {'id': id},
success: function (json) { success: function (json) {
$.alert.ajaxSuccess(json); if (json.status !== 200) {
$.alert.error(json.message);
}
}, },
error: $.alert.ajaxError error: $.alert.ajaxError
}); });
} });
/** /**
* 推送到百度 * 推送到百度
...@@ -259,7 +242,7 @@ ...@@ -259,7 +242,7 @@
message += '不合法的url:' + notValid + '\n'; message += '不合法的url:' + notValid + '\n';
} }
message += '今日剩余' + remain + '条可推送的url。'; message += '今日剩余' + remain + '条可推送的url。';
$.alert.info(message, 5000); $.alert.info(message, null, 5000);
} }
}, },
error: $.alert.ajaxError error: $.alert.ajaxError
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
<link href="https://cdn.bootcss.com/bootstrap-daterangepicker/2.1.24/daterangepicker.min.css" rel="stylesheet"> <link href="https://cdn.bootcss.com/bootstrap-daterangepicker/2.1.24/daterangepicker.min.css" rel="stylesheet">
<link href="https://cdn.bootcss.com/bootstrap-datetimepicker/4.17.47/css/bootstrap-datetimepicker.min.css" rel="stylesheet"> <link href="https://cdn.bootcss.com/bootstrap-datetimepicker/4.17.47/css/bootstrap-datetimepicker.min.css" rel="stylesheet">
<link href="https://cdn.bootcss.com/zTree.v3/3.5.29/css/metroStyle/metroStyle.min.css" rel="stylesheet"> <link href="https://cdn.bootcss.com/zTree.v3/3.5.29/css/metroStyle/metroStyle.min.css" rel="stylesheet">
<link href="https://cdnjs.cloudflare.com/ajax/libs/switchery/0.8.2/switchery.min.css" rel="stylesheet">
<link href="/assets/css/zhyd.core.css" rel="stylesheet"> <link href="/assets/css/zhyd.core.css" rel="stylesheet">
<#nested> <#nested>
......
<footer> <footer>
<div class="pull-right"> <div class="pull-right">
Copyright © 2018 <a href="https://www.zhyd.me" target="_blank">yadong.zhang</a> · Powered by <a href="https://gitee.com/yadong.zhang/DBlog" title="DBlog是一款简洁美观、自适应的Java博客系统..." target="_blank"><strong>DBlog</strong></a>. All Rights Reserved. Copyright © 2018 <a href="https://www.zhyd.me" target="_blank">yadong.zhang</a> · Powered by <a href="https://gitee.com/yadong.zhang/DBlog" title="OneBlog是一款简洁美观、自适应的Java博客系统..." target="_blank"><strong>OneBlog</strong></a>. All Rights Reserved.
</div> </div>
<div class="clearfix"></div> <div class="clearfix"></div>
</footer> </footer>
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
<script src="https://cdn.bootcss.com/zTree.v3/3.5.29/js/jquery.ztree.core.min.js"></script> <script src="https://cdn.bootcss.com/zTree.v3/3.5.29/js/jquery.ztree.core.min.js"></script>
<script src="https://cdn.bootcss.com/zTree.v3/3.5.29/js/jquery.ztree.excheck.min.js"></script> <script src="https://cdn.bootcss.com/zTree.v3/3.5.29/js/jquery.ztree.excheck.min.js"></script>
<script src="https://unpkg.com/wangeditor@3.1.1/release/wangEditor.js" type="text/javascript"></script> <script src="https://unpkg.com/wangeditor@3.1.1/release/wangEditor.js" type="text/javascript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/switchery/0.8.2/switchery.min.js"></script>
<script src="/assets/js/validator.js"></script> <script src="/assets/js/validator.js"></script>
<script src="/assets/js/ajaxfileupload.js"></script> <script src="/assets/js/ajaxfileupload.js"></script>
......
...@@ -32,8 +32,8 @@ ...@@ -32,8 +32,8 @@
<ul id="menu1" class="dropdown-menu list-unstyled msg_list" role="menu"> <ul id="menu1" class="dropdown-menu list-unstyled msg_list" role="menu">
<li> <li>
<div class="text-center"> <div class="text-center">
<a> <a href="/comments">
<strong>See All Alerts</strong> <strong>查看所有</strong>
<i class="fa fa-angle-right"></i> <i class="fa fa-angle-right"></i>
</a> </a>
</div> </div>
......
...@@ -44,7 +44,7 @@ ...@@ -44,7 +44,7 @@
<div class="separator"> <div class="separator">
<div class="clearfix"></div> <div class="clearfix"></div>
<div> <div>
<h1><i class="fa fa-coffee"></i> ${config.siteName}后台管理系统</h1> <h1><i class="fa fa-coffee"></i> ${config.siteName}管理系统</h1>
<p>Copyright © 2018 <a href="https://www.zhyd.me" target="_blank" style="margin: 0">yadong.zhang</a>. All Rights Reserved. </p> <p>Copyright © 2018 <a href="https://www.zhyd.me" target="_blank" style="margin: 0">yadong.zhang</a>. All Rights Reserved. </p>
</div> </div>
</div> </div>
......
...@@ -94,9 +94,9 @@ ...@@ -94,9 +94,9 @@
var status = row.status; var status = row.status;
var html = ''; var html = '';
if (status && status == 'NOT_RELEASE') { if (status && status == 'NOT_RELEASE') {
html = '<@shiro.hasPermission name="notice:release"><a class="btn btn-xs btn-primary btn-release" data-id="' + trId + '"><i class="fa fa-rocket fa-fw"></i>发布</a></@shiro.hasPermission>'; html = '<@shiro.hasPermission name="notice:release"><a class="btn btn-xs btn-success btn-release" data-id="' + trId + '"><i class="fa fa-rocket fa-fw"></i>发布</a></@shiro.hasPermission>';
} else { } else {
html = '<@shiro.hasPermission name="notice:withdraw"><a class="btn btn-xs btn-primary btn-withdraw" data-id="' + trId + '"><i class="fa fa-rocket fa-rotate-180 fa-fw"></i>撤回</a></@shiro.hasPermission>'; html = '<@shiro.hasPermission name="notice:withdraw"><a class="btn btn-xs btn-warning btn-withdraw" data-id="' + trId + '"><i class="fa fa-rocket fa-rotate-180 fa-fw"></i>撤回</a></@shiro.hasPermission>';
} }
var operateBtn = [ var operateBtn = [
html, html,
...@@ -149,7 +149,7 @@ ...@@ -149,7 +149,7 @@
}, { }, {
field: 'operate', field: 'operate',
title: '操作', title: '操作',
width: '135px', width: '150px',
formatter: operateFormatter //自定义方法,添加操作按钮 formatter: operateFormatter //自定义方法,添加操作按钮
} }
] ]
......
...@@ -2,6 +2,7 @@ package com.zyd.blog; ...@@ -2,6 +2,7 @@ package com.zyd.blog;
import com.zyd.blog.business.enums.QiniuUploadType; import com.zyd.blog.business.enums.QiniuUploadType;
import com.zyd.blog.plugin.QiniuApi; import com.zyd.blog.plugin.QiniuApi;
import com.zyd.blog.spider.enums.ExitWayEnum;
import com.zyd.blog.spider.model.*; import com.zyd.blog.spider.model.*;
import com.zyd.blog.spider.processor.ArticleSpiderProcessor; import com.zyd.blog.spider.processor.ArticleSpiderProcessor;
import com.zyd.blog.spider.processor.BaseSpider; import com.zyd.blog.spider.processor.BaseSpider;
...@@ -33,9 +34,9 @@ public class BlogAdminApplicationTests { ...@@ -33,9 +34,9 @@ public class BlogAdminApplicationTests {
} }
@Test @Test
public void spiderTest() { public void imoocSpiderTest() {
BaseSpider<Article> spider = new ArticleSpiderProcessor(new ImoocModel().setUid("1175248") BaseSpider<Article> spider = new ArticleSpiderProcessor(new BaseModel().setUid("1175248")
.setTotalPage(3) .setEntryUrls("https://www.imooc.com/u/1175248/articles?page=1")
.setDomain("www.imooc.com") .setDomain("www.imooc.com")
.setTitleRegex("//span[@class=js-title]/html()") .setTitleRegex("//span[@class=js-title]/html()")
.setAuthorRegex("//div[@class=name_con]/p[@class=name]/a[@class=nick]/html()") .setAuthorRegex("//div[@class=name_con]/p[@class=name]/a[@class=nick]/html()")
...@@ -44,14 +45,16 @@ public class BlogAdminApplicationTests { ...@@ -44,14 +45,16 @@ public class BlogAdminApplicationTests {
.setTargetLinksRegex("/article/[0-9]{1,10}") .setTargetLinksRegex("/article/[0-9]{1,10}")
.setTagRegex("//div[@class=cat-box]/div[@class=cat-wrap]/a[@class=cat]/html()") .setTagRegex("//div[@class=cat-box]/div[@class=cat-wrap]/a[@class=cat]/html()")
.setHeader("Host", "www.imooc.com") .setHeader("Host", "www.imooc.com")
.setHeader("Referer", "https://www.imooc.com")); .setHeader("Referer", "https://www.imooc.com")
.setExitWay(ExitWayEnum.URL_COUNT)
.setCount(5));
spider.run(); spider.run();
} }
@Test @Test
public void csdnSpiderTest() { public void csdnSpiderTest() {
BaseSpider<Article> spider = new ArticleSpiderProcessor(new CsdnModel().setUid("u011197448") BaseSpider<Article> spider = new ArticleSpiderProcessor(new BaseModel().setUid("u011197448")
.setTotalPage(1) .setEntryUrls("https://blog.csdn.net/u011197448/article/list/1")
.setDomain("blog.csdn.net") .setDomain("blog.csdn.net")
.setTitleRegex("//h1[@class=title-article]/html()") .setTitleRegex("//h1[@class=title-article]/html()")
.setAuthorRegex("//div[@class=profile-intro]/div[@class=user-info]/p[@class=name]/a[@class=text-truncate]/html()") .setAuthorRegex("//div[@class=profile-intro]/div[@class=user-info]/p[@class=name]/a[@class=text-truncate]/html()")
...@@ -60,14 +63,16 @@ public class BlogAdminApplicationTests { ...@@ -60,14 +63,16 @@ public class BlogAdminApplicationTests {
.setTargetLinksRegex(".*blog\\.csdn\\.net/u011197448/article/details/[0-9a-zA-Z]{1,15}") .setTargetLinksRegex(".*blog\\.csdn\\.net/u011197448/article/details/[0-9a-zA-Z]{1,15}")
.setTagRegex("//span[@class=artic-tag-box]/a[@class=tag-link]/html()") .setTagRegex("//span[@class=artic-tag-box]/a[@class=tag-link]/html()")
.setHeader("Host", "blog.csdn.net") .setHeader("Host", "blog.csdn.net")
.setHeader("Referer", "https://blog.csdn.net/u011197448/article/list/1")); .setHeader("Referer", "https://blog.csdn.net/u011197448/article/list/1")
.setExitWay(ExitWayEnum.DURATION)
.setCount(5));
spider.run(); spider.run();
} }
@Test @Test
public void iteyeSpiderTest() { public void iteyeSpiderTest() {
BaseSpider<Article> spider = new ArticleSpiderProcessor(new IteyeModel().setUid("843977358") BaseSpider<Article> spider = new ArticleSpiderProcessor(new BaseModel().setUid("843977358")
.setTotalPage(1) .setEntryUrls("http://843977358.iteye.com/?page=1")
.setDomain("843977358.iteye.com") .setDomain("843977358.iteye.com")
.setTitleRegex("//div[@class=blog_title]/h3/a/html()") .setTitleRegex("//div[@class=blog_title]/h3/a/html()")
.setAuthorRegex("//div[@id=blog_owner_name]/html()") .setAuthorRegex("//div[@id=blog_owner_name]/html()")
...@@ -76,15 +81,17 @@ public class BlogAdminApplicationTests { ...@@ -76,15 +81,17 @@ public class BlogAdminApplicationTests {
.setTargetLinksRegex(".*843977358\\.iteye\\.com/blog/[0-9]+") .setTargetLinksRegex(".*843977358\\.iteye\\.com/blog/[0-9]+")
.setTagRegex("//div[@class=news_tag]/a/html()") .setTagRegex("//div[@class=news_tag]/a/html()")
.setHeader("Host", "843977358.iteye.com") .setHeader("Host", "843977358.iteye.com")
.setHeader("Referer", "http://843977358.iteye.com/")); .setHeader("Referer", "http://843977358.iteye.com/")
.setExitWay(ExitWayEnum.URL_COUNT)
.setCount(5));
spider.run(); spider.run();
} }
@Test @Test
public void cnblogSpiderTest() { public void cnblogSpiderTest() {
BaseSpider<Article> spider = new ArticleSpiderProcessor(new CnblogModel().setUid("zhangyadong") BaseSpider<Article> spider = new ArticleSpiderProcessor(new BaseModel().setUid("zhangyadong")
.setTotalPage(1) .setEntryUrls("https://www.cnblogs.com/zhangyadong/default.html?page=1")
.setDomain("www.cnblogs.com") .setDomain("www.cnblogs.com")
.setTitleRegex("//a[@id=cb_post_title_url]/html()") .setTitleRegex("//a[@id=cb_post_title_url]/html()")
.setAuthorRegex("//div[@class=postDesc]/a[1]/html()") .setAuthorRegex("//div[@class=postDesc]/a[1]/html()")
......
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
*/ */
package com.zyd.blog.business.annotation; package com.zyd.blog.business.annotation;
import com.zyd.blog.business.enums.PlatformEnum;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
...@@ -41,4 +43,14 @@ public @interface BussinessLog { ...@@ -41,4 +43,14 @@ public @interface BussinessLog {
*/ */
String value() default ""; String value() default "";
/**
* 平台,默认为后台管理
*/
PlatformEnum platform() default PlatformEnum.ADMIN;
/**
* 是否将当前日志记录到数据库中
*/
boolean save() default true;
} }
...@@ -20,16 +20,24 @@ ...@@ -20,16 +20,24 @@
package com.zyd.blog.business.aspect; package com.zyd.blog.business.aspect;
import com.zyd.blog.business.annotation.BussinessLog; import com.zyd.blog.business.annotation.BussinessLog;
import com.zyd.blog.business.entity.Log;
import com.zyd.blog.business.entity.User;
import com.zyd.blog.business.enums.LogLevelEnum;
import com.zyd.blog.business.enums.LogTypeEnum;
import com.zyd.blog.business.enums.PlatformEnum;
import com.zyd.blog.business.service.SysLogService;
import com.zyd.blog.business.util.WebSpiderUtils;
import com.zyd.blog.util.AspectUtil; import com.zyd.blog.util.AspectUtil;
import com.zyd.blog.util.RegexUtils; import com.zyd.blog.util.RegexUtils;
import com.zyd.blog.util.RequestUtil; import com.zyd.blog.util.RequestUtil;
import com.zyd.blog.util.SessionUtil;
import eu.bitwalker.useragentutils.UserAgent;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.lang.reflect.Method; import java.lang.reflect.Method;
...@@ -49,6 +57,9 @@ import java.util.List; ...@@ -49,6 +57,9 @@ import java.util.List;
@Component @Component
public class BussinessLogAspect { public class BussinessLogAspect {
@Autowired
private SysLogService logService;
@Pointcut(value = "@annotation(com.zyd.blog.business.annotation.BussinessLog)") @Pointcut(value = "@annotation(com.zyd.blog.business.annotation.BussinessLog)")
public void pointcut() { public void pointcut() {
} }
...@@ -68,23 +79,48 @@ public class BussinessLogAspect { ...@@ -68,23 +79,48 @@ public class BussinessLogAspect {
return result; return result;
} }
@AfterThrowing(pointcut = "pointcut()", throwing = "ex")
public void afterThrowing(JoinPoint joinPoint, Throwable ex) throws Throwable {
log.error("捕获到了异常...", ex);
}
private void handle(ProceedingJoinPoint point) throws Exception { private void handle(ProceedingJoinPoint point) throws Exception {
//获取拦截方法的参数 //获取拦截方法的参数
String className = AspectUtil.getClassName(point); String className = AspectUtil.getClassName(point);
Method currentMethod = AspectUtil.getMethod(point); Method currentMethod = AspectUtil.getMethod(point);
//获取操作名称 //获取操作名称
BussinessLog annotation = currentMethod.getAnnotation(BussinessLog.class); BussinessLog annotation = currentMethod.getAnnotation(BussinessLog.class);
boolean save = annotation.save();
PlatformEnum platform = annotation.platform();
String bussinessName = parseContent(point.getArgs(), annotation.value()); String bussinessName = parseContent(point.getArgs(), annotation.value());
String ua = RequestUtil.getUa(); String ua = RequestUtil.getUa();
log.info("{}-{}.{}", bussinessName, className, currentMethod.getName()); log.info("{}-{}.{}", bussinessName, className, currentMethod.getName());
log.info("IP: {}, Method: {}, Request URL: {}", RequestUtil.getIp(), RequestUtil.getMethod(), RequestUtil.getRequestUrl()); log.info("IP: {}, Method: {}, Request URL: {}", RequestUtil.getIp(), RequestUtil.getMethod(), RequestUtil.getRequestUrl());
log.info("User-Agent: " + ua); log.info("User-Agent: " + ua);
if (!save) {
return;
}
Log sysLog = new Log();
sysLog.setLogLevel(LogLevelEnum.INFO);
sysLog.setType(platform.equals(PlatformEnum.WEB) ? LogTypeEnum.VISIT : LogTypeEnum.SYSTEM);
sysLog.setIp(RequestUtil.getIp());
sysLog.setReferer(RequestUtil.getReferer());
sysLog.setRequestUrl(RequestUtil.getRequestUrl());
sysLog.setUa(ua);
sysLog.setSpiderType(WebSpiderUtils.parseUa(ua));
User user = SessionUtil.getUser();
if(user != null) {
sysLog.setUserId(user.getId());
sysLog.setContent(String.format("用户: [%s] | 操作: %s | 参数: %s", user.getUsername(), bussinessName, RequestUtil.getParameters()));
} else {
sysLog.setContent(String.format("访客: [%s] | 操作: %s | 参数: %s", sysLog.getIp(), bussinessName, RequestUtil.getParameters()));
}
try {
UserAgent agent = UserAgent.parseUserAgentString(ua);
sysLog.setBrowser(agent.getBrowser().getName());
sysLog.setOs(agent.getOperatingSystem().getName());
logService.insert(sysLog);
} catch (Exception e) {
e.printStackTrace();
}
} }
private String parseContent(Object[] params, String bussinessName) { private String parseContent(Object[] params, String bussinessName) {
......
/**
* Copyright [2016-2018] [yadong.zhang]
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zyd.blog.business.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.zyd.blog.business.enums.LogLevelEnum;
import com.zyd.blog.business.enums.LogTypeEnum;
import com.zyd.blog.persistence.beans.SysLog;
import java.util.Date;
/**
* @author yadong.zhang email:yadong.zhang0415(a)gmail.com
* @version 1.0
* @date 2018/01/09 17:40
* @since 1.0
*/
public class Log {
private static final long serialVersionUID = 1L;
private SysLog sysLog;
/* generateConstructor */
public Log() {
this.sysLog = new SysLog();
}
public Log(SysLog sysLog) {
this.sysLog = sysLog;
}
@JsonIgnore
public SysLog getSysLog() {
return this.sysLog;
}
public Long getId() {
return this.sysLog.getId();
}
public void setId(Long id) {
this.sysLog.setId(id);
}
public Long getUserId() {
return this.sysLog.getUserId();
}
public void setUserId(Long userId) {
this.sysLog.setUserId(userId);
}
public String getLogLevel() {
return this.sysLog.getLogLevel();
}
public void setLogLevel(LogLevelEnum logLevel) {
if (null == logLevel) {
return;
}
this.sysLog.setLogLevel(logLevel.toString());
}
public void setLogLevel(String logLevel) {
this.sysLog.setLogLevel(logLevel);
}
public String getIp() {
return this.sysLog.getIp();
}
public void setIp(String ip) {
this.sysLog.setIp(ip);
}
public String getContent() {
return this.sysLog.getContent();
}
public void setContent(String content) {
this.sysLog.setContent(content);
}
public String getType() {
return this.sysLog.getType();
}
public void setType(LogTypeEnum type) {
if (null == type) {
return;
}
this.sysLog.setType(type.toString());
}
public void setType(String type) {
this.sysLog.setType(type);
}
public String getUa() {
return this.sysLog.getUa();
}
public void setUa(String ua) {
this.sysLog.setUa(ua);
}
public String getOs() {
return this.sysLog.getOs();
}
public void setOs(String os) {
this.sysLog.setOs(os);
}
public String getBrowser() {
return this.sysLog.getBrowser();
}
public void setBrowser(String browser) {
this.sysLog.setBrowser(browser);
}
public String getRequestUrl() {
return this.sysLog.getRequestUrl();
}
public void setRequestUrl(String requestUrl) {
this.sysLog.setRequestUrl(requestUrl);
}
public String getReferer() {
return this.sysLog.getReferer();
}
public void setReferer(String referer) {
this.sysLog.setReferer(referer);
}
public String getSpiderType() {
return this.sysLog.getSpiderType();
}
public void setSpiderType(String spiderType) {
this.sysLog.setSpiderType(spiderType);
}
public Date getCreateTime() {
return this.sysLog.getCreateTime();
}
public void setCreateTime(Date createTime) {
this.sysLog.setCreateTime(createTime);
}
public Date getUpdateTime() {
return this.sysLog.getUpdateTime();
}
public void setUpdateTime(Date updateTime) {
this.sysLog.setUpdateTime(updateTime);
}
}
/**
* MIT License
* Copyright (c) 2018 yadong.zhang
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.zyd.blog.business.entity;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.zyd.blog.persistence.beans.BizStatistics;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @website https://www.zhyd.me
* @date 2018/4/16 16:26
* @since 1.0
*/
public class Statistics {
private BizStatistics bizStatistics;
public Statistics(BizStatistics bizStatistics) {
this.bizStatistics = bizStatistics;
}
public Statistics() {
}
@JsonIgnore
public BizStatistics getBizStatistics() {
return bizStatistics;
}
public String getName() {
return this.bizStatistics.getName();
}
public void setName(String name) {
this.bizStatistics.setName(name);
}
public Integer getValue() {
return this.bizStatistics.getValue();
}
public void setValue(Integer value) {
this.bizStatistics.setValue(value);
}
}
package com.zyd.blog.business.enums;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @date 2018/7/15 22:00
* @since 1.0
*/
public enum CachePrefixEnum {
BIZ("biz_cache_"),
VIEW("view_cache_"),
DDOS("ddos_cache_"),
WX("wx_api_cache_"),
SPIDER("spider_cache_"),
;
private String prefix;
CachePrefixEnum(String prefix) {
this.prefix = prefix;
}
public String getPrefix() {
return prefix;
}
}
package com.zyd.blog.business.enums;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @date 2018/5/7 15:16
* @since 1.0
*/
public enum PlatformEnum {
ADMIN, WEB
}
...@@ -34,15 +34,7 @@ public enum ResponseStatus { ...@@ -34,15 +34,7 @@ public enum ResponseStatus {
SUCCESS(200, "操作成功!"), SUCCESS(200, "操作成功!"),
ERROR(500, "服务器未知错误!"), ERROR(500, "服务器未知错误!"),
UNAUTHORIZED(500, "尚未登录!"), INVALID_PARAMS(500, "操作失败无效的参数!");
FORBIDDEN(500, "您没有操作权限!"),
NOT_FOUND(500, "资源不存在!"),
LOGIN_ERROR(500, "账号或密码错误!"),
USER_EXIST(500, "已存在的用户!"),
INVALID_AUTHCODE(500, "手机验证码无效!"),
INVALID_TOKEN(500, "无效的TOKEN,您没有操作权限!"),
INVALID_ACCESS(500, "无效的请求,该请求已过期!"),
DELETE_ERROR(500, "删除失败!");
private Integer code; private Integer code;
private String message; private String message;
......
/**
* MIT License
* Copyright (c) 2018 yadong.zhang
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.zyd.blog.business.service;
import com.zyd.blog.business.entity.Article;
import com.zyd.blog.business.entity.Statistics;
import java.util.List;
/**
* 统计
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @website https://www.zhyd.me
* @date 2018/4/16 16:26
* @since 1.0
*/
public interface BizStatisticsService {
/**
* 获取热门文章
*
* @return
*/
List<Article> listHotArticle(int pageSize);
/**
* 获取爬虫统计
*
* @return
*/
List<Statistics> listSpider(int pageSize);
/**
* 获取文章分类统计
*
* @return
*/
List<Statistics> listType(int pageSize);
}
...@@ -14,4 +14,6 @@ import java.io.PrintWriter; ...@@ -14,4 +14,6 @@ import java.io.PrintWriter;
public interface RemoverService { public interface RemoverService {
void run(Long typeId, BaseModel model, PrintWriter writer); void run(Long typeId, BaseModel model, PrintWriter writer);
void stop();
} }
/**
* Copyright [2016-2018] [yadong.zhang]
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zyd.blog.business.service;
import com.github.pagehelper.PageInfo;
import com.zyd.blog.business.entity.Log;
import com.zyd.blog.business.vo.LogConditionVO;
import com.zyd.blog.framework.object.AbstractService;
/**
* @author yadong.zhang email:yadong.zhang0415(a)gmail.com
* @version 1.0
* @date 2018/01/09 17:40
* @since 1.0
*/
public interface SysLogService extends AbstractService<Log, Integer> {
/**
* 分页查询
*
* @param vo
* @return
*/
PageInfo<Log> findPageBreakByCondition(LogConditionVO vo);
}
...@@ -26,10 +26,12 @@ import com.zyd.blog.business.entity.Article; ...@@ -26,10 +26,12 @@ import com.zyd.blog.business.entity.Article;
import com.zyd.blog.business.entity.User; import com.zyd.blog.business.entity.User;
import com.zyd.blog.business.enums.ArticleStatusEnum; import com.zyd.blog.business.enums.ArticleStatusEnum;
import com.zyd.blog.business.enums.QiniuUploadType; import com.zyd.blog.business.enums.QiniuUploadType;
import com.zyd.blog.business.enums.ResponseStatus;
import com.zyd.blog.business.service.BizArticleService; import com.zyd.blog.business.service.BizArticleService;
import com.zyd.blog.business.service.BizArticleTagsService; import com.zyd.blog.business.service.BizArticleTagsService;
import com.zyd.blog.business.vo.ArticleConditionVO; import com.zyd.blog.business.vo.ArticleConditionVO;
import com.zyd.blog.framework.exception.ZhydArticleException; import com.zyd.blog.framework.exception.ZhydArticleException;
import com.zyd.blog.framework.exception.ZhydException;
import com.zyd.blog.framework.holder.RequestHolder; import com.zyd.blog.framework.holder.RequestHolder;
import com.zyd.blog.persistence.beans.*; import com.zyd.blog.persistence.beans.*;
import com.zyd.blog.persistence.mapper.BizArticleLookMapper; import com.zyd.blog.persistence.mapper.BizArticleLookMapper;
...@@ -308,8 +310,12 @@ public class BizArticleServiceImpl implements BizArticleService { ...@@ -308,8 +310,12 @@ public class BizArticleServiceImpl implements BizArticleService {
article.setId(id); article.setId(id);
if ("top".equals(type)) { if ("top".equals(type)) {
article.setTop(!article.getTop()); article.setTop(!article.getTop());
} else { } else if("recommend".equals(type)) {
article.setRecommended(!article.getRecommended()); article.setRecommended(!article.getRecommended());
} else if("comment".equals(type)) {
article.setComment(!article.getComment());
} else {
throw new ZhydException(ResponseStatus.INVALID_PARAMS.getMessage());
} }
article.setUpdateTime(new Date()); article.setUpdateTime(new Date());
return bizArticleMapper.updateByPrimaryKeySelective(article) > 0; return bizArticleMapper.updateByPrimaryKeySelective(article) > 0;
......
/**
* MIT License
* Copyright (c) 2018 yadong.zhang
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.zyd.blog.business.service.impl;
import com.github.pagehelper.PageHelper;
import com.zyd.blog.business.entity.Article;
import com.zyd.blog.business.entity.Statistics;
import com.zyd.blog.business.service.BizArticleService;
import com.zyd.blog.business.service.BizStatisticsService;
import com.zyd.blog.persistence.beans.BizStatistics;
import com.zyd.blog.persistence.mapper.BizStatisticsMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.List;
/**
* 统计
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @website https://www.zhyd.me
* @date 2018/4/16 16:26
* @since 1.0
*/
@Service
public class BizStatisticsServiceImpl implements BizStatisticsService {
@Autowired
private BizStatisticsMapper statisticsMapper;
@Autowired
private BizArticleService articleService;
/**
* 获取热门文章
*
* @return
*/
@Override
public List<Article> listHotArticle(int pageSize) {
return articleService.listHotArticle(pageSize);
}
/**
* 获取爬虫统计
*
* @return
*/
@Override
public List<Statistics> listSpider(int pageSize) {
PageHelper.startPage(1, pageSize);
List<BizStatistics> entityList = statisticsMapper.listSpider();
if (CollectionUtils.isEmpty(entityList)) {
return null;
}
List<Statistics> list = new ArrayList<>();
for (BizStatistics entity : entityList) {
list.add(new Statistics(entity));
}
return list;
}
/**
* 获取文章分类统计
*
* @return
*/
@Override
public List<Statistics> listType(int pageSize) {
PageHelper.startPage(1, pageSize);
List<BizStatistics> entityList = statisticsMapper.listType();
if (CollectionUtils.isEmpty(entityList)) {
return null;
}
List<Statistics> list = new ArrayList<>();
for (BizStatistics entity : entityList) {
list.add(new Statistics(entity));
}
return list;
}
}
...@@ -19,7 +19,11 @@ ...@@ -19,7 +19,11 @@
*/ */
package com.zyd.blog.business.service.impl; package com.zyd.blog.business.service.impl;
import com.zyd.blog.business.entity.*; import com.zyd.blog.business.entity.Comment;
import com.zyd.blog.business.entity.Config;
import com.zyd.blog.business.entity.Link;
import com.zyd.blog.business.entity.MailDetail;
import com.zyd.blog.business.entity.Template;
import com.zyd.blog.business.enums.TemplateKeyEnum; import com.zyd.blog.business.enums.TemplateKeyEnum;
import com.zyd.blog.business.service.MailService; import com.zyd.blog.business.service.MailService;
import com.zyd.blog.business.service.SysConfigService; import com.zyd.blog.business.service.SysConfigService;
...@@ -28,7 +32,6 @@ import com.zyd.blog.util.FreeMarkerUtil; ...@@ -28,7 +32,6 @@ import com.zyd.blog.util.FreeMarkerUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.FileSystemResource;
import org.springframework.mail.javamail.JavaMailSender; import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.mail.javamail.MimeMessageHelper; import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
...@@ -39,10 +42,8 @@ import javax.mail.MessagingException; ...@@ -39,10 +42,8 @@ import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress; import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage; import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeUtility; import javax.mail.internet.MimeUtility;
import java.io.File;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
/** /**
...@@ -153,7 +154,11 @@ public class MailServiceImpl implements MailService { ...@@ -153,7 +154,11 @@ public class MailServiceImpl implements MailService {
map.put("link", link); map.put("link", link);
String mailContext = FreeMarkerUtil.template2String(temXml, map, true); String mailContext = FreeMarkerUtil.template2String(temXml, map, true);
String adminEmail = config.getAuthorEmail(); String adminEmail = config.getAuthorEmail();
adminEmail = StringUtils.isEmpty(adminEmail) ? "yadong.zhang0415@gmail.com" : (adminEmail.contains("#") ? adminEmail.replace("#", "@") : adminEmail); if (StringUtils.isEmpty(adminEmail)) {
log.warn("[sendToAdmin]邮件发送失败!未指定系统管理员的邮箱地址");
return;
}
adminEmail = (adminEmail.contains("#") ? adminEmail.replace("#", "@") : adminEmail);
MailDetail mailDetail = new MailDetail("有新的友链消息", adminEmail, config.getAuthorName(), mailContext); MailDetail mailDetail = new MailDetail("有新的友链消息", adminEmail, config.getAuthorName(), mailContext);
send(mailDetail); send(mailDetail);
} }
...@@ -173,53 +178,38 @@ public class MailServiceImpl implements MailService { ...@@ -173,53 +178,38 @@ public class MailServiceImpl implements MailService {
map.put("comment", comment); map.put("comment", comment);
map.put("config", config); map.put("config", config);
String mailContext = FreeMarkerUtil.template2String(temXml, map, true); String mailContext = FreeMarkerUtil.template2String(temXml, map, true);
String subject = "有新的评论消息";
String adminEmail = config.getAuthorEmail(); String adminEmail = config.getAuthorEmail();
adminEmail = StringUtils.isEmpty(adminEmail) ? "yadong.zhang0415@gmail.com" : (adminEmail.contains("#") ? adminEmail.replace("#", "@") : adminEmail); if (StringUtils.isEmpty(adminEmail)) {
MailDetail mailDetail = new MailDetail(subject, adminEmail, config.getAuthorName(), mailContext); log.warn("[sendToAdmin]邮件发送失败!未指定系统管理员的邮箱地址");
return;
}
adminEmail = (adminEmail.contains("#") ? adminEmail.replace("#", "@") : adminEmail);
MailDetail mailDetail = new MailDetail("有新的评论消息", adminEmail, config.getAuthorName(), mailContext);
send(mailDetail); send(mailDetail);
} }
private void sendMessage(MailDetail detail, String from) {
private boolean sendMessage(MailDetail detail, String from) {
log.info("Start to send html email for [{}({})]", detail.getToUsername(), detail.getToMailAddress()); log.info("Start to send html email for [{}({})]", detail.getToUsername(), detail.getToMailAddress());
if (StringUtils.isEmpty(detail.getToMailAddress())) { if (StringUtils.isEmpty(detail.getToMailAddress())) {
log.warn("邮件接收者为空!"); log.warn("邮件接收者为空!");
return false; return;
} }
MimeMessage message = null; MimeMessage message = null;
try { try {
message = javaMailSender.createMimeMessage(); message = javaMailSender.createMimeMessage();
MimeMessageHelper helper = new MimeMessageHelper(message, true); MimeMessageHelper helper = new MimeMessageHelper(message, true);
// 创建邮件发送者地址
InternetAddress fromAddress = new InternetAddress(MimeUtility.encodeText("网站管理员") + "<" + from + ">"); InternetAddress fromAddress = new InternetAddress(MimeUtility.encodeText("网站管理员") + "<" + from + ">");
helper.setFrom(fromAddress); helper.setFrom(fromAddress);
// 创建邮件接收者地址
InternetAddress toAddress = new InternetAddress(MimeUtility.encodeText(detail.getToMailAddress()) + "<" + detail.getToMailAddress() + ">"); InternetAddress toAddress = new InternetAddress(MimeUtility.encodeText(detail.getToMailAddress()) + "<" + detail.getToMailAddress() + ">");
helper.setTo(toAddress); helper.setTo(toAddress);
helper.setSubject(detail.getSubject()); helper.setSubject(detail.getSubject());
// 第二个参数指定发送的是HTML格式
helper.setText(detail.getContent(), detail.isHtml()); helper.setText(detail.getContent(), detail.isHtml());
if (detail.getCc() != null && detail.getCc().length > 0) { if (detail.getCc() != null && detail.getCc().length > 0) {
helper.setCc(detail.getCc()); helper.setCc(detail.getCc());
} }
if (detail.isExitFile()) {
try {
List<String> filePaths = detail.getFilePaths();
for (String filePath : filePaths) {
// 附件 :注意项目路径问题,自动补用项目路径
FileSystemResource file = new FileSystemResource(new File(filePath));
helper.addAttachment("图片.jpg", file);
}
} catch (Exception e) {
log.error("添加附件发生异常", e);
}
}
javaMailSender.send(message); javaMailSender.send(message);
return true;
} catch (MessagingException | UnsupportedEncodingException e) { } catch (MessagingException | UnsupportedEncodingException e) {
log.error("Failed to send E-mail. e [{}]", e.getMessage()); log.error("Failed to send E-mail. e [{}]", e.getMessage());
} }
return false;
} }
} }
...@@ -3,23 +3,35 @@ package com.zyd.blog.business.service.impl; ...@@ -3,23 +3,35 @@ package com.zyd.blog.business.service.impl;
import com.zyd.blog.business.entity.Tags; import com.zyd.blog.business.entity.Tags;
import com.zyd.blog.business.entity.User; import com.zyd.blog.business.entity.User;
import com.zyd.blog.business.enums.ArticleStatusEnum; import com.zyd.blog.business.enums.ArticleStatusEnum;
import com.zyd.blog.business.service.*; import com.zyd.blog.business.service.BizArticleService;
import com.zyd.blog.business.service.BizArticleTagsService;
import com.zyd.blog.business.service.BizTagsService;
import com.zyd.blog.business.service.RemoverService;
import com.zyd.blog.business.service.SysConfigService;
import com.zyd.blog.business.util.ImageDownloadUtil; import com.zyd.blog.business.util.ImageDownloadUtil;
import com.zyd.blog.framework.exception.ZhydException;
import com.zyd.blog.spider.model.Article; import com.zyd.blog.spider.model.Article;
import com.zyd.blog.spider.model.BaseModel; import com.zyd.blog.spider.model.BaseModel;
import com.zyd.blog.spider.processor.ArticleSpiderProcessor; import com.zyd.blog.spider.processor.ArticleSpiderProcessor;
import com.zyd.blog.spider.processor.BaseSpider; import com.zyd.blog.spider.processor.BaseSpider;
import com.zyd.blog.spider.util.WriterUtil; import com.zyd.blog.spider.util.WriterUtil;
import com.zyd.blog.spider.webmagic.ZhydSpider;
import com.zyd.blog.util.SessionUtil; import com.zyd.blog.util.SessionUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import us.codecraft.webmagic.Spider;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.*; import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
...@@ -31,7 +43,6 @@ import java.util.stream.Collectors; ...@@ -31,7 +43,6 @@ import java.util.stream.Collectors;
* @date 2018/8/21 15:38 * @date 2018/8/21 15:38
* @since 1.8 * @since 1.8
*/ */
@Slf4j
@Service @Service
public class RemoverServiceImpl implements RemoverService { public class RemoverServiceImpl implements RemoverService {
...@@ -48,22 +59,22 @@ public class RemoverServiceImpl implements RemoverService { ...@@ -48,22 +59,22 @@ public class RemoverServiceImpl implements RemoverService {
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
@Override @Override
public void run(Long typeId, BaseModel model, PrintWriter writer) { public void run(Long typeId, @Validated BaseModel model, PrintWriter writer) {
WriterUtil writerUtil = new WriterUtil(writer);
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
if (null == typeId) { if (null == typeId) {
WriterUtil.writer2Html(writer, "校验不通过!请选择文章分类......", String.format("共耗时 %s ms.", (System.currentTimeMillis() - start))); writerUtil.print("校验不通过!请选择文章分类......", String.format("共耗时 %s ms.", (System.currentTimeMillis() - start))).shutdown();
WriterUtil.shutdown(writer);
return; return;
} }
BaseSpider<Article> spider = new ArticleSpiderProcessor(model, writer); User user = SessionUtil.getUser();
List<Article> list = spider.run(); BaseSpider<Article> spider = new ArticleSpiderProcessor(model, writerUtil, user.getId());
CopyOnWriteArrayList<Article> list = spider.run();
if (CollectionUtils.isEmpty(list)) { if (CollectionUtils.isEmpty(list)) {
WriterUtil.writer2Html(writer, String.format("共耗时 %s ms.", (System.currentTimeMillis() - start))); writerUtil.print(String.format("未抓取到任何内容,请检查目标网站<a href=\"%s\" target=\"_blank\">%s</a>是否可访问!共耗时 %s ms.", model.getEntryUrls()[0], model.getEntryUrls()[0],(System.currentTimeMillis() - start))).shutdown();
WriterUtil.shutdown(writer);
return; return;
} }
WriterUtil.writer2Html(writer, "抓取完成,共抓取到【" + list.size() + "】条数据..."); writerUtil.print("=================================== Next ↓↓↓===================================");
WriterUtil.writer2Html(writer, "准备保存到数据库中..."); writerUtil.print("数据抓取完成,此次共抓取到" + list.size() + "条数据。准备持久化到数据库中");
// 获取数据库中的标签列表 // 获取数据库中的标签列表
// List<String> oldTags = tagsService.listAll().stream().map(Tags::getName).collect(toList()); // List<String> oldTags = tagsService.listAll().stream().map(Tags::getName).collect(toList());
...@@ -75,11 +86,12 @@ public class RemoverServiceImpl implements RemoverService { ...@@ -75,11 +86,12 @@ public class RemoverServiceImpl implements RemoverService {
// 处理标签,并获取最终待添加的标签id // 处理标签,并获取最终待添加的标签id
List<Long> tagIds = null; List<Long> tagIds = null;
Tags newTag = null; Tags newTag = null;
User user = SessionUtil.getUser();
String qiniuBasePath = sysConfigService.get().getQiuniuBasePath(); String qiniuBasePath = sysConfigService.get().getQiuniuBasePath();
for (Article spiderArticle : list) { for (Article spiderArticle : list) {
writerUtil.print("------------------------------------------------------------------------------------");
writerUtil.print(String.format("准备持久化 - %s", spiderArticle.getTitle()));
article = new com.zyd.blog.business.entity.Article(); article = new com.zyd.blog.business.entity.Article();
article.setContent(model.isConvertImg() ? parseImgForHtml(spiderArticle.getContent(), qiniuBasePath, writer) : spiderArticle.getContent()); article.setContent(model.isConvertImg() ? parseImgForHtml(spiderArticle.getSource(), spiderArticle.getContent(), qiniuBasePath, writerUtil) : spiderArticle.getContent());
article.setTitle(spiderArticle.getTitle()); article.setTitle(spiderArticle.getTitle());
article.setTypeId(typeId); article.setTypeId(typeId);
article.setUserId(user.getId()); article.setUserId(user.getId());
...@@ -91,10 +103,7 @@ public class RemoverServiceImpl implements RemoverService { ...@@ -91,10 +103,7 @@ public class RemoverServiceImpl implements RemoverService {
article.setDescription(spiderArticle.getDescription()); article.setDescription(spiderArticle.getDescription());
article.setKeywords(spiderArticle.getKeywords()); article.setKeywords(spiderArticle.getKeywords());
article = articleService.insert(article); article = articleService.insert(article);
WriterUtil.writer2Html(writer, String.format(" > 成功添加【%s】", article.getTitle()), " > 开始同步文章标签..."); writerUtil.print(String.format("持久化成功 --> <a href=\"%s\" target=\"_blank\">%s</a>", spiderArticle.getSource(), article.getTitle()));
// 获取代添加的标签集合
// List<String> reduce = spiderArticle.getTags().stream().filter(item -> !oldTags.contains(item)).collect(toList());
tagIds = new ArrayList<>(); tagIds = new ArrayList<>();
for (String tag : spiderArticle.getTags()) { for (String tag : spiderArticle.getTags()) {
...@@ -113,13 +122,38 @@ public class RemoverServiceImpl implements RemoverService { ...@@ -113,13 +122,38 @@ public class RemoverServiceImpl implements RemoverService {
// 添加文章-标签关联信息 // 添加文章-标签关联信息
articleTagsService.insertList(tagIds.toArray(new Long[0]), article.getId()); articleTagsService.insertList(tagIds.toArray(new Long[0]), article.getId());
WriterUtil.writer2Html(writer, " > 文章标签同步完成..."); writerUtil.print(String.format("已同步<a href=\"%s\" target=\"_blank\">%s</a>的标签!此次共同步%s个标签", spiderArticle.getSource(), article.getTitle(), tagIds.size()));
}
writerUtil.print("=================================== Next ↓↓↓===================================");
writerUtil.print(String.format("程序执行完毕!共耗时 %s ms.", (System.currentTimeMillis() - start))).shutdown();
}
@Override
public void stop() {
ZhydSpider spider = ZhydSpider.SPIDER_BUCKET.get(SessionUtil.getUser().getId());
if (null != spider) {
Spider.Status status = spider.getStatus();
if (status.equals(Spider.Status.Running)) {
spider.stop();
} else if (status.equals(Spider.Status.Init)) {
throw new ZhydException("爬虫正在初始化!");
} else {
throw new ZhydException("当前没有正在运行的爬虫!");
}
} else {
throw new ZhydException("当前没有正在运行的爬虫!");
} }
WriterUtil.writer2Html(writer, "全部跑完了~!!!...", String.format("共耗时 %s ms.", (System.currentTimeMillis() - start)));
WriterUtil.shutdown(writer);
} }
private String parseImgForHtml(String html, String qiniuBasePath, PrintWriter writer) { /**
* 解析Html中的img标签,将图片转存到七牛云
*
* @param referer 为了预防某些网站做了权限验证,不加referer可能会403
* @param html 待解析的html
* @param qiniuBasePath 七牛的根路径,在config表中配置
* @param writerUtil 打印输出的工具类
*/
private String parseImgForHtml(String referer, String html, String qiniuBasePath, WriterUtil writerUtil) {
if (StringUtils.isEmpty(html)) { if (StringUtils.isEmpty(html)) {
return null; return null;
} }
...@@ -130,15 +164,15 @@ public class RemoverServiceImpl implements RemoverService { ...@@ -130,15 +164,15 @@ public class RemoverServiceImpl implements RemoverService {
imgUrlSet.add(imgUrl); imgUrlSet.add(imgUrl);
} }
if (!CollectionUtils.isEmpty(imgUrlSet)) { if (!CollectionUtils.isEmpty(imgUrlSet)) {
WriterUtil.writer2Html(writer, " > 开始转存图片到七牛云..."); writerUtil.print(String.format("检测到存在%s张外链图片,开始转存图片到七牛云...", imgUrlSet.size()));
for (String imgUrl : imgUrlSet) { for (String imgUrl : imgUrlSet) {
String qiniuImgPath = ImageDownloadUtil.convertToQiniu(imgUrl); String qiniuImgPath = ImageDownloadUtil.convertToQiniu(imgUrl, referer);
if (StringUtils.isEmpty(qiniuImgPath)) { if (StringUtils.isEmpty(qiniuImgPath)) {
WriterUtil.writer2Html(writer, " >> 图片转存失败,请确保七牛云以配置完毕!请查看控制台详细错误信息..."); writerUtil.print(" * 图片转存失败,请确保七牛云以配置完毕!请查看控制台详细错误信息...");
continue; continue;
} }
html = html.replaceAll(imgUrl, qiniuBasePath + qiniuImgPath); html = html.replace(imgUrl, qiniuBasePath + qiniuImgPath);
WriterUtil.writer2Html(writer, String.format(" >> <a href=\"%s\" target=\"_blank\">原图片</a> convert to <a href=\"%s\" target=\"_blank\">七牛云</a>...", imgUrl, qiniuImgPath)); writerUtil.print(String.format("<a href=\"%s\" target=\"_blank\">原图片</a> convert to <a href=\"%s%s\" target=\"_blank\">七牛云</a>...", imgUrl, qiniuBasePath, qiniuImgPath));
} }
} }
return html; return html;
......
...@@ -20,9 +20,9 @@ ...@@ -20,9 +20,9 @@
package com.zyd.blog.business.service.impl; package com.zyd.blog.business.service.impl;
import com.zyd.blog.business.annotation.RedisCache; import com.zyd.blog.business.annotation.RedisCache;
import com.zyd.blog.business.consts.DateConst;
import com.zyd.blog.business.entity.Config; import com.zyd.blog.business.entity.Config;
import com.zyd.blog.business.service.SysConfigService; import com.zyd.blog.business.service.SysConfigService;
import com.zyd.blog.framework.property.AppProperties;
import com.zyd.blog.persistence.beans.SysConfig; import com.zyd.blog.persistence.beans.SysConfig;
import com.zyd.blog.persistence.mapper.SysConfigMapper; import com.zyd.blog.persistence.mapper.SysConfigMapper;
import com.zyd.blog.util.DateUtil; import com.zyd.blog.util.DateUtil;
...@@ -48,6 +48,8 @@ public class SysConfigServiceImpl implements SysConfigService { ...@@ -48,6 +48,8 @@ public class SysConfigServiceImpl implements SysConfigService {
@Autowired @Autowired
private SysConfigMapper sysConfigMapper; private SysConfigMapper sysConfigMapper;
@Autowired
private AppProperties properties;
/** /**
* 获取系统配置 * 获取系统配置
...@@ -108,13 +110,12 @@ public class SysConfigServiceImpl implements SysConfigService { ...@@ -108,13 +110,12 @@ public class SysConfigServiceImpl implements SysConfigService {
public Map<String, Object> getSiteInfo() { public Map<String, Object> getSiteInfo() {
Map<String, Object> map = sysConfigMapper.getSiteInfo(); Map<String, Object> map = sysConfigMapper.getSiteInfo();
if (!CollectionUtils.isEmpty(map)) { if (!CollectionUtils.isEmpty(map)) {
Date recordeTime = (Date) map.get("recordeTime"); Date lastUpdateTime = (Date) map.get("lastUpdateTime");
if (!StringUtils.isEmpty(recordeTime)) { if (!StringUtils.isEmpty(lastUpdateTime)) {
map.put("recordeTime", DateUtil.date2Str(recordeTime, "yyyy年MM月dd日HH点")); map.put("lastUpdateTime", DateUtil.date2Str(lastUpdateTime, "yyyy年MM月dd日HH点"));
} }
Date buildSiteDate = DateUtil.str2Date("2016-10-27 00:00:00", DateConst.YYYY_MM_DD_HH_MM_SS_EN);
// 获取建站天数 // 获取建站天数
map.put("buildSiteDate", DateUtil.getGapDay(buildSiteDate, new Date())); map.put("buildSiteDate", DateUtil.getGapDay(properties.getBuildWebsiteDate(), new Date()));
} }
return map; return map;
} }
......
/**
* Copyright [2016-2018] [yadong.zhang]
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zyd.blog.business.service.impl;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.zyd.blog.business.entity.Log;
import com.zyd.blog.business.service.SysLogService;
import com.zyd.blog.business.vo.LogConditionVO;
import com.zyd.blog.persistence.beans.SysLog;
import com.zyd.blog.persistence.mapper.SysLogMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* @author yadong.zhang email:yadong.zhang0415(a)gmail.com
* @version 1.0
* @date 2018/01/09 17:50
* @since 1.0
*/
@Service
public class SysLogServiceImpl implements SysLogService {
@Autowired
private SysLogMapper sysLogMapper;
@Override
public PageInfo<Log> findPageBreakByCondition(LogConditionVO vo){
PageHelper.startPage(vo.getPageNumber(),vo.getPageSize());
List<SysLog>list=sysLogMapper.findPageBreakByCondition(vo);
if (CollectionUtils.isEmpty(list)) {
return null;
}
List<Log> boList = new ArrayList<>();
for(SysLog sysLog : list){
boList.add(new Log(sysLog));
}
PageInfo bean = new PageInfo<SysLog>(list);
bean.setList(boList);
return bean;
}
@Override
@Transactional(rollbackFor = Exception.class)
public Log insert(Log entity){
Assert.notNull(entity, "Log不可为空!");
entity.setUpdateTime(new Date());
entity.setCreateTime(new Date());
sysLogMapper.insertSelective(entity.getSysLog());
return entity;
}
@Override
public void insertList(List<Log> entities){
Assert.notNull(entities, "Logs不可为空!");
List<SysLog> list = new ArrayList<>();
for (Log entity : entities) {
entity.setUpdateTime(new Date());
entity.setCreateTime(new Date());
list.add(entity.getSysLog());
}
sysLogMapper.insertList(list);
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean removeByPrimaryKey(Integer primaryKey){
return sysLogMapper.deleteByPrimaryKey(primaryKey) > 0;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean update(Log entity){
Assert.notNull(entity, "Log不可为空!");
entity.setUpdateTime(new Date());
return sysLogMapper.updateByPrimaryKey(entity.getSysLog()) > 0;
}
@Override
@Transactional(rollbackFor = Exception.class)
public boolean updateSelective(Log entity){
Assert.notNull(entity, "Log不可为空!");
entity.setUpdateTime(new Date());
return sysLogMapper.updateByPrimaryKeySelective(entity.getSysLog()) > 0;
}
@Override
public Log getByPrimaryKey(Integer primaryKey){
Assert.notNull(primaryKey, "PrimaryKey不可为空!");
SysLog entity = sysLogMapper.selectByPrimaryKey(primaryKey);
return null == entity ? null : new Log(entity);
}
@Override
public Log getOneByEntity(Log entity){
Assert.notNull(entity, "Log不可为空!");
SysLog bo = sysLogMapper.selectOne(entity.getSysLog());
return null == bo ? null : new Log(bo);
}
@Override
public List<Log> listAll(){
List<SysLog> entityList = sysLogMapper.selectAll();
if (CollectionUtils.isEmpty(entityList)) {
return null;
}
List<Log> list = new ArrayList<>();
for (SysLog entity : entityList) {
list.add(new Log(entity));
}
return list;
}
@Override
public List<Log> listByEntity(Log entity){
Assert.notNull(entity, "Log不可为空!");
List<SysLog> entityList = sysLogMapper.select(entity.getSysLog());
if (CollectionUtils.isEmpty(entityList)) {
return null;
}
List<Log> list = new ArrayList<>();
for (SysLog po : entityList) {
list.add(new Log(po));
}
return list;
}
}
...@@ -2,7 +2,6 @@ package com.zyd.blog.business.util; ...@@ -2,7 +2,6 @@ package com.zyd.blog.business.util;
import com.zyd.blog.business.enums.QiniuUploadType; import com.zyd.blog.business.enums.QiniuUploadType;
import com.zyd.blog.plugin.QiniuApi; import com.zyd.blog.plugin.QiniuApi;
import com.zyd.blog.util.FileUtil;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.CloseableHttpResponse;
...@@ -10,7 +9,13 @@ import org.apache.http.client.methods.HttpGet; ...@@ -10,7 +9,13 @@ import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.client.HttpClients;
import java.io.*; import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.UUID; import java.util.UUID;
@Slf4j @Slf4j
...@@ -19,12 +24,13 @@ public class ImageDownloadUtil { ...@@ -19,12 +24,13 @@ public class ImageDownloadUtil {
/** /**
* 将网络图片转存到七牛云 * 将网络图片转存到七牛云
* *
* @param imgUrl 网络图片地址 * @param imgUrl 网络图片地址
* @param referer 为了预防某些网站做了权限验证,不加referer可能会403
*/ */
public static String convertToQiniu(String imgUrl) { public static String convertToQiniu(String imgUrl, String referer) {
log.debug("download img >> %s", imgUrl); log.debug("download img >> %s", imgUrl);
String qiniuImgPath = null; String qiniuImgPath = null;
try (InputStream is = getInputStreamByUrl(checkUrl(imgUrl)); try (InputStream is = getInputStreamByUrl(imgUrl, referer);
ByteArrayOutputStream outStream = new ByteArrayOutputStream();) { ByteArrayOutputStream outStream = new ByteArrayOutputStream();) {
byte[] buffer = new byte[1024]; byte[] buffer = new byte[1024];
int len = 0; int len = 0;
...@@ -35,30 +41,23 @@ public class ImageDownloadUtil { ...@@ -35,30 +41,23 @@ public class ImageDownloadUtil {
.withFileName("temp." + getSuffixByUrl(imgUrl), QiniuUploadType.SIMPLE) .withFileName("temp." + getSuffixByUrl(imgUrl), QiniuUploadType.SIMPLE)
.upload(outStream.toByteArray()); .upload(outStream.toByteArray());
} catch (Exception e) { } catch (Exception e) {
log.error("Error.", e); log.error(e.getMessage(), e);
} }
return qiniuImgPath; return qiniuImgPath;
} }
private static String getSuffixByUrl(String imgUrl) {
String defaultSuffix = "png";
if (StringUtils.isEmpty(imgUrl)) {
return defaultSuffix;
}
String temStr = imgUrl.substring(imgUrl.lastIndexOf("/"));
int index = temStr.lastIndexOf(".");
return -1 == index ? defaultSuffix : temStr.substring(index + 1);
}
/** /**
* 下载网络图片到本地<br>暂时不用,只供测试
*
* @param imgUrl 网络图片地址 * @param imgUrl 网络图片地址
* @param referer 为了预防某些网站做了权限验证,不加referer可能会403
* @param localPath 待保存的本地地址 * @param localPath 待保存的本地地址
*/ */
public static String download(String imgUrl, String localPath) { @Deprecated
log.debug("download img >> %s", imgUrl); public static String download(String imgUrl, String referer, String localPath) {
String fileName = localPath + File.separator + UUID.randomUUID().toString() + "." + getSuffixByUrl(imgUrl); String fileName = localPath + File.separator + UUID.randomUUID().toString() + "." + getSuffixByUrl(imgUrl);
try (InputStream is = getInputStreamByUrl(checkUrl(imgUrl)); try (InputStream is = getInputStreamByUrl(imgUrl, referer);
FileOutputStream fos = new FileOutputStream(fileName)) { FileOutputStream fos = new FileOutputStream(fileName)) {
if (null == is) { if (null == is) {
return null; return null;
...@@ -79,9 +78,22 @@ public class ImageDownloadUtil { ...@@ -79,9 +78,22 @@ public class ImageDownloadUtil {
return fileName; return fileName;
} }
private static InputStream getInputStreamByUrl(String url) { private static String getSuffixByUrl(String imgUrl) {
HttpGet httpGet = new HttpGet(url); String defaultSuffix = "png";
httpGet.setHeader("user-agent", "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.146 Safari/537.36"); if (StringUtils.isEmpty(imgUrl)) {
return defaultSuffix;
}
String temStr = imgUrl.substring(imgUrl.lastIndexOf("/"));
int index = temStr.lastIndexOf(".");
return -1 == index ? defaultSuffix : temStr.substring(index + 1);
}
private static InputStream getInputStreamByUrl(String url, String referer) {
HttpGet httpGet = new HttpGet(checkUrl(url));
httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36");
if (StringUtils.isNotEmpty(referer)) {
httpGet.setHeader("referer", referer);
}
CloseableHttpClient httpclient = HttpClients.createDefault(); CloseableHttpClient httpclient = HttpClients.createDefault();
CloseableHttpResponse response = null; CloseableHttpResponse response = null;
InputStream in = null; InputStream in = null;
...@@ -95,8 +107,7 @@ public class ImageDownloadUtil { ...@@ -95,8 +107,7 @@ public class ImageDownloadUtil {
return null; return null;
} }
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); log.error(e.getMessage(), e);
log.error("Error.", e);
} }
return in; return in;
} }
......
package com.zyd.blog.business.util;
import com.zyd.blog.business.enums.CachePrefixEnum;
import com.zyd.blog.framework.holder.SpringContextHolder;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.util.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @date 2018/5/7 11:25
* @since 1.0
*/
public class WebSpiderUtils {
private static final String KEY = CachePrefixEnum.SPIDER.getPrefix() + "list";
public static String parseUa(String ua) {
if (StringUtils.isEmpty(ua)) {
return null;
}
Map<String, String> spider = listSpider();
for (Map.Entry<String, String> entry : spider.entrySet()) {
String spiderSign = entry.getKey();// 爬虫的标记
if (ua.contains(spiderSign) || ua.equalsIgnoreCase(spiderSign) || ua.toLowerCase().contains(spiderSign.toLowerCase())) {
return entry.getValue();
}
}
return null;
}
private static Map<String, String> listSpider() {
RedisTemplate redisTemplate = (RedisTemplate) SpringContextHolder.getBean("redisTemplate");
ValueOperations<String, Map<String, String>> operations = redisTemplate.opsForValue();
if (redisTemplate.hasKey(KEY)) {
return operations.get(KEY);
}
Map<String, String> spider = new HashMap<>();
spider.put("Baiduspider-image", "百度图片搜索");
spider.put("Baiduspider-video", "百度视频搜索");
spider.put("Baiduspider-news", "百度新闻搜索");
spider.put("Baiduspider-favo", "百度搜藏");
spider.put("Baiduspider-cpro", "百度联盟");
spider.put("Baiduspider-sfkr", "百度竞价蜘蛛");
spider.put("Baiduspider-ads", "百度商务搜索");
spider.put("Baidu-YunGuanCe", "百度云观测");
spider.put("Baiduspider", "百度");
spider.put("www.baidu.com", "百度");
spider.put("Google Web Preview", "谷歌");
spider.put("Google Search Console", "谷歌站长工具");
spider.put("Google-Site-Verification", "谷歌站长验证");
spider.put("Googlebot-Mobile", "谷歌手机搜索");
spider.put("Googlebot-Image", "谷歌图片搜索");
spider.put("AppEngine-Google", "谷歌");
spider.put("Mediapartners", "谷歌");
spider.put("FeedBurner", "谷歌");
spider.put("Googlebot", "谷歌");
spider.put("Google", "谷歌");
spider.put("google.com", "谷歌");
spider.put("YoudaoBot", "网易有道");
spider.put("YodaoBot", "网易有道");
spider.put("360Spider", "360");
spider.put("bingbot", "必应");
spider.put("Yahoo", "雅虎");
spider.put("Sosospider", "腾讯搜搜");
spider.put("Sosoimagespider", "搜索图片");
spider.put("Sogou", "搜狗蜘蛛");
spider.put("msnbot", "MSN蜘蛛");
spider.put("YisouSpider", "一搜蜘蛛");
spider.put("ia_archiver", "Alexa蜘蛛");
spider.put("EasouSpider", "宜sou蜘蛛");
spider.put("JikeSpider", "即刻蜘蛛");
spider.put("EtaoSpider", "一淘网蜘蛛");
spider.put("AdsBot", "Adwords");
spider.put("Speedy", "entireweb");
spider.put("YandexBot", "YandexBot");
spider.put("AhrefsBot", "AhrefsBot");
spider.put("ezooms.bot", "ezooms.bot");
spider.put("Java", "Java程序");
spider.put("Mnogosearch", "MnoGoSearch搜索引擎(PHP)");
spider.put("Morfeus Fucking Scanner", "PHP漏洞扫描器");
spider.put("project25499", "Project 25499扫描器");
spider.put("25499", "Project 25499扫描器");
spider.put("James BOT", "JamesBOT搜索引擎");
spider.put("cognitiveseo", "JamesBOT搜索引擎");
spider.put("Iframely", "URL Meta Debugger插件");
spider.put("muhstik-scan", "僵尸网络-挖矿软件");
spider.put("muhstik", "僵尸网络-挖矿软件");
spider.put("SEMrushBot", "站点分析蜘蛛");// 可屏蔽
spider.put("python-requests", "python爬虫");
spider.put("python", "python爬虫");
spider.put("Test Certificate Info", "测试证书信息");
spider.put("w3m/0.5.3+git20180125", "w3m");
spider.put("wget", "wget");
spider.put("gnu.org/gnu/wget", "wget");
spider.put("WinHTTP", "WinHTTP");
spider.put("WordPress", "WordPress");
spider.put("Xenu Link Sleuth", "死链接检测工具");
operations.set(KEY, spider, 7, TimeUnit.DAYS);
return spider;
}
}
/**
* Copyright [2016-2018] [yadong.zhang]
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zyd.blog.business.vo;
import com.zyd.blog.framework.object.BaseConditionVO;
import lombok.Getter;
import lombok.Setter;
/**
* @author yadong.zhang email:yadong.zhang0415(a)gmail.com
* @version 1.0
* @date 2018/01/09 17:40
* @since 1.0
*/
@Getter
@Setter
public class LogConditionVO extends BaseConditionVO {
private Long userId;
private String logLevel;
private String type;
private Boolean spider;
}
/** /**
* MIT License * MIT License
* * <p>
* Copyright (c) 2018 yadong.zhang * Copyright (c) 2018 yadong.zhang
* * <p>
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* * <p>
* The above copyright notice and this permission notice shall be included in all * The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software. * copies or substantial portions of the Software.
* * <p>
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
...@@ -23,10 +23,16 @@ ...@@ -23,10 +23,16 @@
*/ */
package com.zyd.blog.framework.property; package com.zyd.blog.framework.property;
import com.zyd.blog.business.consts.DateConst;
import lombok.Data; import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
/** /**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com) * @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0 * @version 1.0
...@@ -39,10 +45,15 @@ import org.springframework.stereotype.Component; ...@@ -39,10 +45,15 @@ import org.springframework.stereotype.Component;
@Data @Data
public class AppProperties { public class AppProperties {
public Boolean enableKaptcha; /**
* 是否启用验证码
*/
public boolean enableKaptcha = false;
public boolean getEnableKaptcha() { /*
return null == enableKaptcha ? false : enableKaptcha; * 创建网站的时间,用于计算已建站的天数,默认为2018-01-01
} */
@DateTimeFormat(pattern = DateConst.YYYY_MM_DD_HH_MM_SS_EN)
private Date buildWebsiteDate = Date.from(LocalDate.of(2018,1,1).atStartOfDay(ZoneId.systemDefault()).toInstant());
} }
/**
* MIT License
* Copyright (c) 2018 yadong.zhang
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.zyd.blog.persistence.beans;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.persistence.Transient;
/**
* 统计
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @website https://www.zhyd.me
* @date 2018/4/16 16:26
* @since 1.0
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class BizStatistics {
@Transient
private String name;
@Transient
private Integer value;
}
/**
* Copyright [2016-2018] [yadong.zhang]
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zyd.blog.persistence.beans;
import com.zyd.blog.framework.object.AbstractDO;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* @author yadong.zhang email:yadong.zhang0415(a)gmail.com
* @version 1.0
* @date 2018/01/09 17:36
* @since 1.0
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class SysLog extends AbstractDO {
private Long userId;
private String logLevel;
private String ip;
private String content;
private String type;
private String ua;
private String os;
private String browser;
private String spiderType;
private String requestUrl;
private String referer;
}
/**
* MIT License
*
* Copyright (c) 2018 yadong.zhang
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.zyd.blog.persistence.mapper;
import com.zyd.blog.persistence.beans.BizStatistics;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* 统计
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @website https://www.zhyd.me
* @version 1.0
* @date 2018/4/16 16:26
* @since 1.0
*/
@Repository
public interface BizStatisticsMapper {
List<BizStatistics> listSpider();
List<BizStatistics> listType();
}
/**
* MIT License
*
* Copyright (c) 2018 yadong.zhang
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package com.zyd.blog.persistence.mapper;
import com.zyd.blog.business.vo.LogConditionVO;
import com.zyd.blog.persistence.beans.SysLog;
import com.zyd.blog.plugin.BaseMapper;
import org.springframework.stereotype.Repository;
import java.util.List;
/**
* @author yadong.zhang email:yadong.zhang0415(a)gmail.com
* @version 1.0
* @date 2018/01/09 17:45
* @since 1.0
*/
@Repository
public interface SysLogMapper extends BaseMapper<SysLog> {
/**
* 分页查询
* @param vo
*
* @return
*/
List<SysLog> findPageBreakByCondition(LogConditionVO vo);
}
<?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.zyd.blog.persistence.mapper.BizStatisticsMapper">
<resultMap id="rm" type="com.zyd.blog.persistence.beans.BizStatistics">
<result property="name" jdbcType="VARCHAR" column="name"/>
<result property="value" jdbcType="INTEGER" column="name_value"/>
</resultMap>
<select id="listSpider" parameterType="Integer" resultMap="rm">
SELECT
l.spider_type AS 'name',
COUNT( l.id ) AS name_value
FROM
sys_log l
WHERE
l.spider_type IS NOT NULL
GROUP BY
l.spider_type
ORDER BY
name_value DESC
</select>
<select id="listType" parameterType="Integer" resultMap="rm">
SELECT
bt.`name` AS 'name',
count( ba.id ) AS name_value
FROM
biz_type bt
INNER JOIN biz_article ba ON bt.id = ba.type_id
GROUP BY
ba.type_id
ORDER BY
name_value DESC
</select>
</mapper>
...@@ -71,7 +71,7 @@ ...@@ -71,7 +71,7 @@
) )
</if> </if>
ORDER BY ORDER BY
t.sort ASC parent.sort ASC, t.sort ASC
</select> </select>
<select id="listParent" resultMap="rm"> <select id="listParent" resultMap="rm">
......
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
t.tag_count AS tagCount, t.tag_count AS tagCount,
type.type_count AS typeCount, type.type_count AS typeCount,
c.comment_count AS commentCount, c.comment_count AS commentCount,
u.recorde_time AS recordeTime u.recorde_time AS lastUpdateTime
FROM FROM
( (
SELECT SELECT
......
...@@ -21,13 +21,29 @@ public class ImageDownloadUtilTest { ...@@ -21,13 +21,29 @@ public class ImageDownloadUtilTest {
@Test @Test
public void imoocTest() { public void imoocTest() {
String html = "<p>目前,该功能已内置了三个平台(imooc、csdn和iteye),根据不同的平台,程序已默认了一套抓取规则,如下图系列<br><img title=\"DBlog开源博客新增博客迁移功能(支持多个站点)_\"图片2=\"\"src=\"//img.mukewang.com/5b7fd07c000125ed18090932.png\"alt=\"图片描述\"style=\"cursor: pointer;\"><br><img title=\"DBlog开源博客新增博客迁移功能(支持多个站点)_\"图片3=\"\"src=\"//img.mukewang.com/5b7fd0870001dce917490934.png\"alt=\"图片描述\"style=\"cursor: pointer;\"><br><img title=\"DBlog开源博客新增博客迁移功能(支持多个站点)_\"图片4=\"\"src=\"//img.mukewang.com/5b7fd08d000190f617610917.png\"alt=\"图片描述\"style=\"cursor: pointer;\"><br><img title=\"DBlog开源博客新增博客迁移功能(支持多个站点)_\"图片5=\"\"src=\"//img.mukewang.com/5b7fd0940001b6c317440936.png\"alt=\"图片描述\"style=\"cursor: pointer;\"></p>"; String html = "<p>目前,该功能已内置了三个平台(imooc、csdn和iteye),根据不同的平台,程序已默认了一套抓取规则,如下图系列<br><img title=\"DBlog开源博客新增博客迁移功能(支持多个站点)_\"图片2=\"\"src=\"//img.mukewang.com/5b7fd07c000125ed18090932.png\"alt=\"图片描述\"style=\"cursor: pointer;\"><br><img title=\"DBlog开源博客新增博客迁移功能(支持多个站点)_\"图片3=\"\"src=\"//img.mukewang.com/5b7fd0870001dce917490934.png\"alt=\"图片描述\"style=\"cursor: pointer;\"><br><img title=\"DBlog开源博客新增博客迁移功能(支持多个站点)_\"图片4=\"\"src=\"//img.mukewang.com/5b7fd08d000190f617610917.png\"alt=\"图片描述\"style=\"cursor: pointer;\"><br><img title=\"DBlog开源博客新增博客迁移功能(支持多个站点)_\"图片5=\"\"src=\"//img.mukewang.com/5b7fd0940001b6c317440936.png\"alt=\"图片描述\"style=\"cursor: pointer;\"></p>";
parseImgForHtml(html); parseImgForHtml(html, null);
} }
@Test @Test
public void iteyeTest() { public void iteyeTest() {
String html = "<p>&nbsp;&nbsp;&nbsp;前段时间在项目中用到了上传头像,并且获取剪切后的头像功能,单一的上传头像很好处理,直接把改文件上传就可以,但是剪切后的头像,它的src却是一个base64字符串,如图:<br><img alt=\"\"src=\"http://dl2.iteye.com/upload/attachment/0109/3648/857431ce-8d0a-35b7-bdee-e3facc7bd0b6.png\"title=\"点击查看原始大小图片\"class=\"magplus\"width=\"699\"height=\"650\"><br>&nbsp;,直接将这个地址当做文件路径上传到后台肯定不行,因为java无法编译改地址,不能识别为一个图片路径。那么,这就用到了对base64位字符串进行解码处理,将其解析为一个可被正确识别的文件。</p>"; String html = "<p>&nbsp;&nbsp;&nbsp;前段时间在项目中用到了上传头像,并且获取剪切后的头像功能,单一的上传头像很好处理,直接把改文件上传就可以,但是剪切后的头像,它的src却是一个base64字符串,如图:<br><img alt=\"\"src=\"http://dl2.iteye.com/upload/attachment/0109/3648/857431ce-8d0a-35b7-bdee-e3facc7bd0b6.png\"title=\"点击查看原始大小图片\"class=\"magplus\"width=\"699\"height=\"650\"><br>&nbsp;,直接将这个地址当做文件路径上传到后台肯定不行,因为java无法编译改地址,不能识别为一个图片路径。那么,这就用到了对base64位字符串进行解码处理,将其解析为一个可被正确识别的文件。</p>";
parseImgForHtml(html); parseImgForHtml(html, null);
}
@Test
public void csdnTest() {
String html = "<p>视频如下图所示(点击下图查看视频示例): <br>\n" +
"<a href=\"https://gitee.com/yadong.zhang/static/raw/master/dblog/DBlog-%E6%96%87%E7%AB%A0%E6%90%AC%E8%BF%90%E5%B7%A5%E7%A4%BA%E4%BE%8B.webm\" rel=\"nofollow\" target=\"_blank\"><img src=\"https://images.gitee.com/uploads/images/2018/0824/161625_250da176_784199.png\" alt=\"输入图片说明\" title=\"屏幕截图.png\"></a></p><p>使用时,只需要手动指定以下几项配置即可 <br>\n" +
"<img src=\"https://img-blog.csdn.net/20180824171459468?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3UwMTExOTc0NDg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70\" alt=\"这里写图片描述\" title=\"\"> <br>\n" +
"其中,这四处配置含义如下:</p>";
parseImgForHtml(html, "https://blog.csdn.net/u011197448/article/details/82022098");
}
@Test
public void giteeTest() {
String html = "<p>视频如下图所示(点击下图查看视频示例): <br>\n" +
"<a href=\"https://gitee.com/yadong.zhang/static/raw/master/dblog/DBlog-%E6%96%87%E7%AB%A0%E6%90%AC%E8%BF%90%E5%B7%A5%E7%A4%BA%E4%BE%8B.webm\" rel=\"nofollow\" target=\"_blank\"><img src=\"https://images.gitee.com/uploads/images/2018/0824/161625_250da176_784199.png\" alt=\"输入图片说明\" title=\"屏幕截图.png\"></a></p>";
parseImgForHtml(html, null);
} }
/** /**
...@@ -36,7 +52,7 @@ public class ImageDownloadUtilTest { ...@@ -36,7 +52,7 @@ public class ImageDownloadUtilTest {
* @param html * @param html
* @return * @return
*/ */
private void parseImgForHtml(String html) { private void parseImgForHtml(String html, String referer) {
Pattern p = Pattern.compile("<img[^>]+src\\s*=\\s*['\"]([^'\"]+)['\"][^>]*>"); Pattern p = Pattern.compile("<img[^>]+src\\s*=\\s*['\"]([^'\"]+)['\"][^>]*>");
Matcher m = null; Matcher m = null;
m = p.matcher(html); m = p.matcher(html);
...@@ -48,8 +64,9 @@ public class ImageDownloadUtilTest { ...@@ -48,8 +64,9 @@ public class ImageDownloadUtilTest {
System.out.println(html); System.out.println(html);
if (!CollectionUtils.isEmpty(imgUrlSet)) { if (!CollectionUtils.isEmpty(imgUrlSet)) {
for (String imgUrl : imgUrlSet) { for (String imgUrl : imgUrlSet) {
System.out.println(imgUrl);
String filePath = "D://var/tmp/"; String filePath = "D://var/tmp/";
String localPath = ImageDownloadUtil.download(imgUrl, filePath); String localPath = ImageDownloadUtil.download(imgUrl, referer, filePath);
html = html.replaceAll(imgUrl, localPath); html = html.replaceAll(imgUrl, localPath);
} }
} }
......
...@@ -41,7 +41,7 @@ ...@@ -41,7 +41,7 @@
<dependency> <dependency>
<groupId>org.jsoup</groupId> <groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId> <artifactId>jsoup</artifactId>
<version>1.7.2</version> <version>1.10.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.hibernate.validator</groupId> <groupId>org.hibernate.validator</groupId>
......
package com.zyd.blog.spider.enums;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @website https://www.zhyd.me
* @date 2018/7/31 17:20
*/
public enum ExitWayEnum {
/**
* 默认方式,直到程序将所有url抓取完成才会退出
*/
DEFAULT("默认", 0),
/*
* 持续时间
*/
DURATION("持续时间(s)", 60),
/**
* 抓取的条数
*/
URL_COUNT("链接条数", 10);
private String desc;
private int defaultCount;
ExitWayEnum(String desc, int defaultCount) {
this.desc = desc;
this.defaultCount = defaultCount;
}
public String getDesc() {
return desc;
}
public int getDefaultCount() {
return defaultCount;
}
}
package com.zyd.blog.spider.enums;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @website https://www.zhyd.me
* @date 2018/9/26 14:21
* @since 1.8
*/
public enum UserAgentEnum {
PC("Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36"),
MOBILE("Mozilla/5.0 (Linux; U; Android 4.0; en-us; GT-I9300 Build/IMM76D) AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30");
private String ua;
UserAgentEnum(String ua) {
this.ua = ua;
}
public String getUa() {
return ua;
}
}
package com.zyd.blog.spider.model; package com.zyd.blog.spider.model;
import com.zyd.blog.spider.enums.ExitWayEnum;
import com.zyd.blog.spider.enums.UserAgentEnum;
import lombok.Data; import lombok.Data;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import us.codecraft.webmagic.proxy.Proxy;
import javax.validation.constraints.Max; import javax.validation.constraints.Max;
import javax.validation.constraints.Min; import javax.validation.constraints.Min;
...@@ -13,8 +16,8 @@ import java.util.Map; ...@@ -13,8 +16,8 @@ import java.util.Map;
/** /**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com) * @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @website https://www.zhyd.me
* @version 1.0 * @version 1.0
* @website https://www.zhyd.me
* @date 2018/7/23 13:33 * @date 2018/7/23 13:33
*/ */
@Data @Data
...@@ -64,27 +67,31 @@ public class BaseModel { ...@@ -64,27 +67,31 @@ public class BaseModel {
private String[] entryUrls; private String[] entryUrls;
/** /**
* 退出方式{1:等待时间(waitTime必填),2:抓取到的url数量(urlCount必填)} * 退出方式{DURATION:爬虫持续的时间,URL_COUNT:抓取到的url数量}
*/ */
private int exitWay = 1; private String exitWay = ExitWayEnum.URL_COUNT.toString();
/** /**
* 单位:秒 * 对应退出方式,当exitWay = URL_COUNT时,该值表示url数量,当exitWay = DURATION时,该值表示爬虫持续的时间
*/ */
private int waitTime = 60; private int count;
private int urlCount = 100;
private List<Cookie> cookies = new ArrayList<>(); private List<Cookie> cookies = new ArrayList<>();
private Map<String, String> headers = new HashMap<>(); private Map<String, String> headers = new HashMap<>();
private String ua = "Mozilla/5.0 (ozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36"; private String ua = UserAgentEnum.PC.getUa();
private String uid; private String uid;
private Integer totalPage; // private Integer totalPage;
/* 保留字段,针对ajax渲染的页面 */ /* 保留字段,针对ajax渲染的页面 */
private Boolean ajaxRequest = false; private Boolean ajaxRequest = false;
/* 是否转存图片 */ /* 是否转存图片 */
private boolean convertImg = false; private boolean convertImg = false;
private List<Proxy> proxyList = new ArrayList<>();
/* 是否开启自动代理,开启时将会自动获取代理ip */
private ProxyType proxyType = ProxyType.CUSTOM;
public String getUid() { public String getUid() {
return uid; return uid;
} }
...@@ -94,15 +101,6 @@ public class BaseModel { ...@@ -94,15 +101,6 @@ public class BaseModel {
return this; return this;
} }
public Integer getTotalPage() {
return totalPage;
}
public BaseModel setTotalPage(Integer totalPage) {
this.totalPage = totalPage;
return this;
}
public BaseModel setTitleRegex(String titleRegex) { public BaseModel setTitleRegex(String titleRegex) {
this.titleRegex = titleRegex; this.titleRegex = titleRegex;
return this; return this;
...@@ -113,6 +111,15 @@ public class BaseModel { ...@@ -113,6 +111,15 @@ public class BaseModel {
return this; return this;
} }
// public Integer getTotalPage() {
// return totalPage;
// }
// public BaseModel setTotalPage(Integer totalPage) {
// this.totalPage = totalPage;
// return this;
// }
public BaseModel setReleaseDateRegex(String releaseDateRegex) { public BaseModel setReleaseDateRegex(String releaseDateRegex) {
this.releaseDateRegex = releaseDateRegex; this.releaseDateRegex = releaseDateRegex;
return this; return this;
...@@ -180,13 +187,18 @@ public class BaseModel { ...@@ -180,13 +187,18 @@ public class BaseModel {
return this; return this;
} }
public BaseModel setExitWay(int exitWay) { public BaseModel setExitWay(String exitWay) {
this.exitWay = exitWay; this.exitWay = exitWay;
return this; return this;
} }
public BaseModel setWaitTime(int waitTime) { public BaseModel setExitWay(ExitWayEnum exitWay) {
this.waitTime = waitTime; this.exitWay = exitWay.toString();
return this;
}
public BaseModel setCount(int count) {
this.count = count;
return this; return this;
} }
...@@ -232,4 +244,42 @@ public class BaseModel { ...@@ -232,4 +244,42 @@ public class BaseModel {
this.ajaxRequest = ajaxRequest; this.ajaxRequest = ajaxRequest;
return this; return this;
} }
private void addProxy(Proxy proxy) {
if (this.proxyType == ProxyType.CUSTOM || null == proxy) {
return;
}
proxyList.add(proxy);
}
public BaseModel setProxy(String proxyStr) {
if (this.proxyType != ProxyType.CUSTOM || proxyStr == null) {
return this;
}
String[] proxyArr = proxyStr.split("\r\n");
for (String s : proxyArr) {
String[] proxy = s.split("|");
if (proxy.length == 2) {
this.addProxy(new Proxy(proxy[0], Integer.parseInt(proxy[1])));
} else if (proxy.length == 4) {
this.addProxy(new Proxy(proxy[0], Integer.parseInt(proxy[1]), proxy[2], proxy[3]));
}
}
return this;
}
enum ProxyType {
/**
* 自动获取IP代理池
*/
AUTO,
/**
* 自定义
*/
CUSTOM,
/**
* 禁用代理
*/
DISABLE
}
} }
package com.zyd.blog.spider.model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotEmpty;
import java.util.LinkedList;
import java.util.List;
/**
* 本地跑测试用
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @website https://www.zhyd.me
* @version 1.0
* @date 2018/7/23 15:58
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class CnblogModel extends BaseModel {
@NotEmpty(message = "必须指定待抓取的网址")
@Override
public String[] getEntryUrls() {
List<String> urls = new LinkedList<>();
String urlFormat = "https://www.cnblogs.com/%s/default.html?page=%s";
for (int i = 1; i <= getTotalPage(); i++) {
urls.add(String.format(urlFormat, getUid(), i));
}
return urls.toArray(new String[urls.size()]);
}
}
package com.zyd.blog.spider.model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotEmpty;
import java.util.LinkedList;
import java.util.List;
/**
* 本地跑测试用
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @website https://www.zhyd.me
* @version 1.0
* @date 2018/7/23 15:58
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class CsdnModel extends BaseModel {
@NotEmpty(message = "必须指定待抓取的网址")
@Override
public String[] getEntryUrls() {
List<String> urls = new LinkedList<>();
String urlFormat = "https://blog.csdn.net/%s/article/list/%s";
for (int i = 1; i <= getTotalPage(); i++) {
urls.add(String.format(urlFormat, getUid(), i));
}
return urls.toArray(new String[urls.size()]);
}
}
package com.zyd.blog.spider.model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.util.LinkedList;
import java.util.List;
/**
* 本地跑测试用
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @website https://www.zhyd.me
* @version 1.0
* @date 2018/7/23 15:58
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class ImoocModel extends BaseModel {
@Override
public String[] getEntryUrls() {
List<String> urls = new LinkedList<>();
String urlFormat = "https://www.imooc.com/u/%s/articles?page=%s";
for (int i = 1; i <= getTotalPage(); i++) {
urls.add(String.format(urlFormat, getUid(), i));
}
return urls.toArray(new String[urls.size()]);
}
}
package com.zyd.blog.spider.model;
import lombok.Data;
import lombok.EqualsAndHashCode;
import javax.validation.constraints.NotEmpty;
import java.util.LinkedList;
import java.util.List;
/**
* 本地跑测试用
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @website https://www.zhyd.me
* @version 1.0
* @date 2018/7/23 15:58
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class IteyeModel extends BaseModel {
@NotEmpty(message = "必须指定待抓取的网址")
@Override
public String[] getEntryUrls() {
List<String> urls = new LinkedList<>();
String urlFormat = "http://%s.iteye.com/?page=%s";
for (int i = 1; i <= getTotalPage(); i++) {
urls.add(String.format(urlFormat, getUid(), i));
}
return urls.toArray(new String[0]);
}
}
...@@ -2,22 +2,26 @@ package com.zyd.blog.spider.processor; ...@@ -2,22 +2,26 @@ package com.zyd.blog.spider.processor;
import com.zyd.blog.spider.model.Article; import com.zyd.blog.spider.model.Article;
import com.zyd.blog.spider.model.BaseModel; import com.zyd.blog.spider.model.BaseModel;
import com.zyd.blog.spider.scheduler.BlockingQueueScheduler;
import com.zyd.blog.spider.util.CommonUtil;
import com.zyd.blog.spider.util.WriterUtil; import com.zyd.blog.spider.util.WriterUtil;
import lombok.extern.slf4j.Slf4j; import com.zyd.blog.spider.webmagic.ZhydSpider;
import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import us.codecraft.webmagic.Spider; import us.codecraft.webmagic.ResultItems;
import us.codecraft.webmagic.downloader.HttpClientDownloader;
import us.codecraft.webmagic.proxy.Proxy;
import us.codecraft.webmagic.proxy.SimpleProxyProvider;
import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolation;
import javax.validation.Validation; import javax.validation.Validation;
import javax.validation.Validator; import javax.validation.Validator;
import javax.validation.ValidatorFactory; import javax.validation.ValidatorFactory;
import java.io.PrintWriter;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
/** /**
* 爬虫入口 * 爬虫入口
...@@ -27,25 +31,28 @@ import java.util.Set; ...@@ -27,25 +31,28 @@ import java.util.Set;
* @website https://www.zhyd.me * @website https://www.zhyd.me
* @date 2018/7/23 10:38 * @date 2018/7/23 10:38
*/ */
@Slf4j
public class ArticleSpiderProcessor extends BaseProcessor implements BaseSpider<Article> { public class ArticleSpiderProcessor extends BaseProcessor implements BaseSpider<Article> {
private BaseModel model; private BaseModel model;
private PrintWriter writer; private WriterUtil writer;
private Long uuid;
private ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); private ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
private ArticleSpiderProcessor() { private ArticleSpiderProcessor() {
this.writer = new WriterUtil();
} }
public ArticleSpiderProcessor(BaseModel model, PrintWriter writer) { public ArticleSpiderProcessor(BaseModel model, WriterUtil writer, Long uuid) {
super(model); super(model);
this.model = model; this.model = model;
this.writer = writer; this.writer = writer;
this.uuid = uuid;
} }
public ArticleSpiderProcessor(BaseModel model) { public ArticleSpiderProcessor(BaseModel model) {
super(model); super(model);
this.model = model; this.model = model;
this.writer = new WriterUtil();
} }
/** /**
...@@ -54,48 +61,50 @@ public class ArticleSpiderProcessor extends BaseProcessor implements BaseSpider< ...@@ -54,48 +61,50 @@ public class ArticleSpiderProcessor extends BaseProcessor implements BaseSpider<
* @return * @return
*/ */
@Override @Override
public List<Article> run() { public CopyOnWriteArrayList<Article> run() {
List<String> errors = validateModel(model); List<String> errors = validateModel(model);
if (CollectionUtils.isNotEmpty(errors)) { if (CollectionUtils.isNotEmpty(errors)) {
WriterUtil.writer2Html(writer, "校验不通过!请依据下方提示,检查输入参数是否正确......"); writer.print("校验不通过!请依据下方提示,检查输入参数是否正确......");
for (String error : errors) { for (String error : errors) {
WriterUtil.writer2Html(writer, ">> " + error); writer.print(">> " + error);
} }
return null; return null;
} }
List<Article> articles = new LinkedList<>(); writer.print(String.valueOf(CommonUtil.exitWay.get(model.getExitWay()).apply(model.getCount())));
CopyOnWriteArrayList<Article> articles = new CopyOnWriteArrayList<>();
WriterUtil.writer2Html(writer, ">> 爬虫初始化完成,共需抓取 " + model.getTotalPage() + " 页数据..."); ZhydSpider spider = ZhydSpider.create(new ArticleSpiderProcessor(), model, uuid);
Spider spider = Spider.create(new ArticleSpiderProcessor()) spider.addUrl(model.getEntryUrls())
.addUrl(model.getEntryUrls()) .setScheduler(new BlockingQueueScheduler(model))
.addPipeline((resultItems, task) -> { .addPipeline((resultItems, task) -> process(resultItems, articles, spider))
Map<String, Object> map = resultItems.getAll();
String title = String.valueOf(map.get("title"));
if (StringUtils.isEmpty(title) || "null".equals(title)) {
return;
}
String content = String.valueOf(map.get("content"));
String source = String.valueOf(map.get("source"));
String releaseDate = String.valueOf(map.get("releaseDate"));
String author = String.valueOf(map.get("author"));
String description = String.valueOf(map.get("description"));
description = StringUtils.isNotEmpty(description) ? description.replaceAll("\r\n| ", "")
: content.length() > 100 ? content.substring(0, 100) : content;
String keywords = String.valueOf(map.get("keywords"));
keywords = StringUtils.isNotEmpty(keywords) && !"null".equals(keywords) ? keywords.replaceAll(" +|,", ",").replaceAll(",,", ",") : null;
List<String> tags = (List<String>) map.get("tags");
log.info(String.format(">> 正在抓取 -- %s -- %s -- %s -- %s", source, title, releaseDate, author));
WriterUtil.writer2Html(writer, String.format(">> 正在抓取 -- <a href=\"%s\" target=\"_blank\">%s</a> -- %s -- %s", source, title, releaseDate, author));
articles.add(new Article(title, content, author, releaseDate, source, description, keywords, tags));
})
.thread(model.getThreadCount()); .thread(model.getThreadCount());
//设置抓取代理IP
if (!CollectionUtils.isEmpty(model.getProxyList())) {
HttpClientDownloader httpClientDownloader = new HttpClientDownloader();
SimpleProxyProvider provider = SimpleProxyProvider.from(model.getProxyList().toArray(new Proxy[0]));
httpClientDownloader.setProxyProvider(provider);
spider.setDownloader(httpClientDownloader);
}
// 测试代理
/*HttpClientDownloader httpClientDownloader = new HttpClientDownloader();
SimpleProxyProvider provider = SimpleProxyProvider.from(
new Proxy("61.135.217.7", 80)
);
httpClientDownloader.setProxyProvider(provider);
spider.setDownloader(httpClientDownloader);*/
// 启动爬虫 // 启动爬虫
spider.run(); spider.run();
return articles; return articles;
} }
/**
* 校验参数
*
* @param t 待校验的参数
*/
private <T> List<String> validateModel(T t) { private <T> List<String> validateModel(T t) {
Validator validator = factory.getValidator(); Validator validator = factory.getValidator();
Set<ConstraintViolation<T>> constraintViolations = validator.validate(t); Set<ConstraintViolation<T>> constraintViolations = validator.validate(t);
...@@ -106,4 +115,32 @@ public class ArticleSpiderProcessor extends BaseProcessor implements BaseSpider< ...@@ -106,4 +115,32 @@ public class ArticleSpiderProcessor extends BaseProcessor implements BaseSpider<
} }
return messageList; return messageList;
} }
/**
* 自定义管道的处理方法
*
* @param resultItems 自定义Processor处理完后的所有参数
* @param articles 爬虫文章集合
*/
private void process(ResultItems resultItems, List<Article> articles, ZhydSpider spider) {
if (null == spider) {
System.out.println("======================爬虫结束了");
return;
}
Map<String, Object> map = resultItems.getAll();
String title = String.valueOf(map.get("title"));
if (StringUtils.isEmpty(title) || "null".equals(title)) {
return;
}
String content = String.valueOf(map.get("content"));
String source = String.valueOf(map.get("source"));
String releaseDate = String.valueOf(map.get("releaseDate"));
String author = String.valueOf(map.get("author"));
String description = CommonUtil.subDescStr(String.valueOf(map.get("description")), content);
String keywords = CommonUtil.subKeywordsStr(String.valueOf(map.get("keywords")));
articles.add(new Article(title, content, author, releaseDate, source, description, keywords, (List<String>) map.get("tags")));
writer.print(String.format("正在抓取 -- <a href=\"%s\" target=\"_blank\">%s</a> -- %s -- %s", source, title, releaseDate, author));
}
} }
package com.zyd.blog.spider.processor; package com.zyd.blog.spider.processor;
import java.util.List; import java.util.concurrent.CopyOnWriteArrayList;
/** /**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com) * @author yadong.zhang (yadong.zhang0415(a)gmail.com)
...@@ -15,7 +15,7 @@ public interface BaseSpider<T> { ...@@ -15,7 +15,7 @@ public interface BaseSpider<T> {
* *
* @return * @return
*/ */
List<T> run(); CopyOnWriteArrayList<T> run();
} }
/**
* Copyright [2016-2017] [yadong.zhang]
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zyd.blog.spider.scheduler;
import com.zyd.blog.spider.enums.ExitWayEnum;
import com.zyd.blog.spider.model.BaseModel;
import us.codecraft.webmagic.Request;
import us.codecraft.webmagic.Task;
import us.codecraft.webmagic.scheduler.DuplicateRemovedScheduler;
import us.codecraft.webmagic.scheduler.MonitorableScheduler;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
/**
* 自定义的调度器,主要用来处理url
*
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @website https://www.zhyd.me
* @date 2018/7/31 17:37
*/
public class BlockingQueueScheduler extends DuplicateRemovedScheduler implements MonitorableScheduler {
private BlockingQueue<Request> queue = new LinkedBlockingQueue<>();
private int realUrlCount = -1;
public BlockingQueueScheduler(BaseModel model) {
if (ExitWayEnum.URL_COUNT.toString().equals(model.getExitWay())) {
// 实际抓取的url数量包括入口页面
this.realUrlCount = model.getCount() + model.getEntryUrls().length;
}
}
@Override
public void pushWhenNoDuplicate(Request request, Task task) {
// 当程序退出方式非URL_COUNT时按照正常逻辑处理
if (realUrlCount == -1) {
this.queue.add(request);
return;
}
// 在有效期内(realUrlCount > 0),每次push url时realUrlCount - 1, 当 realUrlCount <= 0 时,当前Scheduler将不再收录新的url
if (realUrlCount <= 0) {
return;
}
realUrlCount--;
this.queue.add(request);
}
@Override
public Request poll(Task task) {
return (Request) this.queue.poll();
}
@Override
public int getLeftRequestsCount(Task task) {
return this.queue.size();
}
@Override
public int getTotalRequestsCount(Task task) {
return this.getDuplicateRemover().getTotalRequestsCount(task);
}
}
package com.zyd.blog.spider.util;
import com.zyd.blog.spider.enums.ExitWayEnum;
import org.apache.commons.lang3.StringUtils;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @website https://www.zhyd.me
* @date 2018/9/27 17:40
* @since 1.8
*/
public class CommonUtil {
public static Map<String, Function> exitWay = new HashMap<>();
static {
exitWay.put(ExitWayEnum.DEFAULT.toString(), (obj) -> "当前程序将会在处理完所有匹配到的连接后自动停止...");
exitWay.put(ExitWayEnum.URL_COUNT.toString(), (count) -> String.format("当前程序将会在抓取够%s个连接后自动停止...", count));
exitWay.put(ExitWayEnum.DURATION.toString(), (count) -> String.format("当前程序将会在运行%s秒后自动停止...", count));
}
public static String subDescStr(String description, String content) {
return StringUtils.isNotEmpty(description) ? description.replaceAll("\r\n| ", "") : content.length() > 100 ? content.substring(0, 100) : content;
}
public static String subKeywordsStr(String keywords) {
return StringUtils.isNotEmpty(keywords) && !"null".equals(keywords) ? keywords.replaceAll(" +|,", ",").replaceAll(",,", ",") : null;
}
}
...@@ -19,6 +19,8 @@ ...@@ -19,6 +19,8 @@
*/ */
package com.zyd.blog.spider.util; package com.zyd.blog.spider.util;
import lombok.extern.slf4j.Slf4j;
import java.io.PrintWriter; import java.io.PrintWriter;
/** /**
...@@ -30,22 +32,41 @@ import java.io.PrintWriter; ...@@ -30,22 +32,41 @@ import java.io.PrintWriter;
* @date 2017/11/18 11:48 * @date 2017/11/18 11:48
* @since 1.0 * @since 1.0
*/ */
@Slf4j
public class WriterUtil { public class WriterUtil {
public static void writer2Html(PrintWriter writer, String... msgs) { private PrintWriter writer;
public WriterUtil() {
}
public WriterUtil(PrintWriter writer) {
this.writer = writer;
}
public WriterUtil print(String... msgs) {
if (null == writer) { if (null == writer) {
return; for (String msg : msgs) {
log.info(msg);
}
return this;
} }
for (String msg : msgs) { for (String msg : msgs) {
log.info(msg);
writer.print("<script>parent.printMessage('" + msg + "');</script>"); writer.print("<script>parent.printMessage('" + msg + "');</script>");
writer.flush(); if(null != writer) {
writer.flush();
}
} }
return this;
} }
public static void shutdown(PrintWriter writer) { public void shutdown() {
writer2Html(writer, "程序结束...", "shutdown"); print("exit...", "shutdown");
if (null != writer) { if (null != writer) {
writer.close(); writer.close();
writer = null;
} }
} }
} }
package com.zyd.blog.spider.webmagic;
import com.zyd.blog.spider.enums.ExitWayEnum;
import com.zyd.blog.spider.model.BaseModel;
import us.codecraft.webmagic.Request;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.processor.PageProcessor;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @website https://www.zhyd.me
* @date 2018/9/26 17:21
* @since 1.8
*/
public class ZhydSpider extends Spider {
/**
* 用来保存正在运行的所有Spider,key要求唯一,一般为用户ID,需要调用方生成
*/
public static final ConcurrentHashMap<Long, ZhydSpider> SPIDER_BUCKET = new ConcurrentHashMap<>();
private BaseModel model;
/**
* 唯一的key,一般为用户ID,需要调用方生成
*/
private Long uuid;
private volatile long startTime = 0L;
private ZhydSpider(PageProcessor pageProcessor, BaseModel model, Long uuid) {
super(pageProcessor);
this.model = model;
this.uuid = uuid;
SPIDER_BUCKET.put(uuid, this);
}
public static ZhydSpider create(PageProcessor pageProcessor, BaseModel model, Long uuid) {
return new ZhydSpider(pageProcessor, model, uuid);
}
@Override
protected void onSuccess(Request request) {
super.onSuccess(request);
if (this.getStatus() == Spider.Status.Running && ExitWayEnum.DURATION.toString().equals(model.getExitWay())) {
if (startTime < System.currentTimeMillis()) {
this.stop();
}
}
}
@Override
public void run() {
startTime = System.currentTimeMillis() + model.getCount() * 1000;
super.run();
}
@Override
protected void onError(Request request) {
super.onError(request);
}
@Override
public void close() {
super.close();
SPIDER_BUCKET.remove(this.uuid);
}
@Override
public void stop() {
super.stop();
// this.close();
SPIDER_BUCKET.remove(this.uuid);
}
}
package com.zyd.blog.spider;
import com.zyd.blog.spider.enums.ExitWayEnum;
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.junit.Test;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;
/**
* @author yadong.zhang (yadong.zhang0415(a)gmail.com)
* @version 1.0
* @website https://www.zhyd.me
* @date 2018/9/27 17:26
* @since 1.8
*/
public class OtherTest {
@Test
public void main() {
Map<String, Function> map = new HashMap<>();
map.put(ExitWayEnum.URL_COUNT.toString(), (count) -> String.format("当前程序将会在抓取完%s个连接后停止...", count));
map.put(ExitWayEnum.DURATION.toString(), (count) -> String.format("当前程序将会在运行%s秒后停止...", count));
System.out.println(map.get(ExitWayEnum.DURATION.toString()).apply(100));
}
@Test
public void enumTest(){
long start = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
EnumTest.TWO.toString();
}
long end = System.currentTimeMillis();
System.out.println("enum.toString --> " + (end - start));
for (int i = 0; i < 10000000; i++) {
EnumTest.valueOf("TWO");
}
System.out.println("enum.valueOf --> " + (System.currentTimeMillis() - end));
}
private enum EnumTest {
ONE,
TWO,
THREE
}
@Test
public void checkProxy() throws IOException {
//http://1212.ip138.com/ic.asp 可以换成任何比较快的网页
Connection.Response response = Jsoup.connect("http://ip138.com/ips138.asp")
.timeout(10 * 1000)
.proxy("61.135.217.7", 80)
.execute();
System.out.println(response.statusCode());
}
}
...@@ -22,9 +22,11 @@ package com.zyd.blog.controller; ...@@ -22,9 +22,11 @@ package com.zyd.blog.controller;
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.zyd.blog.business.annotation.BussinessLog;
import com.zyd.blog.business.entity.Comment; import com.zyd.blog.business.entity.Comment;
import com.zyd.blog.business.entity.Link; import com.zyd.blog.business.entity.Link;
import com.zyd.blog.business.enums.CommentStatusEnum; import com.zyd.blog.business.enums.CommentStatusEnum;
import com.zyd.blog.business.enums.PlatformEnum;
import com.zyd.blog.business.service.BizArticleService; import com.zyd.blog.business.service.BizArticleService;
import com.zyd.blog.business.service.BizCommentService; import com.zyd.blog.business.service.BizCommentService;
import com.zyd.blog.business.service.SysLinkService; import com.zyd.blog.business.service.SysLinkService;
...@@ -73,6 +75,7 @@ public class RestApiController { ...@@ -73,6 +75,7 @@ public class RestApiController {
private SysNoticeService noticeService; private SysNoticeService noticeService;
@PostMapping("/autoLink") @PostMapping("/autoLink")
@BussinessLog(value = "自助申请友链", platform = PlatformEnum.WEB)
public ResponseVO autoLink(@Validated Link link, BindingResult bindingResult) { public ResponseVO autoLink(@Validated Link link, BindingResult bindingResult) {
log.info("申请友情链接......"); log.info("申请友情链接......");
log.info(JSON.toJSONString(link)); log.info(JSON.toJSONString(link));
...@@ -89,6 +92,7 @@ public class RestApiController { ...@@ -89,6 +92,7 @@ public class RestApiController {
} }
@PostMapping("/qq/{qq}") @PostMapping("/qq/{qq}")
@BussinessLog(value = "获取QQ信息", platform = PlatformEnum.WEB)
public ResponseVO qq(@PathVariable("qq") String qq) { public ResponseVO qq(@PathVariable("qq") String qq) {
if (StringUtils.isEmpty(qq)) { if (StringUtils.isEmpty(qq)) {
return ResultUtil.error(""); return ResultUtil.error("");
...@@ -116,12 +120,14 @@ public class RestApiController { ...@@ -116,12 +120,14 @@ public class RestApiController {
} }
@PostMapping("/comments") @PostMapping("/comments")
@BussinessLog(value = "评论列表", platform = PlatformEnum.WEB, save = false)
public ResponseVO comments(CommentConditionVO vo) { public ResponseVO comments(CommentConditionVO vo) {
vo.setStatus(CommentStatusEnum.APPROVED.toString()); vo.setStatus(CommentStatusEnum.APPROVED.toString());
return ResultUtil.success(null, commentService.list(vo)); return ResultUtil.success(null, commentService.list(vo));
} }
@PostMapping("/comment") @PostMapping("/comment")
@BussinessLog(value = "发表评论", platform = PlatformEnum.WEB)
public ResponseVO comment(Comment comment) { public ResponseVO comment(Comment comment) {
try { try {
commentService.comment(comment); commentService.comment(comment);
...@@ -133,6 +139,7 @@ public class RestApiController { ...@@ -133,6 +139,7 @@ public class RestApiController {
} }
@PostMapping("/doSupport/{id}") @PostMapping("/doSupport/{id}")
@BussinessLog(value = "点赞评论{1}", platform = PlatformEnum.WEB)
public ResponseVO doSupport(@PathVariable("id") Long id) { public ResponseVO doSupport(@PathVariable("id") Long id) {
try { try {
commentService.doSupport(id); commentService.doSupport(id);
...@@ -144,6 +151,7 @@ public class RestApiController { ...@@ -144,6 +151,7 @@ public class RestApiController {
} }
@PostMapping("/doOppose/{id}") @PostMapping("/doOppose/{id}")
@BussinessLog(value = "点踩评论{1}", platform = PlatformEnum.WEB)
public ResponseVO doOppose(@PathVariable("id") Long id) { public ResponseVO doOppose(@PathVariable("id") Long id) {
try { try {
commentService.doOppose(id); commentService.doOppose(id);
...@@ -155,6 +163,7 @@ public class RestApiController { ...@@ -155,6 +163,7 @@ public class RestApiController {
} }
@PostMapping("/doPraise/{id}") @PostMapping("/doPraise/{id}")
@BussinessLog(value = "点赞文章{1}", platform = PlatformEnum.WEB)
public ResponseVO doPraise(@PathVariable("id") Long id) { public ResponseVO doPraise(@PathVariable("id") Long id) {
try { try {
articleService.doPraise(id); articleService.doPraise(id);
...@@ -166,6 +175,7 @@ public class RestApiController { ...@@ -166,6 +175,7 @@ public class RestApiController {
} }
@PostMapping("/listNotice") @PostMapping("/listNotice")
@BussinessLog(value = "公告列表", platform = PlatformEnum.WEB, save = false)
public ResponseVO listNotice() { public ResponseVO listNotice() {
return ResultUtil.success("", noticeService.listRelease()); return ResultUtil.success("", noticeService.listRelease());
} }
......
...@@ -43,4 +43,6 @@ logging: ...@@ -43,4 +43,6 @@ logging:
app: app:
# 是否启用kaptcha验证码 # 是否启用kaptcha验证码
enableKaptcha: false enableKaptcha: false
# 创建网站的时间,用于计算已建站的天数,默认为2018-01-01
buildWebsiteDate: 2018-01-01 00:00:00
####################################自定义配置########################################## ####################################自定义配置##########################################
\ No newline at end of file
...@@ -42,4 +42,6 @@ logging: ...@@ -42,4 +42,6 @@ logging:
app: app:
# 是否启用kaptcha验证码 # 是否启用kaptcha验证码
enableKaptcha: false enableKaptcha: false
# 创建网站的时间,用于计算已建站的天数,默认为2018-01-01
buildWebsiteDate: 2018-01-01 00:00:00
####################################自定义配置########################################## ####################################自定义配置##########################################
\ No newline at end of file
...@@ -54,7 +54,7 @@ ...@@ -54,7 +54,7 @@
<strong>文章归档目录</strong> <strong>文章归档目录</strong>
</h1> </h1>
<@zhydTag method="siteInfo"> <@zhydTag method="siteInfo">
<div class="archives-meta"> 站点统计:${siteInfo.typeCount!(0)}个分类&nbsp;&nbsp; ${siteInfo.tagCount!(0)}个标签&nbsp;&nbsp; ${siteInfo.articleCount!(0)}篇文章&nbsp;&nbsp; ${siteInfo.commentCount!(0)}条留言&nbsp;&nbsp; 最后更新:${siteInfo.recordeTime} </div> <div class="archives-meta"> 站点统计:${siteInfo.typeCount!(0)}个分类&nbsp;&nbsp; ${siteInfo.tagCount!(0)}个标签&nbsp;&nbsp; ${siteInfo.articleCount!(0)}篇文章&nbsp;&nbsp; ${siteInfo.commentCount!(0)}条留言&nbsp;&nbsp; 最后更新:${siteInfo.lastUpdateTime} </div>
</@zhydTag> </@zhydTag>
<p class="blog-description hitokoto"></p> <p class="blog-description hitokoto"></p>
</div> </div>
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册