介绍
这是一个工具类小程序,由我个人开发,其主要功能就是帮助用户去除视频的水印(这里的水印是现在一些热门平台如dy、ks、xhs等下载视频和图片上的平台作者水印)。
小程序通过微信接口wxLogin中返回的openId作为识别用户的标志,从而建立用户的后台管理,实现对用户日使用次数的调整、用户的使用情况的数据收集。
小程序端和后端通过jwt生成的token进行接口验证和用户信息传递。
小程序可开通微信流量主,后台管理系统可设置是否开启广告和banner广告、激励视频广告、插屏广告、视频广告的adId设置。
小程序去水印功能可通过本地去水印功能或者第三方接口实现。(注:本地去水印功能是通过python爬虫实现的)
小程序配置通告功能,可在后台管理系统对通告进行增删改查操作。
后端一些接口使用Redis进行数据缓存,减少对数据库的压力和第三方接口使用次数的浪费。
后台管理系统现实现功能有:每日数据的统计和图标展示、用户管理(数据库)、每日用量管理(redis)、解析记录、新增download域名、系统参数设置、通告设置。
项目已经全部开源到我的Gitee,可自行下载:llgululu去水印: 前端技术:uniapp、uView。后端技术:Java、SpringBoot、MybatisPlus、Redis、Mysql。这是一个工具类小程序,由我个人开发,其主要功能就是帮助用户去除视频的水印(这里的水印是现在一些热门平台如dy、ks、xhs等下载视频和图片上的平台作者水印)。
软件架构
小程序端由uniapp+uview进行构建。
后端使用springboot+mybatisPlus+redis+mysql。
后台管理系统使用layui+thymeleaf构建页面,使用echarts实现数据图像化,利用媒体查询适配移动端,使用ajax进行前后端数据交互。
安装教程
- removeMaskWx 是小程序前端目录,removeMask 是后端及后台管理系统目录。
- 先运行removemask.sql文件,建立数据库,本地也要有redis服务。
- removeMask目录下,修改yml文件中的mysql和redis的名称、密码。本地去水印功能只支持抖音,如果要使用第三方接口,请自行开通并在yal文件上配置。
- resource文件夹下python文件夹里面的两个python文件里面的cookie设置为你自己的cookie,然后修改util文件夹下HomeAnalysisUtil.java文件中的两个python文件的地址(如果使用第三方接口可忽略)。
- 启动springboot服务,浏览器输入 域名:8004/login/admin/login(本地运行则为 localhost:8004/login/admin/login) ,进入后台管理系统,输入账号密码,默认都为admin,进入页面系统设置下的参数设置,修改后台登录的账号和密码(管理员账号设置),修改系统设置里面的AppId和AppSecret为你小程序的,如果你的小程序没有开通流量主就不用开启广告,如果开通了流量主,请先填写每种广告对应的adId再开启。(这里后台就配置好了)
- removeMaskWx 目录用HbulidX打开,修改common.js文件里面的baseURL为你的域名(本地则为localhost:8004)。再修改manifest.json文件微信配置下的appid为你小程序的appid。然后点击发行到微信小程序,进入微信小程序查看前后端是否交互成功即可。
页面展示
小程序页面展示
后台管理页面展示
部分代码展示
微信小程序实现用户登录认证
package com.llgululu.app.controller;
import com.llgululu.app.entity.Admin;
import com.llgululu.app.entity.Setting;
import com.llgululu.app.entity.Total;
import com.llgululu.app.entity.Userinfo;
import com.llgululu.app.service.*;
import com.llgululu.app.util.R;
import com.llgululu.app.util.RedisUtil;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.llgululu.app.util.JWTUtil.createJwt;
import static com.llgululu.app.util.RestTemplateUtil.getUserOpenID;
/**
* <p>
* 前端控制器
* </p>
*
* @author llgululu
* @since 2023-08-27
*/
@RestController
@CrossOrigin //允许跨域请求
@RequestMapping("/login")
public class LoginController {
private final ISettingService iSettingService;
private final IRecordService iRecordService;
private final IUserinfoService iUserinfoService;
private final IAdminService iAdminService;
private final RedisUtil redisUtil;
private final ITotalService iTotalService;
public LoginController(ISettingService iSettingService, IRecordService iRecordService, IUserinfoService iUserinfoService, IAdminService iAdminService, RedisUtil redisUtil, ITotalService iTotalService) {
this.iSettingService = iSettingService;
this.iRecordService = iRecordService;
this.iUserinfoService = iUserinfoService;
this.iAdminService = iAdminService;
this.redisUtil = redisUtil;
this.iTotalService = iTotalService;
}
@RequestMapping(value = "/user/login")
@ResponseBody
public ResponseEntity<Object> userLogin(@RequestParam(value = "code") String code) {
HttpHeaders headers = new HttpHeaders();
Map<Object, Object> map = new HashMap<>();
Map<Object, Object> sysMap = new HashMap<>();
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
Setting setting;
if (redisUtil.hasKey("sys_setting")) {
sysMap = redisUtil.hmget("sys_setting");
setting = (Setting) sysMap.get("setting");
} else {
setting = iSettingService.getById(1);
sysMap.put("setting", setting);
redisUtil.hmset("sys_setting", sysMap, 60 * 60);
}
String openid = getUserOpenID(setting.getSeAppId(), setting.getSeAppSecret(), code);
if (openid == null) {
return new ResponseEntity<>(R.error(400, "code错误!"), headers, HttpStatus.BAD_REQUEST);
}
Userinfo user = iUserinfoService.checkUserIsExist(openid, setting.getSeUserDailyCount());
String token = createJwt(user);
redisUtil.checkUserIsExist(user);
map.put("token", token);
return new ResponseEntity<>(R.ok("登录成功", map), headers, HttpStatus.OK);
}
@Scheduled(cron = "0 10 3 * * *")
public void dailyJob() {
int totalRecords = (int) iRecordService.count();
int totalUsers = (int) iUserinfoService.count();
Total total = new Total();
total.settTotalUse(totalRecords);
total.settUser(totalUsers);
long date = new Date().getTime();
total.settTime(new Date(date - 24 * 60 * 60 * 1000));
iTotalService.save(total);
List<Userinfo> list = iUserinfoService.list();
redisUtil.setUserInfoMap(list);
}
@RequestMapping(value = "/admin/login", produces = "text/html; charset=UTF-8")
public ModelAndView login(ModelAndView model) {
model.setViewName("login");
return model;
}
@RequestMapping(value = "/admin/checkLogin")
@ResponseBody
public ResponseEntity<Object> adminCheckLogin(HttpServletRequest request, @RequestParam(value = "adminName") String adminName, @RequestParam(value = "password") String password) {
HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
Admin admin = iAdminService.checkAdminLogin(adminName, password);
if (admin == null) {
return new ResponseEntity<>(R.ok(1002, "账号密码错误!"), headers, HttpStatus.OK);
} else {
request.getSession().setAttribute("sysadmin", admin);
return new ResponseEntity<>(R.ok(1001, "登录成功"), headers, HttpStatus.OK);
}
}
}