package com.example.ShadowSocksShare.service.impl; import com.example.ShadowSocksShare.domain.ShadowSocksDetailsEntity; import com.example.ShadowSocksShare.domain.ShadowSocksDetailsRepository; import com.example.ShadowSocksShare.domain.ShadowSocksEntity; import com.example.ShadowSocksShare.domain.ShadowSocksRepository; import com.example.ShadowSocksShare.service.ShadowSocksCrawlerService; import com.example.ShadowSocksShare.service.ShadowSocksSerivce; import com.google.zxing.BarcodeFormat; import com.google.zxing.WriterException; import com.google.zxing.client.j2se.MatrixToImageWriter; import com.google.zxing.common.BitMatrix; import com.google.zxing.qrcode.QRCodeWriter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; import org.apache.commons.lang3.time.DateUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.List; /** * 1. 爬取 目标网站 SS 信息 * 2. SS 信息入库 * 3. 前台页面展示信息 */ @Slf4j @Service public class ShadowSocksSerivceImpl implements ShadowSocksSerivce { @Autowired private ShadowSocksRepository shadowSocksRepository; @Autowired private ShadowSocksDetailsRepository shadowSocksDetailsRepository; /** * 1. 爬取 SS 并入库 * 2. SS 信息入库 */ @Override @Transactional public void crawlerAndSave(ShadowSocksCrawlerService service) { // 1. 爬取 目标网站 SS 信息 long begin = System.currentTimeMillis(); ShadowSocksEntity socksEntity = service.getShadowSocks(); long end = System.currentTimeMillis(); log.debug("抓取执行时间: {} 毫秒", end - begin); // 2. SS 信息入库 if (socksEntity != null) { // shadowSocksRepository.deleteByTargetURL(socksEntity.getTargetURL()); ShadowSocksEntity entity = shadowSocksRepository.findByTargetURL(socksEntity.getTargetURL()); if (entity != null) shadowSocksRepository.delete(entity); shadowSocksRepository.save(socksEntity); log.debug("入库执行时间: {} 毫秒", System.currentTimeMillis() - end); } } /** * 3. 查询 SS 信息 */ @Override public List<ShadowSocksEntity> findAll(Pageable pageable) { Page<ShadowSocksEntity> entities = shadowSocksRepository.findAll(pageable); return entities.getContent(); } /** * 随机查询一条可用 ss 信息 */ @Override public ShadowSocksDetailsEntity findFirstByRandom() { return shadowSocksDetailsRepository.findFirstByValidOrderByRandomAsc(); } /** * 3. 生成 SSR 订阅连接 * 生成规则:https://github.com/ssrbackup/shadowsocks-rss/wiki/SSR-QRcode-scheme */ @Override public String toSSLink(List<ShadowSocksEntity> entities, boolean valid) { if (!entities.isEmpty()) { StringBuilder link = new StringBuilder(); for (ShadowSocksEntity entity : entities) { for (ShadowSocksDetailsEntity detailsEntity : entity.getShadowSocksSet()) { if (valid) { if (detailsEntity.isValid()) { link.append(detailsEntity.getLink()).append("\n"); } } else { link.append(detailsEntity.getLink()).append("\n"); } // log.debug("link ------>{}\n{}", detailsEntity, detailsEntity.getLinkNotSafe()); } } // log.debug(link.toString()); return Base64.encodeBase64String(link.toString().getBytes(StandardCharsets.UTF_8)); } return ""; } /** * SS 有效性检查,获取 SS 信息,判断端口有效性,并更新数据 */ @Override @Transactional public void checkValid() { // 查询 一小时前 更新的数据(即:一小时内更新过的数据,不测试有效性) Date qDate = DateUtils.addHours(new Date(), -1); PageRequest pageRequest = PageRequest.of(0, 100, Sort.Direction.ASC, "id"); Page<ShadowSocksDetailsEntity> entityList = shadowSocksDetailsRepository.findByValidTimeLessThanEqual(qDate, pageRequest); for (ShadowSocksDetailsEntity shadowSocksDetailsEntity : entityList.getContent()) { boolean _valid = ShadowSocksCrawlerService.isReachable(shadowSocksDetailsEntity); // 如果检测结果与库中数据 不一致,则更新数据 if (_valid != shadowSocksDetailsEntity.isValid()) { shadowSocksDetailsEntity.setValid(_valid); shadowSocksDetailsEntity.setValidTime(new Date()); shadowSocksDetailsRepository.save(shadowSocksDetailsEntity); } } } /** * 生成二维码 */ @Override public byte[] createQRCodeImage(String text, int width, int height) throws WriterException, IOException { QRCodeWriter qrCodeWriter = new QRCodeWriter(); BitMatrix bitMatrix = qrCodeWriter.encode(text, BarcodeFormat.QR_CODE, width, height); ByteArrayOutputStream pngOutputStream = new ByteArrayOutputStream(); MatrixToImageWriter.writeToStream(bitMatrix, "PNG", pngOutputStream); byte[] pngData = pngOutputStream.toByteArray(); return pngData; } }