新增短链功能
This commit is contained in:
@@ -0,0 +1,61 @@
|
|||||||
|
package com.accompany.admin.dto.link;
|
||||||
|
|
||||||
|
import com.alibaba.excel.annotation.ExcelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: liaozetao
|
||||||
|
* @date: 2023/9/19 18:34
|
||||||
|
* @description:
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class ShortLinkAdminDto {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短链ID
|
||||||
|
*/
|
||||||
|
@ExcelProperty("短链ID")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短链名称
|
||||||
|
*/
|
||||||
|
@ExcelProperty("短链名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短链
|
||||||
|
*/
|
||||||
|
@ExcelProperty("短链")
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 二维码图片
|
||||||
|
*/
|
||||||
|
@ExcelProperty("二维码图片")
|
||||||
|
private String qrcode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 定向跳转
|
||||||
|
*/
|
||||||
|
@ExcelProperty("定向跳转")
|
||||||
|
private String typeName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成时间
|
||||||
|
*/
|
||||||
|
@ExcelProperty("生成时间")
|
||||||
|
private String createTimeStr;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 累计点击数
|
||||||
|
*/
|
||||||
|
@ExcelProperty("累计点击数")
|
||||||
|
private Integer clickNum;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 累计点击uv
|
||||||
|
*/
|
||||||
|
@ExcelProperty("累计点击uv")
|
||||||
|
private Integer uvNum;
|
||||||
|
}
|
@@ -0,0 +1,28 @@
|
|||||||
|
package com.accompany.admin.vo.link;
|
||||||
|
|
||||||
|
import com.accompany.business.model.link.ShortLink;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: liaozetao
|
||||||
|
* @date: 2023/9/19 15:42
|
||||||
|
* @description:
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@ApiModel
|
||||||
|
public class ShortLinkAdminVo extends ShortLink {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 累计点击数
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("累计点击数")
|
||||||
|
private Integer clickNum = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 累计点击uv
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("累计点击uv")
|
||||||
|
private Integer uvNum = 0;
|
||||||
|
}
|
@@ -67,6 +67,11 @@
|
|||||||
<artifactId>tomcat-embed-core</artifactId>
|
<artifactId>tomcat-embed-core</artifactId>
|
||||||
<scope>compile</scope>
|
<scope>compile</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.zxing</groupId>
|
||||||
|
<artifactId>javase</artifactId>
|
||||||
|
<version>${zxing.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
@@ -0,0 +1,56 @@
|
|||||||
|
package com.accompany.admin.service.link;
|
||||||
|
|
||||||
|
import com.accompany.admin.vo.link.ShortLinkAdminVo;
|
||||||
|
import com.accompany.business.model.link.ShortLink;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import org.springframework.web.context.request.ServletWebRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: liaozetao
|
||||||
|
* @date: 2023/9/19 15:35
|
||||||
|
* @description:
|
||||||
|
*/
|
||||||
|
public interface ShortLinkAdminService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页
|
||||||
|
*
|
||||||
|
* @param linkId
|
||||||
|
* @param linkName
|
||||||
|
* @param page
|
||||||
|
* @param pageSize
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Page<ShortLinkAdminVo> page(Long linkId, String linkName, Integer page, Integer pageSize);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存
|
||||||
|
*
|
||||||
|
* @param param
|
||||||
|
*/
|
||||||
|
void save(ShortLink param);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失效
|
||||||
|
*
|
||||||
|
* @param linkId
|
||||||
|
*/
|
||||||
|
void expire(Long linkId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用
|
||||||
|
*
|
||||||
|
* @param linkId
|
||||||
|
*/
|
||||||
|
void enable(Long linkId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出
|
||||||
|
*
|
||||||
|
* @param linkId
|
||||||
|
* @param linkName
|
||||||
|
* @param servletWebRequest
|
||||||
|
*/
|
||||||
|
void export(Long linkId, String linkName, ServletWebRequest servletWebRequest);
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,186 @@
|
|||||||
|
package com.accompany.admin.service.link.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import cn.hutool.core.date.DatePattern;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.accompany.admin.dto.link.ShortLinkAdminDto;
|
||||||
|
import com.accompany.admin.service.link.ShortLinkAdminService;
|
||||||
|
import com.accompany.admin.util.QrCodeUtil;
|
||||||
|
import com.accompany.admin.vo.link.ShortLinkAdminVo;
|
||||||
|
import com.accompany.business.enums.link.ShortLinkTypeEnum;
|
||||||
|
import com.accompany.business.model.link.ShortLink;
|
||||||
|
import com.accompany.business.model.link.ShortLinkRecord;
|
||||||
|
import com.accompany.business.mybatismapper.link.ShortLinkMapper;
|
||||||
|
import com.accompany.business.mybatismapper.link.ShortLinkRecordMapper;
|
||||||
|
import com.accompany.business.service.api.QinniuService;
|
||||||
|
import com.accompany.common.constant.Constant;
|
||||||
|
import com.accompany.common.model.PageReq;
|
||||||
|
import com.accompany.core.service.SysConfService;
|
||||||
|
import com.alibaba.excel.EasyExcel;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.io.IOUtils;
|
||||||
|
import org.apache.commons.lang3.time.DateFormatUtils;
|
||||||
|
import org.springframework.beans.BeanUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.context.request.ServletWebRequest;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: liaozetao
|
||||||
|
* @date: 2023/9/19 15:35
|
||||||
|
* @description:
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class ShortLinkAdminServiceImpl implements ShortLinkAdminService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ShortLinkMapper shortLinkMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ShortLinkRecordMapper shortLinkRecordMapper;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private QinniuService qinniuService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private SysConfService sysConfService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Page<ShortLinkAdminVo> page(Long linkId, String linkName, Integer currentPage, Integer pageSize) {
|
||||||
|
IPage<ShortLink> page = shortLinkMapper.selectPage(new Page<>(currentPage, pageSize), Wrappers.<ShortLink>lambdaQuery()
|
||||||
|
.eq(linkId != null, ShortLink::getId, linkId)
|
||||||
|
.like(StrUtil.isNotEmpty(linkName), ShortLink::getName, linkName)
|
||||||
|
.orderByDesc(ShortLink::getCreateTime));
|
||||||
|
Page<ShortLinkAdminVo> iPage = new Page<>(currentPage, pageSize);
|
||||||
|
List<ShortLinkAdminVo> admins = new ArrayList<>();
|
||||||
|
List<ShortLink> records = page.getRecords();
|
||||||
|
if (CollectionUtil.isNotEmpty(records)) {
|
||||||
|
for (ShortLink record : records) {
|
||||||
|
ShortLinkAdminVo admin = new ShortLinkAdminVo();
|
||||||
|
BeanUtils.copyProperties(record, admin);
|
||||||
|
admin.setClickNum(shortLinkRecordMapper.selectCount(Wrappers.<ShortLinkRecord>lambdaQuery()
|
||||||
|
.eq(ShortLinkRecord::getLinkId, record.getId())));
|
||||||
|
//UV
|
||||||
|
admin.setUvNum(shortLinkRecordMapper.getUvCount(record.getId()));
|
||||||
|
admins.add(admin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iPage.setTotal(page.getTotal());
|
||||||
|
iPage.setRecords(admins);
|
||||||
|
return iPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void save(ShortLink param) {
|
||||||
|
String linkUrl = sysConfService.getSysConfValueById(Constant.SysConfId.SHORT_LINK_URL);
|
||||||
|
Long id = param.getId();
|
||||||
|
Date now = new Date();
|
||||||
|
param.setUpdateTime(now);
|
||||||
|
if (id == null) {
|
||||||
|
Random random = new Random();
|
||||||
|
String code;
|
||||||
|
String qrcode = StrUtil.EMPTY;
|
||||||
|
for (code = Integer.toHexString(random.nextInt(900001) + 100000);
|
||||||
|
shortLinkMapper.selectCount(Wrappers.<ShortLink>lambdaQuery()
|
||||||
|
.eq(ShortLink::getCode, code)) > 0; code = Integer.toHexString(random.nextInt(900001) + 100000))
|
||||||
|
;
|
||||||
|
code = code.toLowerCase();
|
||||||
|
String url = linkUrl + code;
|
||||||
|
InputStream inputStream = null;
|
||||||
|
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||||
|
QrCodeUtil.encode(url, 200, 200, outputStream);
|
||||||
|
inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
||||||
|
qrcode = qinniuService.uploadByStream(inputStream);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
} finally {
|
||||||
|
IOUtils.closeQuietly(inputStream);
|
||||||
|
}
|
||||||
|
param.setUrl(url);
|
||||||
|
param.setCode(code);
|
||||||
|
param.setQrcode(qrcode);
|
||||||
|
param.setCreateTime(now);
|
||||||
|
shortLinkMapper.insert(param);
|
||||||
|
} else {
|
||||||
|
shortLinkMapper.updateById(param);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void expire(Long linkId) {
|
||||||
|
ShortLink shortLink = shortLinkMapper.selectById(linkId);
|
||||||
|
if (shortLink == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
shortLink.setIsEnabled(Constant.Yes1No0.NO);
|
||||||
|
shortLink.setExpireTime(new Date());
|
||||||
|
shortLinkMapper.updateById(shortLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void enable(Long linkId) {
|
||||||
|
ShortLink shortLink = shortLinkMapper.selectById(linkId);
|
||||||
|
if (shortLink == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
shortLink.setIsEnabled(Constant.Yes1No0.YES);
|
||||||
|
shortLink.setExpireTime(null);
|
||||||
|
shortLinkMapper.updateById(shortLink);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void export(Long linkId, String linkName, ServletWebRequest servletWebRequest) {
|
||||||
|
PageReq req = new PageReq();
|
||||||
|
req.setPage(1);
|
||||||
|
req.setPageSize(1000000);
|
||||||
|
List<ShortLinkAdminDto> datas = new ArrayList<>();
|
||||||
|
Page<ShortLinkAdminVo> page = page(linkId, linkName, 1, 100000);
|
||||||
|
List<ShortLinkAdminVo> records = page.getRecords();
|
||||||
|
if (CollectionUtil.isNotEmpty(records)) {
|
||||||
|
for (ShortLinkAdminVo record : records) {
|
||||||
|
Integer type = record.getType();
|
||||||
|
Date createTime = record.getCreateTime();
|
||||||
|
ShortLinkAdminDto admin = new ShortLinkAdminDto();
|
||||||
|
BeanUtils.copyProperties(record, admin);
|
||||||
|
String typeName = "应用商店";
|
||||||
|
if (type != null) {
|
||||||
|
if (type == ShortLinkTypeEnum.CUSTOM.ordinal()) {
|
||||||
|
typeName = "自定义URL:" + record.getCustomValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
admin.setTypeName(typeName);
|
||||||
|
if (createTime != null) {
|
||||||
|
admin.setCreateTimeStr(DateFormatUtils.format(createTime, DatePattern.NORM_DATETIME_PATTERN));
|
||||||
|
}
|
||||||
|
datas.add(admin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (servletWebRequest.getResponse() != null) {
|
||||||
|
try {
|
||||||
|
//这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
|
||||||
|
servletWebRequest.getResponse().setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||||
|
servletWebRequest.getResponse().setCharacterEncoding("utf-8");
|
||||||
|
//这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
|
||||||
|
String fileName = URLEncoder.encode("短链记录", "UTF-8").replaceAll("\\+", "%20");
|
||||||
|
servletWebRequest.getResponse().setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
|
||||||
|
EasyExcel.write(servletWebRequest.getResponse().getOutputStream(), ShortLinkAdminDto.class).sheet("短链记录").doWrite(datas);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,42 @@
|
|||||||
|
package com.accompany.admin.util;
|
||||||
|
|
||||||
|
import com.google.zxing.BarcodeFormat;
|
||||||
|
import com.google.zxing.EncodeHintType;
|
||||||
|
import com.google.zxing.MultiFormatWriter;
|
||||||
|
import com.google.zxing.client.j2se.MatrixToImageWriter;
|
||||||
|
import com.google.zxing.common.BitMatrix;
|
||||||
|
import com.google.zxing.qrcode.decoder.ErrorCorrectionLevel;
|
||||||
|
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.util.Hashtable;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 二维码生成工具类
|
||||||
|
*/
|
||||||
|
public class QrCodeUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成二维码
|
||||||
|
* David
|
||||||
|
*
|
||||||
|
* @param url url 网址
|
||||||
|
* @param width 二维码宽度
|
||||||
|
* @param height 二维码高度
|
||||||
|
*/
|
||||||
|
public static void encode(String url, int width, int height, OutputStream outputStream) {
|
||||||
|
Map<EncodeHintType, Object> hints = new Hashtable<>();
|
||||||
|
// 指定纠错等级
|
||||||
|
hints.put(EncodeHintType.ERROR_CORRECTION, ErrorCorrectionLevel.L);
|
||||||
|
// 指定编码格式
|
||||||
|
hints.put(EncodeHintType.CHARACTER_SET, "UTF8");
|
||||||
|
try {
|
||||||
|
BitMatrix bitMatrix = new MultiFormatWriter().encode(url, BarcodeFormat.QR_CODE, width, height, hints);
|
||||||
|
MatrixToImageWriter.writeToStream(bitMatrix, "png", outputStream);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,101 @@
|
|||||||
|
package com.accompany.admin.controller.link;
|
||||||
|
|
||||||
|
import com.accompany.admin.service.link.ShortLinkAdminService;
|
||||||
|
import com.accompany.admin.vo.link.ShortLinkAdminVo;
|
||||||
|
import com.accompany.business.model.link.ShortLink;
|
||||||
|
import com.accompany.common.model.PageReq;
|
||||||
|
import com.accompany.common.result.BusiResult;
|
||||||
|
import com.accompany.common.result.PageResult;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
import org.springframework.web.context.request.ServletWebRequest;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: liaozetao
|
||||||
|
* @date: 2023/9/19 15:41
|
||||||
|
* @description:
|
||||||
|
*/
|
||||||
|
@Api(tags = "短链管理")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/admin/short/link")
|
||||||
|
public class ShortLinkAdminController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ShortLinkAdminService shortLinkAdminService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页
|
||||||
|
*
|
||||||
|
* @param linkId
|
||||||
|
* @param linkName
|
||||||
|
* @param req
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation("分页")
|
||||||
|
@GetMapping("page")
|
||||||
|
public PageResult<ShortLinkAdminVo> page(Long linkId, String linkName, PageReq req) {
|
||||||
|
return new PageResult<>(shortLinkAdminService.page(linkId, linkName, req.getPage(), req.getPageSize()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存
|
||||||
|
*
|
||||||
|
* @param param
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation("保存")
|
||||||
|
@PostMapping("save")
|
||||||
|
public BusiResult<Void> save(ShortLink param) {
|
||||||
|
shortLinkAdminService.save(param);
|
||||||
|
return BusiResult.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失效
|
||||||
|
*
|
||||||
|
* @param linkId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation("失效")
|
||||||
|
@GetMapping("expire")
|
||||||
|
public BusiResult<Void> expire(Long linkId) {
|
||||||
|
shortLinkAdminService.expire(linkId);
|
||||||
|
return BusiResult.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 启用
|
||||||
|
*
|
||||||
|
* @param linkId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation("启用")
|
||||||
|
@GetMapping("enable")
|
||||||
|
public BusiResult<Void> enable(Long linkId) {
|
||||||
|
shortLinkAdminService.enable(linkId);
|
||||||
|
return BusiResult.success();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导出
|
||||||
|
*
|
||||||
|
* @param linkId
|
||||||
|
* @param linkName
|
||||||
|
* @param request
|
||||||
|
* @param response
|
||||||
|
*/
|
||||||
|
@ApiOperation("导出")
|
||||||
|
@PostMapping("export")
|
||||||
|
public void export(Long linkId, String linkName, HttpServletRequest request, HttpServletResponse response) {
|
||||||
|
shortLinkAdminService.export(linkId, linkName, new ServletWebRequest(request, response));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,282 @@
|
|||||||
|
<section class="content">
|
||||||
|
<div class="box box-primary">
|
||||||
|
<div class="box-body">
|
||||||
|
<!-- Content Header (Page header) -->
|
||||||
|
<section class="content-header">
|
||||||
|
<h1 id="itemTitle"></h1>
|
||||||
|
</section>
|
||||||
|
<!-- .content -->
|
||||||
|
<div id="table"></div>
|
||||||
|
<div id="toolbar">
|
||||||
|
<form id="searchForm" action="/admin/short/link/export" method="get" target="_blank">
|
||||||
|
<div class="col-sm-12">
|
||||||
|
<label for="linkId" class="col-sm-2 control-label">短链id:</label>
|
||||||
|
<div class="col-sm-2">
|
||||||
|
<input type="text" class="form-control" name="linkId" id="linkId">
|
||||||
|
</div>
|
||||||
|
<label for="linkName" class="col-sm-2 control-label">短链名称:</label>
|
||||||
|
<div class="col-sm-2">
|
||||||
|
<input type="text" class="form-control" name="linkName" id="linkName">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<button id="btnSearch" class="btn btn-default">
|
||||||
|
<i class="glyphicon glyphicon-search"></i>查询
|
||||||
|
</button>
|
||||||
|
<button id="btnExport" class="btn btn-default">
|
||||||
|
<i class="glyphicon glyphicon-Export"></i>导出
|
||||||
|
</button>
|
||||||
|
<button id="btnAdd" class="btn btn-default">
|
||||||
|
<i class="glyphicon glyphicon-plus-sign"></i>生成
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="modal fade" id="editModal" tabindex="-1" role="dialog" aria-labelledby="modalLabel">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
<h4 class="modal-title" id="modalLabel">生成短链</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<form class="form-horizontal">
|
||||||
|
<input type="hidden" name="id" id="id"/>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="name" class="col-sm-3 control-label">短链名称:</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="text" class="form-control" id="name">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<label for="type" class="col-sm-3 control-label">定向跳转:</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<select name="type" id="type" class="form-control validate[required]">
|
||||||
|
<option value="0">应用商店</option>
|
||||||
|
<option value="1">自定义URL</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="customValue" class="col-sm-3 control-label">自定义:</label>
|
||||||
|
<div class="col-sm-9">
|
||||||
|
<input type="text" class="form-control" id="customValue">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
|
||||||
|
<button type="button" class="btn btn-primary" id="save">生成</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="tipModal" tabindex="-1" role="dialog" aria-labelledby="modalLabel">
|
||||||
|
<div class="modal-dialog" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4 class="modal-title">提示信息</h4>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body" id="tipMsg"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
$(function() {
|
||||||
|
$('#table').bootstrapTable('destroy');
|
||||||
|
$('#table').bootstrapTable({
|
||||||
|
columns: [
|
||||||
|
{field: 'id', title: '短链ID', align: 'center', width: '5%'},
|
||||||
|
{field: 'name', title: '短链名称', align: 'center', width: '5%'},
|
||||||
|
{field: 'url', title: '短链', align: 'center', width: '5%'},
|
||||||
|
{
|
||||||
|
field: 'qrcode',
|
||||||
|
title: '二维码图片',
|
||||||
|
align: 'center',
|
||||||
|
width: '5%',
|
||||||
|
formatter: function (val, row, index) {
|
||||||
|
return "<img src='"+val+"' width='40' height='40'>";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
field: 'type',
|
||||||
|
title: '定向跳转',
|
||||||
|
align: 'center',
|
||||||
|
width: '5%',
|
||||||
|
formatter: function (val, row, index) {
|
||||||
|
var value = '';
|
||||||
|
if (type == 0) {
|
||||||
|
value = '应用商店';
|
||||||
|
} else {
|
||||||
|
value = '自定义URL<br/>' + row.customValue;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{field: 'createTime', title: '生成时间', align: 'center', width: '5%'},
|
||||||
|
{field: 'clickNum', title: '累计点击数', align: 'center', width: '5%'},
|
||||||
|
{field: 'uvNum', title: '累计点击uv', align: 'center', width: '5%'},
|
||||||
|
{
|
||||||
|
field: 'id',
|
||||||
|
title: '操作',
|
||||||
|
align: 'center',
|
||||||
|
width: '5%',
|
||||||
|
valign: 'middle',
|
||||||
|
formatter: function (val, row, index) {
|
||||||
|
var value = '<button class="btn btn-sm btn-default opt-edit" data-index="' + index + '">编辑</button>';
|
||||||
|
if (row.isEnabled == 0) {
|
||||||
|
value += '<button class="btn btn-sm btn-default opt-enable" data-index="' + index + '">启用</button>';
|
||||||
|
} else if (row.isEnabled == 1) {
|
||||||
|
value += '<button class="btn btn-sm btn-default opt-expire" data-index="' + index + '">失效</button>';
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
cache: false,
|
||||||
|
striped: true,
|
||||||
|
showRefresh: false,
|
||||||
|
pageSize: 20,
|
||||||
|
pagination: true,
|
||||||
|
pageList: [20, 50, 100],
|
||||||
|
search: false,
|
||||||
|
sidePagination: "server", //表示服务端请求
|
||||||
|
//设置为undefined可以获取pageNumber,pageSize,searchText,sortName,sortOrder
|
||||||
|
//设置为limit可以获取limit, offset, search, sort, order
|
||||||
|
queryParamsType: "undefined",
|
||||||
|
queryParams: function queryParams(params) { //设置查询参数
|
||||||
|
var param = {
|
||||||
|
page: params.pageNumber,
|
||||||
|
pageSize: params.pageSize,
|
||||||
|
linkId: $('#linkId').val(),
|
||||||
|
linkName: $('#linkName').val(),
|
||||||
|
};
|
||||||
|
return param;
|
||||||
|
},
|
||||||
|
toolbar: '#toolbar',
|
||||||
|
url: '/admin/short/link/page',
|
||||||
|
onLoadSuccess: function () { //加载成功时执行
|
||||||
|
console.log("load success");
|
||||||
|
},
|
||||||
|
onLoadError: function () { //加载失败时执行
|
||||||
|
console.log("load fail");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//导出功能
|
||||||
|
$("#btnExport").on('click',function () {
|
||||||
|
$("#searchForm").submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 查询刷新
|
||||||
|
$('#btnSearch').on('click', function () {
|
||||||
|
TableHelper.doRefresh('#table');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#btnAdd').on('click', function () {
|
||||||
|
$("#id").val('');
|
||||||
|
$("#name").val('');
|
||||||
|
$("#type").val('');
|
||||||
|
$("#customValue").val('');
|
||||||
|
$("#editModal").modal('show');
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#table').on('click', '.opt-edit', function () {
|
||||||
|
const currentData = $('#table').bootstrapTable('getData')[$(this).data('index')];
|
||||||
|
$('#id').val(currentData.id);
|
||||||
|
$('#name').val(currentData.name);
|
||||||
|
$('#type').val(currentData.type);
|
||||||
|
$('#customValue').val(currentData.customValue);
|
||||||
|
$("#editModal").modal('show');
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#save").click(function () {
|
||||||
|
const msg = '确定要保存吗?';
|
||||||
|
if (confirm(msg)) {
|
||||||
|
const data = {
|
||||||
|
id: $('#id').val(),
|
||||||
|
name: $('#name').val(),
|
||||||
|
type: $('#type').val(),
|
||||||
|
customValue: $('#customValue').val(),
|
||||||
|
}
|
||||||
|
$.ajax({
|
||||||
|
type: "post",
|
||||||
|
url: "/admin/short/link/save",
|
||||||
|
data: data,
|
||||||
|
dataType: "json",
|
||||||
|
success: function (json) {
|
||||||
|
if (json.success == 'true' || json.code == 200) {
|
||||||
|
$("#tipMsg").text("保存成功");
|
||||||
|
$("#tipModal").modal('show');
|
||||||
|
TableHelper.doRefresh("#table");
|
||||||
|
$("#editModal").modal('hide');
|
||||||
|
} else {
|
||||||
|
$("#tipMsg").text("保存失败." + json.message);
|
||||||
|
$("#tipModal").modal('show');
|
||||||
|
$("#editModal").modal('hide');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 失效
|
||||||
|
$('#table').on('click', '.opt-expire', function () {
|
||||||
|
const currentData = $('#table').bootstrapTable('getData')[$(this).data('index')];
|
||||||
|
var id = currentData.id;
|
||||||
|
const msg = '确定要失效吗?'
|
||||||
|
if (confirm(msg)) {
|
||||||
|
$.ajax({
|
||||||
|
type: "get",
|
||||||
|
url: "/admin/short/link/expire?linkId=" + id,
|
||||||
|
dataType: "json",
|
||||||
|
success: function (json) {
|
||||||
|
if (json.success == 'true' || json.code == 200) {
|
||||||
|
$("#tipMsg").text("操作成功");
|
||||||
|
$("#tipModal").modal('show');
|
||||||
|
TableHelper.doRefresh("#table");
|
||||||
|
} else {
|
||||||
|
$("#tipMsg").text("操作失败." + json.message);
|
||||||
|
$("#tipModal").modal('show');
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (e){
|
||||||
|
serverError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 启用
|
||||||
|
$('#table').on('click', '.opt-enable', function () {
|
||||||
|
const currentData = $('#table').bootstrapTable('getData')[$(this).data('index')];
|
||||||
|
var id = currentData.id;
|
||||||
|
const msg = '确定要启用吗?'
|
||||||
|
if (confirm(msg)) {
|
||||||
|
$.ajax({
|
||||||
|
type: "get",
|
||||||
|
url: "/admin/short/link/enable?linkId=" + id,
|
||||||
|
dataType: "json",
|
||||||
|
success: function (json) {
|
||||||
|
if (json.success == 'true' || json.code == 200) {
|
||||||
|
$("#tipMsg").text("操作成功");
|
||||||
|
$("#tipModal").modal('show');
|
||||||
|
TableHelper.doRefresh("#table");
|
||||||
|
} else {
|
||||||
|
$("#tipMsg").text("操作失败." + json.message);
|
||||||
|
$("#tipModal").modal('show');
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: function (e){
|
||||||
|
serverError(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
@@ -1,16 +0,0 @@
|
|||||||
/**
|
|
||||||
* Simplified Chinese translation for bootstrap-datetimepicker
|
|
||||||
* Yuan Cheung <advanimal@gmail.com>
|
|
||||||
*/
|
|
||||||
;(function($){
|
|
||||||
$.fn.datetimepicker.dates['zh-CN'] = {
|
|
||||||
days: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六", "星期日"],
|
|
||||||
daysShort: ["周日", "周一", "周二", "周三", "周四", "周五", "周六", "周日"],
|
|
||||||
daysMin: ["日", "一", "二", "三", "四", "五", "六", "日"],
|
|
||||||
months: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
|
|
||||||
monthsShort: ["一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"],
|
|
||||||
today: "今天",
|
|
||||||
suffix: [],
|
|
||||||
meridiem: ["上午", "下午"]
|
|
||||||
};
|
|
||||||
}(jQuery));
|
|
@@ -1824,6 +1824,10 @@ public class Constant {
|
|||||||
*/
|
*/
|
||||||
public static final String SMS_SDK_TYPE = "sms_sdk_type";
|
public static final String SMS_SDK_TYPE = "sms_sdk_type";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短链
|
||||||
|
*/
|
||||||
|
public static final String SHORT_LINK_URL = "short_link_url";
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class ActiveMq {
|
public static class ActiveMq {
|
||||||
|
@@ -0,0 +1,19 @@
|
|||||||
|
package com.accompany.business.enums.link;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: liaozetao
|
||||||
|
* @date: 2023/9/19 17:03
|
||||||
|
* @description:
|
||||||
|
*/
|
||||||
|
public enum ShortLinkTypeEnum {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 应用商店
|
||||||
|
*/
|
||||||
|
STORE,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义链接
|
||||||
|
*/
|
||||||
|
CUSTOM;
|
||||||
|
}
|
@@ -0,0 +1,91 @@
|
|||||||
|
package com.accompany.business.model.link;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: liaozetao
|
||||||
|
* @date: 2023/9/19 15:15
|
||||||
|
* @description:
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("short_link")
|
||||||
|
public class ShortLink {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("主键")
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编码
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("编码")
|
||||||
|
private String code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("名称")
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短链
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("短链")
|
||||||
|
private String url;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 二维码
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("二维码")
|
||||||
|
private String qrcode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 类型
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("类型")
|
||||||
|
private Integer type;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义URL
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("自定义URL")
|
||||||
|
private String customValue;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用 0 失效 1 启用
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("是否启用 0 失效 1 启用")
|
||||||
|
private Integer isEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 失效时间
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("失效时间")
|
||||||
|
private Date expireTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("创建时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("更新时间")
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,77 @@
|
|||||||
|
package com.accompany.business.model.link;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: liaozetao
|
||||||
|
* @date: 2023/9/19 15:15
|
||||||
|
* @description:
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("short_link_record")
|
||||||
|
public class ShortLinkRecord {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主键
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("主键")
|
||||||
|
@TableId(type = IdType.AUTO)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 短链ID
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("短链ID")
|
||||||
|
private Long linkId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("系统")
|
||||||
|
private String os;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 系统版本
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("系统版本")
|
||||||
|
private String osVersion;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设备
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("设备")
|
||||||
|
private String model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IP
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("IP")
|
||||||
|
private String clientIp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户端时间
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("客户端时间")
|
||||||
|
private Date clientTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建时间
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("创建时间")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
@ApiModelProperty("更新时间")
|
||||||
|
private Date updateTime;
|
||||||
|
|
||||||
|
}
|
@@ -88,6 +88,12 @@
|
|||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-boot-test-autoconfigure</artifactId>
|
<artifactId>spring-boot-test-autoconfigure</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<!-- https://mvnrepository.com/artifact/eu.bitwalker/UserAgentUtils -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>eu.bitwalker</groupId>
|
||||||
|
<artifactId>UserAgentUtils</artifactId>
|
||||||
|
<version>${bitwalker.version}</version>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
@@ -0,0 +1,12 @@
|
|||||||
|
package com.accompany.business.mybatismapper.link;
|
||||||
|
|
||||||
|
import com.accompany.business.model.link.ShortLink;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: liaozetao
|
||||||
|
* @date: 2023/9/19 15:26
|
||||||
|
* @description:
|
||||||
|
*/
|
||||||
|
public interface ShortLinkMapper extends BaseMapper<ShortLink> {
|
||||||
|
}
|
@@ -0,0 +1,21 @@
|
|||||||
|
package com.accompany.business.mybatismapper.link;
|
||||||
|
|
||||||
|
import com.accompany.business.model.link.ShortLinkRecord;
|
||||||
|
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
|
||||||
|
import org.apache.ibatis.annotations.Param;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: liaozetao
|
||||||
|
* @date: 2023/9/19 15:26
|
||||||
|
* @description:
|
||||||
|
*/
|
||||||
|
public interface ShortLinkRecordMapper extends BaseMapper<ShortLinkRecord> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取uv数
|
||||||
|
* @param linkId
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
Integer getUvCount(@Param("linkId") Long linkId);
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,12 @@
|
|||||||
|
package com.accompany.business.service.link;
|
||||||
|
|
||||||
|
import com.accompany.business.model.link.ShortLinkRecord;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: liaozetao
|
||||||
|
* @date: 2023/9/19 15:27
|
||||||
|
* @description:
|
||||||
|
*/
|
||||||
|
public interface ShortLinkRecordService extends IService<ShortLinkRecord> {
|
||||||
|
}
|
@@ -0,0 +1,20 @@
|
|||||||
|
package com.accompany.business.service.link;
|
||||||
|
|
||||||
|
import com.accompany.business.model.link.ShortLink;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.IService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: liaozetao
|
||||||
|
* @date: 2023/9/19 15:27
|
||||||
|
* @description:
|
||||||
|
*/
|
||||||
|
public interface ShortLinkService extends IService<ShortLink> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点击
|
||||||
|
* @param code
|
||||||
|
* @param clientTime
|
||||||
|
*/
|
||||||
|
Integer click(String code, String clientTime);
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,18 @@
|
|||||||
|
package com.accompany.business.service.link.impl;
|
||||||
|
|
||||||
|
import com.accompany.business.model.link.ShortLinkRecord;
|
||||||
|
import com.accompany.business.mybatismapper.link.ShortLinkRecordMapper;
|
||||||
|
import com.accompany.business.service.link.ShortLinkRecordService;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: liaozetao
|
||||||
|
* @date: 2023/9/19 15:28
|
||||||
|
* @description:
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class ShortLinkRecordServiceImpl extends ServiceImpl<ShortLinkRecordMapper, ShortLinkRecord> implements ShortLinkRecordService {
|
||||||
|
}
|
@@ -0,0 +1,100 @@
|
|||||||
|
package com.accompany.business.service.link.impl;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollectionUtil;
|
||||||
|
import cn.hutool.core.date.DatePattern;
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.accompany.business.enums.link.ShortLinkTypeEnum;
|
||||||
|
import com.accompany.business.model.link.ShortLink;
|
||||||
|
import com.accompany.business.model.link.ShortLinkRecord;
|
||||||
|
import com.accompany.business.mybatismapper.link.ShortLinkMapper;
|
||||||
|
import com.accompany.business.mybatismapper.link.ShortLinkRecordMapper;
|
||||||
|
import com.accompany.business.service.link.ShortLinkService;
|
||||||
|
import com.accompany.common.utils.DateTimeUtil;
|
||||||
|
import com.accompany.common.utils.IPUtils;
|
||||||
|
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||||
|
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||||
|
import eu.bitwalker.useragentutils.Browser;
|
||||||
|
import eu.bitwalker.useragentutils.OperatingSystem;
|
||||||
|
import eu.bitwalker.useragentutils.UserAgent;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.http.HttpHeaders;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.context.request.RequestAttributes;
|
||||||
|
import org.springframework.web.context.request.RequestContextHolder;
|
||||||
|
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||||
|
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: liaozetao
|
||||||
|
* @date: 2023/9/19 15:28
|
||||||
|
* @description:
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
@Service
|
||||||
|
public class ShortLinkServiceImpl extends ServiceImpl<ShortLinkMapper, ShortLink> implements ShortLinkService {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ShortLinkRecordMapper shortLinkRecordMapper;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Integer click(String code, String clientTimeStr) {
|
||||||
|
int shortLinkType = ShortLinkTypeEnum.CUSTOM.ordinal();
|
||||||
|
try {
|
||||||
|
Long linkId = null;
|
||||||
|
List<ShortLink> shortLinks = baseMapper.selectList(Wrappers.<ShortLink>lambdaQuery()
|
||||||
|
.eq(ShortLink::getCode, code));
|
||||||
|
if (CollectionUtil.isNotEmpty(shortLinks)) {
|
||||||
|
ShortLink shortLink = shortLinks.get(0);
|
||||||
|
linkId = shortLink.getId();
|
||||||
|
shortLinkType = shortLink.getType();
|
||||||
|
}
|
||||||
|
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
|
||||||
|
if (requestAttributes == null) {
|
||||||
|
return shortLinkType;
|
||||||
|
}
|
||||||
|
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
|
||||||
|
HttpServletRequest request = servletRequestAttributes.getRequest();
|
||||||
|
String os = StrUtil.EMPTY;
|
||||||
|
String osVersion = StrUtil.EMPTY;
|
||||||
|
String model = StrUtil.EMPTY;
|
||||||
|
//设备
|
||||||
|
String userAgentStr = request.getHeader(HttpHeaders.USER_AGENT);
|
||||||
|
if (StrUtil.isNotEmpty(userAgentStr)) {
|
||||||
|
UserAgent userAgent = UserAgent.parseUserAgentString(userAgentStr);
|
||||||
|
OperatingSystem operatingSystem = userAgent.getOperatingSystem();
|
||||||
|
os = operatingSystem.getName();
|
||||||
|
osVersion = String.valueOf(operatingSystem.getId());
|
||||||
|
model = operatingSystem.getDeviceType().getName();
|
||||||
|
}
|
||||||
|
//客户端时间
|
||||||
|
Date clientTime = new Date();
|
||||||
|
if (StrUtil.isNotEmpty(clientTimeStr)) {
|
||||||
|
try {
|
||||||
|
clientTime = DateTimeUtil.convertStrToDate(clientTimeStr, DatePattern.NORM_DATETIME_PATTERN);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//IP
|
||||||
|
String ipAddress = IPUtils.getRealIpAddress(request);
|
||||||
|
//记录
|
||||||
|
ShortLinkRecord record = new ShortLinkRecord();
|
||||||
|
record.setLinkId(linkId);
|
||||||
|
record.setClientIp(ipAddress);
|
||||||
|
record.setOs(os);
|
||||||
|
record.setOsVersion(osVersion);
|
||||||
|
record.setModel(model);
|
||||||
|
record.setClientTime(clientTime);
|
||||||
|
record.setCreateTime(new Date());
|
||||||
|
record.setUpdateTime(new Date());
|
||||||
|
shortLinkRecordMapper.insert(record);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error(e.getMessage(), e);
|
||||||
|
}
|
||||||
|
return shortLinkType;
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,5 @@
|
|||||||
|
<?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.accompany.business.mybatismapper.link.ShortLinkMapper" >
|
||||||
|
|
||||||
|
</mapper>
|
@@ -0,0 +1,11 @@
|
|||||||
|
<?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.accompany.business.mybatismapper.link.ShortLinkRecordMapper">
|
||||||
|
<select id="getUvCount" resultType="java.lang.Integer">
|
||||||
|
select
|
||||||
|
count(distinct concat(slr.client_ip, '_', slr.model))
|
||||||
|
from
|
||||||
|
short_link_record as slr
|
||||||
|
where slr.link_id = #{linkId}
|
||||||
|
</select>
|
||||||
|
</mapper>
|
@@ -0,0 +1,35 @@
|
|||||||
|
package com.accompany.business.controller.link;
|
||||||
|
|
||||||
|
import com.accompany.business.service.link.ShortLinkService;
|
||||||
|
import com.accompany.common.result.BusiResult;
|
||||||
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.web.bind.annotation.GetMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author: liaozetao
|
||||||
|
* @date: 2023/9/19 19:00
|
||||||
|
* @description:
|
||||||
|
*/
|
||||||
|
@Api(tags = "短链")
|
||||||
|
@RestController
|
||||||
|
@RequestMapping("/short/link")
|
||||||
|
public class ShortLinkController {
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ShortLinkService shortLinkService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 点击
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation("点击")
|
||||||
|
@GetMapping("click")
|
||||||
|
public BusiResult<Void> click(String code, String clientTime) {
|
||||||
|
shortLinkService.click(code, clientTime);
|
||||||
|
return BusiResult.success();
|
||||||
|
}
|
||||||
|
}
|
@@ -97,6 +97,8 @@
|
|||||||
<hippo4j-core.version>1.5.0</hippo4j-core.version>
|
<hippo4j-core.version>1.5.0</hippo4j-core.version>
|
||||||
<android-json.version>0.0.20131108.vaadin1</android-json.version>
|
<android-json.version>0.0.20131108.vaadin1</android-json.version>
|
||||||
<bcprov-jdk15on.version>1.64</bcprov-jdk15on.version>
|
<bcprov-jdk15on.version>1.64</bcprov-jdk15on.version>
|
||||||
|
<zxing.version>3.2.0</zxing.version>
|
||||||
|
<bitwalker.version>1.20</bitwalker.version>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<dependencyManagement>
|
<dependencyManagement>
|
||||||
|
Reference in New Issue
Block a user