Skip to content

Jeffrey2971/wechat

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

微信公众号后台

一、项目介绍

① 项目功能、状态

  • 公众号的功能包括了为用户提供分享获取永久免费使用功能、用户反馈功能、文档翻译功能、图片文字识别翻译功能、图片实景翻译功能

  • 目前公众号 【图片文字识别翻译】 + 【图文识别翻译 OCR】已为 10000+ 名用户提供服务

  • 项目由构思、所采用的技术栈、项目的创建以及代码的编写从 0 到 1 由自己完成

② 项目创建原因

  • 在项目创建之前,当时使用 Mac 系统没多久发现并没有适合自己的翻译插件或软件,由于在使用电脑时微信一般都会登陆上,正好可以使用微信中带翻译功能的微信公众号进行翻译我所需的内容,但问题是大部分公众号在使用达到一定次数后需要进行收费或看视频才能获取翻译使用机会。因此在一开始决定使用微信订阅号或微信接口测试号开发一个能属于自己能够使用、配合第三方 api 翻译接口的公众号

③ 决定对项目进行维护

  • 在某日查看公众号时,原本想开发用来自己用的公众号发现已经有几十名用户关注并且使用了有一段时间,有了粉丝即有了动力,所以心血来潮决定将公众号进行维护也坚持到了现在

④ 遇到的问题

  • 订阅号响应的问题

    • 在收到来自用户的消息后,必须在五秒内对消息进行响应,否则微信会直接给用户提示公众号异常信息,但是订阅号并没有异步响应接口的权限且由于微信给定开发者的回复总体长度为 2048 字节(1 个汉字 ≈ 2 / 3 个字母 ≈ 2 / 3 字节),在识别或翻译时往往因为回复长度恰好不足而导致无法翻译甚至回复失败,因此在原基础上提供了 ”点击查看更多“ 按钮,在回复长度超出临界值时将会触发此按钮。在其他情况下(图片原文字节长度 + 翻译内容字节长度 + 60(提示内容字节长度) < THRESHOLD(临界值 2048)),该按钮不会触发,但这种方法治标不治本,在用户发送较大的图片时往往来不及五秒内响应,所以最后开通了服务号并使用一些响应的接口解决
  • 文档翻译前端在线预览显示的问题

    • 在前端页面在线预览原有格式的文档并不是容易的事,所以项目采用了基于 HTTP 服务的 kkFileView 框架来展示不同类型文件在前端显示预览
  • 文档数据存储的问题

    • 原先采用 HashMap 进行存储数据结构并定期对 Map 中的过期数据进行删除,但由于数据对应的过期时间是定义在 value 中的,所以每次需要定时循环对集合内的数据进行检查,当集合内的数据多时这种方法存在一定的性能问题,且该集合在项目中多处调用也存在代码耦合的问题,所以统一采用 Redis 的方式进行存储数据,在设置数据时 value 使用 json 的形式进行存储,并同时定义 TTL 过期时间,最后将操作 Redis 的代码封装为一个工具类进行调用
  • 用户信息安全的问题

    • 包括用户翻译文档存储、获取,用户图片数据存储、获取的问题
  • jvm 频繁宕机问题

    • 定时任务执行时需要一次性获取 redis 中所有的数据,由于服务器内存较小且直接获取所有数据的操作太过粗糙,所以该操作直接导致因 jvm 内存不足而宕机。通过修改采用分段的形式获取 redis 数据解决了该问题
  • 项目健康状况无法及时查看问题

    • 以往通过登录 ssh 查看项目日志以判断项目状态,此种方式不优雅且效率低下,所以采用了 spring-boot-security-starter、spring-boot-admin-starter、spring-boot-admin-client、spring-boot-health-starter、spring-boot-starter-mail 框架将服务注册到 admin 平台实时可视化查看项目状态信息以及通过安全邮箱发送邮件告知管理员

⑤ 所使用的技术栈

  • 后端:kkFileView + SpringBoot + MyBatisPlus + Redis + MySql
  • 前端:html + jquery + bootstrap

二、更新日志

2022-11-4 LTS

  • 新增
    • 采用第三方可视化健康平台 admin 框架作为服务端项目健康监控,服务端采用 spring-boot-actuator-starter、spring-boot-admin-starter、spring-boot-admin-starter-client、spring-boot-security-starter 模块。功能包括 admin 平台的用户校验功能、项目状态信息邮件反馈功能、项目详细信息查看功能、将微服务项目注册到平台的功能等等
  • 修改
    • 定时任务执行时需要一次性获取 redis 中所有的数据,由于服务器内存较小且直接获取所有数据的操作太过粗糙,所以该操作直接导致因 jvm 内存不足而宕机。通过修改采用分段的形式获取 redis 数据解决了该问题

2022-7-13

  • 修改
    • 修改在项目未初始化完成之前就调用了 Bean 组件造成的空指针异常
    • 修改因判断语句添加了 ! 使条件表达式在正常情况下无法成立造成文档翻译功能失效

2022-7-8

  • 重构

    • 本次更新在基础上新增了 kkFileView + SpringBoot + MyBatisPlus + Redis 框架进行重构
    • 众多代码细节的修改、层级关系的修改、新增的功能可在历史提交处 commit message 查看
  • 新增功能

    • 文档翻译功能:根据用户上传的文档以及对应的语种设置进行翻译,并通过异步以及微信模板消息的形式响应给用户

    • 反馈邮件提醒功能:当用户通过表单形式进行反馈时,发送邮件至我的邮箱中,邮箱内容包括用户的 openid 、用户的反馈时间、用户的反馈标题、用户的反馈详情、用户的联系方式、用户的联系方式详情

    • 公众号异常邮件提醒功能:当公众号发生异常时,通过 AOP 切面实现异常邮件的通告并发送至我的邮箱中,邮箱内容包括异常的发生时间、对应的用户 openid、跑出异常的类型、抛出异常的所在方法、抛出异常的堆栈信息

    • 消息模板功能:消息模板是微信推出由于提示用户一些较为重要的消息模板,通过微信官方提供的模板库以及调用响应的 API 进行响应,公众号在文档翻译上使用了模板消息进行异步响应

  • 修改

    • 对数据库表的一些字段修改
    • 对响应模板的一些修改:对用户的翻译内容进行校验、如果发现用户翻译的内容类似于文档的话则在消息尾部添加建议使用文档翻译
    • 修改每位用户每日的使用次数:改回每日可免费使用 5 次机会

2022-3-19

  • 项目重构并终止更新

  • 修改

    • 一些细微的改动

2021-11-22

  • 修改
    • 修改已是免费用户点击推广链接时照样显式推广二维码而不是直接响应文字
    • 修改不论是内测还是普通用户都显式同样的反馈界面
    • 修改点击反馈页面找不到图片资源异常
    • 修改照片、注释、提示信息

2021-10-8

  • 修改
    • 修改菜单栏按钮,移除子菜单,新增推广按钮。并修改了查看使用方式链接
    • 修改每位普通用户每天的免费使用次数为 7 次
    • 新增了临时使用次数功能,该功能主要针对于还未来得及推广的用户使用,在用户每天的免费使用次数用完后,将通过客服接口异步响应一个图文消息,点击后在今日可获取 10 次使用次数。每位用户只能点击一次,在临时次数使用完后系统将不再推送该图文信息
    • 使用反射优化了代码,主要针对于 web 层

2021-9-28

  • 新增
    • 分享功能:在用户关注后或在每次响应后都会携带一条推广获取永久使用权的链接,在用户点击该链接后会生成对应的推广二维码,该二维码原始 url 由微信生成,通过调用草料网二维码模板 api 进行样式填充并解析草料网的返回结果后得到一个添加了样式的二维码 url 链接。首先根据该链接下载二维码图片到本地并上传到微信服务器的永久素材库后得到素材的 Url 地址。得到微信素材库对应的二维码 url 地址后讲改地址返回给 jsp 页面。jsp 得到的是一个来自微信素材库的二维码图片链接,该二维码中含有推广者的 openid 信息,在其他用户根据该二维码进行关注后微信会将该 openid 值推送到后台服务器进行进一步操作。用户推广的二维码只要有三人成功关注就会获取公众号的永久使用权限
  • 限制:如用户非前 100 名关注的内测用户,或未推广二维码取得永久使用权只能每天使用 5 次
  • 解决
    • 迁移关于推广功能的所有方法到 BetaUserService 中

2021-9-22

  • 新增

    • 将公众号进过认证后已升级成为服务号,之前订阅号大部分不能使用的接口在服务号中可以使用
    • 根据工信部要求,在所有相关页面尾部添加了备案号
    • 新增了一套基于用户管理的实现工具类:com.jeffrey.manager.user
    • 新增公众号底部菜单栏按钮:com.jeffrey.menu
  • 问题

    • 关于五秒内无法及时响应图片消息的问题
  • 解决

    • 当用户发送图片后直接响应正在处理中让用户稍等几秒,将耗时操作提交给其他线程操作,然后使用客服接口进行二次响应

2021-9-8

  • 问题

    • 由于订阅号的性质和新增了图片翻译回填的功能,造成了没有在三次请求中的五秒内进行响应,造成了响应失败
  • 解决思路

    • 1、默认情况下用户发送图片,不直接进行请求图片回填,而是用户点了了查看图片回填的链接后再进行图片回填请求
    • 2、用户请求后在响应期间不直接进行图片回填请求,而是在响应完成后再重新进行一次请求,本次请求需要图片回填

2021-9-3

  • 问题

    • 以往的照片文字识别及翻译需要调用两个 API ,现修改后只需调用一个
    • 修改 WeChatServiceImpl 类中的相关代码
  • 解决

    • 将所有识别和翻译的相关功能都转移到了 com > jeffrey > translate 下,并新增了 GetTranslateMetaData 等其他类,调用该类的 getData(); 方法会返回一个 Translate 对象,该对象封装了响应中的信息,通过该对象可以很方便的获取到响应相关数据
      • Translate:封装了响应所有数据
        • SegmentedData:响应的片段信息,例如坐标轴、片段原文、片段译文等
          • DataPoints:坐标轴信息

2021-8-29

  • 问题
    • 邮件业务优化

2021-8-24

  • 问题

    • 某些恶意频繁请求的用户将会占用服务器大量资源,因此需要对其作出限制
  • 解决

    • 新增 FrequencyQueue 线程类,用于当某个 ip 或 openid 的频率达到零界值时,将该 openid 或 ip 添加进 BLACK_MAP 中,其中 KEY 为请求的 openid 或 ip ,VALUE 为当前的时间戳

    • 新增 BlackMapQueue 线程类,用于记录频繁访问的用户,如 BLACK_MAP 中 KEY 的值到达解除访问限制时将该项移除

    • 新增监听器 ListenRequestFrequency,用于请求携带 openid 时记迭代该 openid 的的频率,未携带 openid 时使用请求的 ip 地址进行迭代请求频率

    • 新增过滤器 RequestFilter,用于判断请求来自微信端还是浏览器端,如确认为微信端发送的请求且该请求存在于频繁访问的名单中,则调用 service 层解析 xml 并新建消息对象返回。如请求来自浏览器端则先判断该请求是否携带 openid 如携带且该 openid 存在于频繁访问名单中则直接响应,如未携带 openid 则判断该请求的 ip 地址是否在频繁访问名单中,如存在则直接响应,其他情况进行请求放行处理

2021-8-25

  • 问题

    • 项目部署在 win 系统上,发现响应字符实际比 THRESHOLD 的值要大造成响应字节数 2048 而失败
    • 项目部署在 win 系统上,发现数据库表 feedback 的 title 及 message 字段为乱码
  • 解决

    • 在 WeChatServiceImpl 类中的 THRESHOLD 属性加 50

2021-8-24

  • 问题

    • feedback 表中的 details 字段是唯一的,不能插入两个相同的值。由于在 feedback.jsp 页面上没有做表单校验,导致了插入两个空值而出错
  • 解决

    • 修改数据库字段 details 不唯一
      ALTER TABLE feedback DROP INDEX details;
  • 修改

    • 修改 choose.jsp、feedback.jsp、QuestionServlet 中的参数 data_id 为 openid
    • 移除无用 jar 包

2021-8-23

  • 新增

    • 在反馈成功和失败状态页面上新增了居中和字体大小样式
    • 优化了 Servlet 层代码,将 gc 相关的代码迁移到 Request 监听器中
  • 问题

    • 在 QuestionServiceDaoImpl 类中的 saveFeedBackText() 方法中没有处理在和数据库进行交互时发生异常后归还数据库连接造成了在引发 10 次错误后数据库连接池中没有可用的连接,这将导致服务器响应失败
  • 解决

    • 在 finally 代码块将数据库连接进行归还,并在插入数据前查看用户是否存在以避免报错
    @Override
    public boolean saveFeedBackText(FeedBack feedBack) {
    
            String sql = "insert into feedback (data_id, title, message,way,details, is_beta_user, ctime)VALUE(?,?,?,?,?,?,?)";
            Connection connection = null;
            try {
                String dateTime = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date(feedBack.getTimeStamp()));
                connection = JdbcUtils.getConnection();
                return update(connection, sql, feedBack.getData_id(), feedBack.getTitle(), feedBack.getMessage(), feedBack.getWay(),
                        feedBack.getDetails(), betaUserServiceDao.isExists(feedBack.getData_id(), feedBack.getTable()) ? 1 : 0, dateTime) >= 1;
            } finally {
                JdbcUtils.close(connection);
            }
    
        }

2021-8-18

新增功能

  • 新增 API 翻译参数

    OPTIONS.put("language_type", "CHN_ENG");
    OPTIONS.put("detect_direction", "true");
    OPTIONS.put("detect_language", "true");
    OPTIONS.put("probability", "true");
  • 文档找不到页面重写

问题

  • 发现每个文档到指定销毁时间时并没有正常意义上的销毁,而是清除了 DATA_MAP 中的指定文档,实际上的文档对象是存放在 ServletContext 域中,这样造成的后果为当有大量的文档对象堆积到 ServletContext 对象中造成服务器卡顿甚至宕机

解决思路

  • 在 DATA_MAP 根据文档创建的时间戳 key 销毁文档对象时一起删除 ServletContext 中指定的文档对象

  • 在 DocumentQueue 类中新增一个构造器,用于传递 ServletContext

    private ServletContext servletContext;
    
        public DocumentQueue(ServletContext servletContext){
            this.servletContext = servletContext;
        }
  • 取消在 ListenServletContextStatus 监听器中创建并开启 DocumentQueue 线程,原因为监听器组件不能继承 HttpServlet 否则会引发空指针异常

  • 在 WeChatAccessServlet 类中的 init(ServletConfig servletConfig) 方法中创建并开启 DocumentQueue 线程

二、检查列表

① 检查关注/取消关注是否正常

  • 关注:关注后需要有正常的响应,包括链接的返回、以及第几位关注用户信息返回是否成功正确。同时检查数据库是否新增用户
  • 取消关注:用户取消关注后需检查数据库相关信息是否有移除,包括 beta_users 、users 、share 这三张表,其他表无需移除相关信息

② 发送文字消息是否正常

  • 用户发送文字信息时需要正常响应,包括关键字响应以及提示信息响应。

③ 图片消息消息是否正常

  • 用户发送图片信息时,检查响应的各项链接是否正常

④ 文档翻译是否成功通过模板消息响应

⑤ 关于安全方面的校验

About

微信公众号

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published