Bootstrap

极简AI工具箱网站开源啦!

开源地址:https://gitee.com/toolsj-open/go

反正也经营不下去了,一点流量都没有。虽然谈不上精品,但是我也用心做了。开源出来供学习吧,下面是详细文档:

相关仓库

mysql表结构:https://gitee.com/toolsj-open/mysql

前端-PC:https://gitee.com/toolsj-open/web-ssg

前端-H5:https://gitee.com/toolsj-open/h5-ssg

极简 AI 工具箱后端

本项目主打是无会员,不需要注册,不需要登录。目标群体是零散需求的同学。按次收费,价格低廉。

我想解决的痛点就是:当我需要证件照换底色等偶尔用到的功能时,网上一搜,要么需要注册,要么需要会员。我就用一次,还要注册一通,太麻烦了。所以我得特点就是不用注册,不用会员。

通过 IP 地址来定位给用户。当然我也知道 ipV4 不能精准定位用户,但是没有其他更好的方案了。因为用户量不大。所以也基本是够用了。

其次每次任务都会随机生成一定长度的任务 id。这个 id 基本保证了不会重复。所以只要用户掌握了该 id,也就掌握了他上传的资源。只要他不泄露这个 id,也就不会造成资源泄露。当然,用户忘记该 id,也找不回自己的资源了。

基于百度的 AI 接口,以及 opencv 实现的 Ai 图像处理工具箱,当前包含的功能有:

  • 编号 27: 图片转 word/excel
  • 编号 1: 人像分割|证件照换底色
  • 编号 24: 文档图片去底纹/水印
  • 编号 26: 去除文档手写体
  • 编号 23: 图片无损放大两倍
  • 编号 21: 图片清晰度增强
  • 编号 22: 黑白图片上色
  • 编号 25: 去除摩尔纹
  • 编号 4: 图片格式转换
  • 编号 2: 图片有损压缩
  • 编号 3: png 无损压缩

以上编号不可变动,在前后端代码均有强依赖。如有新增工具,会在tools表中添加。id 确定后不可变更。

部署

opencv 编译

直接参照gocv的文档编译即可,难点在于资源下载不下来,经常超时,多试几次。不用梯子也是可以正常下载下来的,就是要多试。

实在不行可以参照我的博客。手动下载依赖,修改编译脚本。不过这篇文章再看看也有偏颇,仅供参考。

PS:特别注意,gocv 的版本和 opencv 的版本是强绑定的。详情参考官方文档

构建

go 的构建都是很简单的啦,搞定上面的 opencv 编译,就直接go build main.go

运行

运行前,有挺多条件需要具备。基本上都在 config.yaml 里了,必须修改的地方都已经提取到配置文件中了。

  • AppDebug:是否处于调试模式。调试模式不会启用 https。正式环境下改为 false。
  • HttpServer.Port:这是后端 api 的端口,也是前端静态资源的访问端口
  • CA 证书,也就是启用了 https 的 tls 证书
  • 微信支付的私钥。这个参见微信支付文档申请。
  • 支付宝支付的公钥和私钥。这个参见支付宝支付文档申请
  • 日志配置,可不修改
  • mysql 的配置,修改一下用户名和密码。数据库名改了的话,记得修改一下
  • 其他文件里的CnIp:是通过ipdeny收集整理的国内 ip。搞这个是当时很怕 ddos 攻击,查资料说大多数 ddos 都是国外的肉鸡,所以就想着直接限制国外的访问,这样就可以增加防御能力,虽然 ddos 也确实是防不住的。这个直接拿去用就行。目前项目对于这段也注释掉了,因为我发现我的服务器运营大半年来,都没有受到攻击。反而阻止了国外搜索引擎的收录。
  • 其他文件里的BanIp:被永久封禁的 ip。之前想存数据库里的,但是觉得又没必要,就直接拿压缩文件来存了,节省空间,程序退出时会将永封 ip 记录到这里。程序启动时会查找该文件读取永封 ip 加载到内存里。
  • URL:这个域名是为了支付回调而配置的,需要备案。必须是 https。当然没有也没关系,也就是收不到回调。代码里有定时任务轮询支付结果,不过时效性不如回调及时。用户体验会不好
  • BaiduApp:百度接口的应用 id 和接口 URL。应用需要自己去申请,一般新用户都会有体验次数。为什么选百度,我也用了阿里和腾讯的,不如百度全。效果也不如百度的。
  • WaterImg:水印图片,加水印是将该图片叠加到图片上。背景必须是全黑。这个水印也是经过仔细研究搞出来的,效果不错。只不过没法做到动态水印。
  • WebPackageName:前端静态资源存放的地址。后端代码具备了静态资源代理服务,不再需要 nginx 等代理服务。区分 web 端和 H5 端

目前网站toolsj.cn2C2G3M 的服务器里运行良好,程序运行到稳定,大概需要 500M 内存。CPU 几乎没啥需求。性能可以说很不错。当然也是用户量不大。不知道用户量大了怎么样。带宽要求比较高,毕竟需要传输图片。带宽太贵了!

项目结构

  • app:业务相关的代码
  • ca:证书相关的文件。包含 tls 证书,支付的私钥,支付的公钥
  • config:配置文件。包含数据库的配置,日志的配置,微信支付的配置,支付宝支付的配置,以及一些其他配置
  • global:全局变量、常量。按理说这只存放业务无关的。但是目前把一些项目弱相关的变量也放到这里了。不过不多。
  • logs: 日志文件。
  • middleware:中间件。
  • router:路由相关
  • save: 存放业务上传的文件以及处理的结果。比如图片、word 文档等。有一个定时任务会定时清理
  • timer:定时任务。这里的东西和业务有关。
  • utils:工具类。
  • main.go:入口文件
  • README.md: 项目说明
  • go.mod: go 模块文件

细节及技术说明

框架

我使用的是国人基于 gin 框架封装的ginSkeleton,其实 go 的 web 项目对于框架的依赖很小,框架最大的作用是封装了一套路由和中间件。不过我也没有沿用该框架的配置方式,改变了他的项目框架,所以只能算是使用了他封装好的一些工具。现在看作者也不在更新了

如果现在让我选,我选字节开源的框架hertz

静态资源代理

除了必备的配置外,该后端已经具备了前端的静态资源代理功能。对应代码在:/app/proxy/proxy.go。除了普通的代理,还具备根据请求头判断是否启用压缩。是否启用缓存的功能。

除了该代理,在正式环境上,我发现很多用户输入网址是不会输入 http://,而是直接输入网址。这时候浏览器默认是使用 http 来请求,导致请求失败。而大多数网友是看不懂为什么的。

所以,为了友好的用户体验,必须得同时监听 http 的 80 端口和 https 的 443 端口。443 是我们正常的业务端口。而启用 80 端口只有一个目的,就是将 http 的请求重定向到 https 上。对应的代码在/main.gostartHttp80Server()方法里。

这样。用户只要输入网址即可正常访问。

IP 管理模块

这个模块嘛,没有也正常运行。但是我前前后后得花了两周时间琢磨出来。可能也没起到什么作用。但是我觉得我做得不错。

我收集的文件是/cn.zone.txt。国内的 ip 段一般不怎么变化。所以直接直接用。当然也可以一年半载去网站里整一份新的。

详情见我这篇博客

由于 IPV4 并不能精准标识用户,所以如果是一个小区一起使用,造成接口调用次数上涨太快,就会被封禁。但是其实用户量少时不需要担心这点,所以我还是采用了这个方案。

我刚上线网站时,天天有看日志,确实非常多国外的非法请求。他们请求的路径都是和我的业务无关的。比如 php 就很多。然后从我这获取不到任何信息。我的网站也就一直安全运行。

从这些信息来看,php 是别人攻击的重灾区,因为太多的网站就是 php 套的模板。bug 都是一样的。人家一扫描就知道了。目前 go 的 web 项目很少。没有固定的模板。所以他们没有现成的攻击手段。这也是一大优点。何况我这是纯手写的。

当然,开源出来了,要是有人感兴趣,可以帮我找找漏洞。

opencv 及相关的图像处理算法

opencv 没有 go 语言版。好在还有 gocv 提供了调用方法。麻烦是麻烦点。好在能用。

但是,我用下来发现,gocv 调用 opencv 的功能,非常容易内存泄漏。那些图像矩阵,是不受 go 垃圾回收机制控制的。之前我发了不少时间来检查内存泄漏,直到现在我感觉还有内存泄漏的地方。不然我感觉我的程序应该用不到 500M 内存。

但是 go 确实没有好用的图像处理库,其实我用到的功能也很少,无非读取图片、格式转换、压缩。这些倒是都有 go 的库可以做到。但是我发现他们欠缺的是高性能的图像矩阵运算能力。这点 python 就很好,numpy 库+opencv+一些科学计算的库。太完美了,直接让 python 称霸了深度学习领域。

证件找换底色算法

这是本项目里图像处理写算法最复杂的部分。一开始我也没想到,一个满大街都是的证件照换底色功能。不应该百度一搜就能找到吗?但是我发现,百度搜出来的方法,居然是原始的一个个像素点判断+替换。我就觉得不可能这么蠢吧,所以我查到一些不完善的资料,自己琢磨,用矩阵运算的方式。整出了还算效果不错的算法。

详细解析看/技术文档/证件照换底色计算原理.md

图片加水印

这个本项目里图像处理写算法第二复杂的部分。这个也没有完美能用的代码可以 copy。所以我查到一些不完善的资料,自己琢磨,用矩阵运算的方式。整出了还算效果不错的算法。

不过没法做到动态水印,得依赖早已生成的watermark.img图片。

我将该方法剥离出来,写在博客里了:https://blog.csdn.net/lsjweiyi/article/details/140499414。

png 有损压缩

没想到吧,这个功能技术含量非常高。我做不出来。opencv 也没有。然后我翻遍各种资料,发现自己实现这个算法是不现实的。于是我就找开源工具,找了好久好久,最终找到一个不再更新的go-imagequant库。他的官方文档应该是这个。但是 go 不再更新了。不过也能用。而且不允许商业使用的。这个功能其实也没啥人用,去掉也无妨。

该功能仅供学习哈。

本地接口

顾名思义,这些接口不对外开放,只允许本地访问。中间件在/middleware/baseHandler.goLocalhostHandler()方法里。我设置的是只允许127.0.0.1访问。当然你可以修改成其他 ip。

其中包含的接口在/router/lolcalRouter.go 里。

主要是为了方便读取内存里的信息,比如永封的 ip 列表。比如更新前端静态资源。不然前端修改了还需要重启后端。

本项目原来是使用 redis 来管理缓存的。但是后来我想,我的服务器配置这么差,再装个 redis 多不划算。然后缓存数量又不多。不如直接用全局变量来管理。还省去了网络 IO 开销。

百度接口

百度的文档里有 go 版本的用例。所以也不难。跑通了第一个接口后面就简单了。这里就不多介绍了

微信支付

微信支付我是用在 web 端。H5 端我使用的是支付宝支付。

微信支付的门槛在于申请下来那些 key。微信有提 go 语言版的 sdk,但是我后悔了,因为官方不怎么更新,很多 bug。还不如用 gopay。懒得改了。不过对接接口和调试还是花了不少时间。对应的实现在/app/service/payService/wechatPay.go里。

我也是第一次在项目里对接支付,经验不足,也得一般般吧。

支付宝支付

因为微信支付对 H5 的限制很多,支付宝好点,所以无奈还得为了 H5 对接支付宝。申请权限也难。官方师妹提供 go 语言版的 sdk 的。所以用了 gopay。但是 gopay 也只是封装了一部分。还有一部分需要自己写。对应的实现在/app/service/payService/alipay.go里。

定时器

有好多的定时器。这里还是花了挺多时间写的。

  • 百度 token 的刷新。百度接口的 token 是有时间限制的,需要定时刷新。当然简单点可以 5 分钟就检查一次,快过期了就申请刷新。但是我显然不会这么死板,毕竟 token 有效期长达一个月。所以我的定时任务是程序启动时会查一遍,看看过期时间是什么时候,然后根据这个时间创建一个NewTicker。时间到了才会执行。这样避免了多次检查。对应文件在/timer/baiduToken.go.
  • 支付订单状态查询。因为有时回调解析会失败,然后订单设置的有效期到了,他也不会回调。所以需要定时轮询,查询订单状态。判断一下订单是否支付,然后更新订单状态到数据库里。对应文件在/timer/orderStatusQuery.go.
  • 定时删除文件。用户上传的,以及处理完后的结果,都保存在/save目录下。这些文件定时删除。对应文件在/timer/saveFilesDel.go.
  • 定时迁移图片人物和订单明细到备份表里。这里基于两点考虑: ① 图片人物表和订单明细表是经常要查询的,量大了影响查询 ② 每次任务生成的随机 id,虽说重复的概率很低,但是并不保证没有。所以将数据迁移到备份表里,可以避免积累到一定量出现重复的。备份表里是不限制重复的。对应文件在/timer/dataMigrationAndBackup.go.
  • 工具使用量的定时器:工具每次调用,会详细记录调用的结果,包括付款的结果。但是结果是保存在内存中,定时任务每小时汇总一次存到数据库里。然后程序退出前也会执行一次。对应文件在/timer/toolUseSummary.go.如果程序意外死机,那么也会丢失数据。
  • ip 访问次数定时减少的定时器:Ip 管理的初衷就是为了限制每个 ip 的访问频率。定时器目前就是每分钟减少每个 ip 的访问次数。对应文件在/global/ipManage.goCheckIPList()。共有三个任务:① 减少每个 ip 的访问次数 ② 如果该 ip 访问次数为零,则将该 ip 从内存中删除。③ 减少临时封禁 IP 的封禁时长
  • 工具 27 的超时退款定时任务:工具 27 的模式是先付款后使用,但是由于用户经常付了款却迟迟不使用,导致余额被扣。所以需要定时任务轮询未使用的订单,如果超过一定时间未使用,则退款。对应文件在/app/api/docConvert.goQueryOrderAutoRefund27()方法。

然后定时器的总管理位于/timer//timerManager.go里。

;