whyczh
2021-12-11 9d6826c46fc1721a2c63b3bc97db64e1d0e7ba00
注册码License接口添加
6个文件已添加
6个文件已修改
609 ■■■■■ 已修改文件
src/main/java/com/whyc/config/StaticResourceConfig.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/controller/LicenseController.java 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/exception/CryptException.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/mapper/LicenseMapper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/pojo/EleTmp.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/pojo/License.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/service/LicenseService.java 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/util/AESUtil.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/util/SerialNumberUtil.java 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/config/pri_key.ksm 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/config/pub_key.ksm 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/resources/mapper/LicenseMapper.xml 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/main/java/com/whyc/config/StaticResourceConfig.java
@@ -22,6 +22,7 @@
        registry.addResourceHandler("*.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
        registry.addResourceHandler("/service-worker.js").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/license/**").addResourceLocations("classpath:/META-INF/resources/config/");
        //registry.addResourceHandler("/favicon.ico").addResourceLocations("classpath:/META-INF/resources/");
        super.addResourceHandlers(registry);
        //registry.addResourceHandler("doc.html").addResourceLocations("classpath:/META-INF/resources/");
src/main/java/com/whyc/controller/LicenseController.java
New file
@@ -0,0 +1,161 @@
package com.whyc.controller;
import com.whyc.dto.Response;
import com.whyc.encryption.ByteConvertUtil;
import com.whyc.encryption.SM2;
import com.whyc.pojo.License;
import com.whyc.service.LicenseService;
import com.whyc.util.AESUtil;
import com.whyc.util.SerialNumberUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.bouncycastle.math.ec.ECPoint;
import org.springframework.util.ClassUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.math.BigInteger;
@RequestMapping("license")
@RestController
@Api(tags = "注册码验证")
public class LicenseController {
    @Resource
    private LicenseService service;
    /**
     * 检验服务器是否注册,是否已存在序列号
     *  目前需求,校验码发放带上有效期
     *  在校验的时候,需要校验有效期,首次通过的时候存储到数据库,存储解析后的序列号和有效时长(示例:30天),预留字段已经使用时长
     *  因为凭证是针对应用的,所以一个项目数据库中只会保存有一个凭证
     */
    @GetMapping("/checkRegistered")
    @ApiOperation(value = "校验服务器是否注册")
    public Response checkRegistered(){
        return service.checkLicenseExpired();
    }
    /**
     * 方式一: 获取 序列号的时候,需要在5分钟内输入注册码
     * 方式二: 每次注册码生成,校验通过,放置到数据库中,最新存入数据库的生效,其余都失效,license表存在一个flag字段
     *
     * 当前采用:方式一
     */
    /**
     * 获取序列号license
     *
     */
    @GetMapping("/getSerialNumberLicense")
    @ApiOperation(value = "获取序列号license")
    public Response getSerialNumberLicense(){
        //序列号加密 license
        Response model=LicenseController.createLicense(System.currentTimeMillis()+"createTime"+ SerialNumberUtil.getSerialNumber());
        //同时,将序列号生成时间记录到application域中
        //getApplication().setAttribute("serialNumberLicenseTime",System.currentTimeMillis());
        return model;
    }
    //获取一个license
    public static Response createLicense(String serialNumber){
        //初始化sm2参数x
        SM2 x = new SM2();
        String realPath = ClassUtils.getDefaultClassLoader().getResource("").getPath();
        BigInteger privKey = x.importPrivateKey(realPath+"config/pri_key.ksm");
        ECPoint pubKey = x.importPublicKey(realPath+"config/pub_key.ksm");
        //System.out.println("pubKey "+pubKey);
        /*String origin = "Company: Fuguang Electronic\n"
                + "Project:BTS monitor platform\n"
                + "Licence type:Permanent";*/
        //获取加密列表
        //System.out.println("origin "+origin);
        byte[] encryptResult = x.encrypt(serialNumber, pubKey);
        String encrypt = ByteConvertUtil.bytesToHexString(encryptResult);
        //System.out.println("encrypt:"+encrypt);
        return new Response().set(1,encrypt);
    }
    /**
     * 校验license
     * 验证成功添加到数据库web_site.tb_license中
     */
    @GetMapping("/checkSerialNumberLicense")
    @ApiOperation(value = "校验license")
    public Response checkSerialNumberLicense(@RequestParam String license){
        Response model = new Response();
        try {
            //获取macid是否一致
            boolean res = false;
            //初始化sm2参数x
            SM2 x = new SM2();
            String realPath = ClassUtils.getDefaultClassLoader().getResource("").getPath();
            BigInteger privKey = x.importPrivateKey(realPath + "config/pri_key.ksm");
            ECPoint pubKey = x.importPublicKey(realPath + "config/pub_key.ksm");
            String origin = "Company: Fuguang Electronic\n"
                    + "Project:BTS monitor platform\n"
                    + "Licence duration:";
            //获取解密后license,附带校验license编码格式
            String decryptResult = null;
            try {
                byte[] bytes = ByteConvertUtil.hexToByteArray(license);
                decryptResult = x.decrypt(bytes, privKey);
            } catch (Exception e) {
                e.printStackTrace();
                model.setMsg("lincense错误");
                return model;
            }
            //用户只能往小调时间
            String[] split1 = decryptResult.split("machineCode:");
            Long registerCodeTime = Long.valueOf(split1[0]);
            Long machineCodeCreateTime = Long.valueOf(split1[1].split("createTime")[0]);
            //检验解码后license的值是否正确
            String serialNumberStr = SerialNumberUtil.getSerialNumber() + "@@" + origin;
            res = decryptResult.contains(serialNumberStr);
            if (res) {
                //校验license输入时间是否超过5分钟,5分钟内则存储到数据库中
                long currentTimeMillis = System.currentTimeMillis();
                //long serialNumberLicenseTime = (long) getApplication().getAttribute("serialNumberLicenseTime");
                //if(currentTimeMillis-serialNumberLicenseTime>=5*60*1000){
                if (currentTimeMillis - machineCodeCreateTime >= 5 * 60 * 1000) {
                    model.setCode(0);
                    model.setMsg("注册码获取及输入超过5分钟,请重新获取注册码");
                } else if (registerCodeTime > currentTimeMillis) {
                    model.setCode(0);
                    model.setMsg("系统时间不正常,请检查");
                } else {
                    model.setCode(1);
                    model.setMsg("注册码校验成功");
                }
                //model.setData(decryptResult);
                //model.setMsgN(serialNumberStr);
                double duration = Integer.parseInt(decryptResult.split("duration:")[1]);
                License data = new License();
                data.setSerialNumber(SerialNumberUtil.getSerialNumber());
                //duration和TimeInUse加密存入数据库
                String durationEncrypt = AESUtil.aesEncrypt(String.valueOf(duration));
                data.setDuration(durationEncrypt);
                String timeInUseEncrypt = AESUtil.aesEncrypt(String.valueOf(0));
                data.setTimeInUse(timeInUseEncrypt);
                //将序列号,有效期 添加入license表
                service.add(data);
            } else {
                model.setCode(0);
                model.setMsg("验证失败");
                //model.setData(decryptResult);
                //model.setMsgN(serialNumberStr);
            }
        }catch (Exception e){
            model.setCode(0);
            model.setMsg("验证失败");
        }
        return model;
    }
}
src/main/java/com/whyc/exception/CryptException.java
New file
@@ -0,0 +1,15 @@
package com.whyc.exception;
/**
 * 自定义加密解密异常类
 */
public class CryptException extends RuntimeException {
    public CryptException(String message, Throwable cause) {
        super(message, cause);
    }
    public CryptException(String message) {
        super(message);
    }
}
src/main/java/com/whyc/mapper/LicenseMapper.java
@@ -3,4 +3,8 @@
import com.whyc.pojo.License;
public interface LicenseMapper extends CustomMapper<License> {
    int selectExist();
    int createTable();
}
src/main/java/com/whyc/pojo/EleTmp.java
@@ -1,5 +1,6 @@
package com.whyc.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
@@ -15,7 +16,7 @@
@TableName(schema = "web_site",value = "tb_eletmp")
@ApiModel(value="电价表", description="电价分布模板管理")
public class EleTmp {
    @TableId
    @TableId(type = IdType.AUTO)
    private Integer tmpId;
    private String tmpName;
}
src/main/java/com/whyc/pojo/License.java
@@ -1,5 +1,6 @@
package com.whyc.pojo;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.annotations.ApiModel;
import lombok.Data;
@@ -9,7 +10,9 @@
@ApiModel(value="凭证", description="项目允许凭证")
public class License {
    private Integer id;
    @TableField("serialNumber")
    private String serialNumber;
    private String duration;
    @TableField("timeInUse")
    private String timeInUse;
}
src/main/java/com/whyc/service/LicenseService.java
@@ -1,9 +1,18 @@
package com.whyc.service;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.whyc.dto.Response;
import com.whyc.encryption.ByteConvertUtil;
import com.whyc.encryption.SM2;
import com.whyc.mapper.LicenseMapper;
import com.whyc.mapper.UserInfMapper;
import com.whyc.pojo.License;
import com.whyc.pojo.UserInf;
import com.whyc.util.AESUtil;
import com.whyc.util.SerialNumberUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.ClassUtils;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
@@ -11,6 +20,16 @@
@Service
public class LicenseService {
    @Autowired
    private UserInfMapper userInfMapper;
    @Autowired
    private LicenseMapper licenseMapper;
    public boolean add(License license){
        return licenseMapper.insert(license)>0;
    }
    /**
     * 证书文件校验,检查有效性并将文件存储在数据库
@@ -26,7 +45,8 @@
        SM2 x = new SM2();
//        String realPath = ServletActionContext.getServletContext().getRealPath("/");
//        BigInteger privKey = x.importPrivateKey(realPath+"WEB-INF/classes/pri_key.ksm");
        BigInteger privKey = x.importPrivateKey("WEB-INF/classes/pri_key.ksm");
        String path = ClassUtils.getDefaultClassLoader().getResource("").getPath();
        BigInteger privKey = x.importPrivateKey(path+"config/pri_key.ksm");
        //解密
@@ -63,7 +83,142 @@
        }
        return model;
    }
    /**
     * 每次用户登录,校验License中的有效期,无效则无法登录
     * 这个license针对用户
     */
    public Response checkLicenseValidity(String uName){
        Response model = new Response();
        model.setCode(1);
        QueryWrapper<UserInf> userInfQueryWrapper = new QueryWrapper<>();
        userInfQueryWrapper.select("license").eq("uName",uName);
        String license = userInfMapper.selectOne(userInfQueryWrapper).getLicense();
        //String license = new User_infImpl().getLicense(uName);
        if(license==null){
            model.setCode(0);
            model.setMsg("当前用户无license,请添加license后登录");
        }else {
            //初始化sm2参数x
            SM2 x = new SM2();
//            String realPath = ServletActionContext.getServletContext().getRealPath("/");
//            BigInteger privKey = x.importPrivateKey(realPath+"WEB-INF/classes/pri_key.ksm");
            String path = ClassUtils.getDefaultClassLoader().getResource("").getPath();;
            BigInteger privKey = x.importPrivateKey(path+"config/pri_key.ksm");
            //解密
            try {
                byte[] bytes = ByteConvertUtil.hexToByteArray(license);
                String decryptResult = x.decrypt(bytes, privKey);
                String[] split = decryptResult.split("Valid time:");
                if(split.length>1){
                    String validTime=split[1];
                    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                    String today = dateFormat.format(new Date());
                    if(validTime.compareTo(today)<0){
                        model.setCode(0);
                        model.setMsg("license过期,有效期为:"+validTime);
                    }
                }
            }catch (Exception e){
                model.setCode(0);
                model.setMsg("无效的license");
                e.printStackTrace();
            }
        }
        return model;
    }
    /**
     * 获取用户的license
     * @param uName
     * @return
     */
    public Response getLicense(String uName){
        Response model = new Response();
        //String license = new User_infImpl().getLicense(uName);
        QueryWrapper<UserInf> userInfQueryWrapper = new QueryWrapper<>();
        userInfQueryWrapper.select("license").eq("uName",uName);
        String license = userInfMapper.selectOne(userInfQueryWrapper).getLicense();
        model.setCode(1);
        model.setData(license);
        return model;
    }
    public Response checkLicenseExpired() {
        Response model = new Response();
        boolean res=licenseMapper.selectExist()>0;
        if(!res) { //表不存在,新建表,同时初始化参数
            boolean created = licenseMapper.createTable()>0;
        }
        QueryWrapper<License> queryWrapper = new QueryWrapper<>();
        License license = licenseMapper.selectOne(queryWrapper);
        //License license = new LicenseDao().searchOne();
        if(license!=null){
            //校验凭证序列号,项目绑定序列号
            String serialNumberInDB = license.getSerialNumber();
            String serialNumberLocal= SerialNumberUtil.getSerialNumber();
            if(!serialNumberInDB.equals(serialNumberLocal)){
                model.setCode(0);
                model.setMsg("凭证校验失败,请检查项目是否已经迁移");
            }else{
                //校验时间
                //有效时长,单位:天
                //duration和timeInUse解密后再做对比
                String durationEncrypt = license.getDuration();
                double duration = Double.parseDouble(AESUtil.aesDecrypt(durationEncrypt));
                //如果不是永久
                if(duration!=-1){
                    //使用时长,单位:秒
                    String timeInUseEncrypt = license.getTimeInUse();
                    Long timeInUse = Long.valueOf(AESUtil.aesDecrypt(timeInUseEncrypt));
                    if (duration * 24 * 60 * 60 < timeInUse) {
                        model.setCode(0);
                        model.setMsg("凭证已过期,有效期为:" + duration + "天");
                    }else{
                        model.setCode(1);
                    }
                }else {
                    model.setCode(1);
                }
            }
        }else{
            model.setCode(0);
            model.setMsg("注册码尚未输入");
        }
        return model;
    }
    public Response time2DeadLine() {
        Response model = new Response();
        QueryWrapper<License> queryWrapper = new QueryWrapper<>();
        License license = licenseMapper.selectOne(queryWrapper);
        String durationEncrypt = license.getDuration();
        double duration = Double.parseDouble(AESUtil.aesDecrypt(durationEncrypt));
        //-1表明license是永久的
        if(duration==-1){
            model.setCode(2);
        }else {
            String timeInUseEncrypt = license.getTimeInUse();
            Long timeInUse = Long.valueOf(AESUtil.aesDecrypt(timeInUseEncrypt));
            int durationSecond = (int) (duration * 24 * 60 * 60);
            Long leftTimeSecond = durationSecond - timeInUse;
            if(leftTimeSecond<=0){
                model.setCode(1);
                model.setData(0);
            }else {
                double leftTimeDay = Math.ceil(1.0 * leftTimeSecond / 60 / 60 / 24);
                model.setCode(1);
                model.setData(leftTimeDay);
            }
        }
        return model;
    }
}
src/main/java/com/whyc/util/AESUtil.java
New file
@@ -0,0 +1,97 @@
package com.whyc.util;
import com.whyc.exception.CryptException;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.GeneralSecurityException;
/**
 * AES对称加密
 */
public class AESUtil {
    public static void main(String[] args) {
        String test = aesEncrypt("2");
        String test2 = aesDecrypt(test);
        test2 = aesDecrypt("270A58CF479DC49985B5D06EC8D8C82A");
        System.out.println(test);
        System.out.println(test2);
    }
    private static String defaultKey = "www.whyctech.com";
    private static byte[] key = defaultKey.getBytes();
    public static String aesEncrypt(String input) {
        byte[] encryptResult = aesEncrypt(input.getBytes());
        return parseByte2HexStr(encryptResult);
    }
    private static byte[] aesEncrypt(byte[] bytes) {
        return aes(bytes, key, Cipher.ENCRYPT_MODE);
    }
    private static byte[] aes(byte[] bytes, byte[] key, int encryptMode) {
        try {
            SecretKey secretKey = new SecretKeySpec(key, "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(encryptMode, secretKey);
            return cipher.doFinal(bytes);
        } catch (GeneralSecurityException e) {
            throw new CryptException("加密异常", e);
        }
    }
    /*======解码======*/
    public static String aesDecrypt(String input) {
        byte[] bytes = parseHexStr2Byte(input);
        try {
            SecretKey secretKey = new SecretKeySpec(key, "AES");
            Cipher cipher = Cipher.getInstance("AES");
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            return new String(cipher.doFinal(bytes));
        } catch (GeneralSecurityException e) {
            throw new CryptException("解密异常", e);
        }
    }
    /**
     * 将二进制转换成16进制
     *
     * @param buf
     * @return
     */
    private static String parseByte2HexStr(byte buf[]) {
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < buf.length; i++) {
            String hex = Integer.toHexString(buf[i] & 0xFF);
            if (hex.length() == 1) {
                hex = '0' + hex;
            }
            sb.append(hex.toUpperCase());
        }
        return sb.toString();
    }
    /**
     * 将16进制转换为二进制
     *
     * @param hexStr
     * @return
     */
    private static byte[] parseHexStr2Byte(String hexStr) {
        if (hexStr.length() < 1)
            return null;
        byte[] result = new byte[hexStr.length() / 2];
        for (int i = 0; i < hexStr.length() / 2; i++) {
            int high = Integer.parseInt(hexStr.substring(i * 2, i * 2 + 1), 16);
            int low = Integer.parseInt(hexStr.substring(i * 2 + 1, i * 2 + 2), 16);
            result[i] = (byte) (high * 16 + low);
        }
        return result;
    }
}
src/main/java/com/whyc/util/SerialNumberUtil.java
New file
@@ -0,0 +1,155 @@
package com.whyc.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Scanner;
/**
 * 电脑 CPU 主板 硬盘 序列号获取工具
 */
public class SerialNumberUtil {
    public static String getSerialNumber(){
        return getCPUNumber()+getBoisNumber()+getDiskDriveSerialNumber();
    }
    public static String getCPUNumber(){
        String os = System.getProperty("os.name").toLowerCase();
        System.out.println(os);
        Process process = null;
        String serial = null;
        if(os.contains("window")) {
            System.out.println("windows");
            try {
                process = Runtime.getRuntime().exec(
                        new String[]{"wmic", "cpu", "get", "ProcessorId"});
                process.getOutputStream().close();
                Scanner sc = new Scanner(process.getInputStream());
                String property = sc.next();
                serial = sc.next();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else{
            //linux
            System.out.println("linux");
            serial =getSerialNumber("dmidecode -t processor | grep 'ID'", "ID",":");
        }
        return serial;
    }
    public static String getBoisNumber(){
        String os = System.getProperty("os.name").toLowerCase();
        Process process = null;
        String serial = null;
        if(os.contains("window")) {
            try {
                process = Runtime.getRuntime().exec(
                        new String[]{"wmic", "csproduct", "get", "UUID"});
                process.getOutputStream().close();
                Scanner sc = new Scanner(process.getInputStream());
                String property = sc.next();
                serial = sc.next();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else {
            //linux
            serial =getSerialNumber("dmidecode |grep 'Serial Number'", "Serial Number",":");        }
        return serial;
    }
    public static String getDiskDriveSerialNumber(){
        String os = System.getProperty("os.name").toLowerCase();
        Process process = null;
        String serial = "";
        if(os.contains("window")) {
            try {
                process = Runtime.getRuntime().exec(
                        new String[]{"wmic", "diskdrive", "get", "serialnumber"});
                process.getOutputStream().close();
                BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream()));
                String line;
                int count = 0;
                while ((line = input.readLine()) != null) {
                    count++;
                    if (count != 1) {
                        line = line.trim();
                        serial += line;
                    }
                }
                input.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else {
            //linux
            serial=getSerialNumber("fdisk -l", "Disk identifier",":");
        }
        return serial;
    }
    /**linux*/
    public static String executeLinuxCmd(String cmd)  {
        try {
            System.out.println("got cmd job : " + cmd);
            Runtime run = Runtime.getRuntime();
            Process process;
            process = run.exec(cmd);
            InputStream in = process.getInputStream();
            BufferedReader bs = new BufferedReader(new InputStreamReader(in));
            StringBuffer out = new StringBuffer();
            byte[] b = new byte[8192];
            for (int n; (n = in.read(b)) != -1;) {
                out.append(new String(b, 0, n));
            }
            in.close();
            process.destroy();
            return out.toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    /**
     *
     * @param cmd 命令语句
     * @param record 要查看的字段
     * @param symbol 分隔符
     * @return
     */
    public static String getSerialNumber(String cmd ,String record,String symbol) {
        String execResult = executeLinuxCmd(cmd);
        String[] infos = execResult.split("\n");
        for(String info : infos) {
            info = info.trim();
            if(info.indexOf(record) != -1) {
                info.replace(" ", "");
                String[] sn = info.split(symbol);
                return sn[1];
            }
        }
        return null;
    }
}
src/main/resources/config/pri_key.ksm
Binary files differ
src/main/resources/config/pub_key.ksm
New file
@@ -0,0 +1 @@
ÂÇã3wò.É\–¸`}Øòហ   º    n–GöêŒê^œ²õØ­L¯»”ðÁmûșŸ7ŽÑÁ‹1ýK:¾–®Eë
src/main/resources/mapper/LicenseMapper.xml
@@ -1,6 +1,18 @@
<?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.whyc.mapper.LicenseMapper" >
    <update id="createTable">
        CREATE TABLE `web_site`.`tb_license`  (
            `id` int(11) NOT NULL AUTO_INCREMENT,
            `serialNumber` varchar(255) NOT NULL DEFAULT '',
            `serialNumber` varchar(255) NOT NULL DEFAULT '',
            `timeInUse` varchar(255) NOT NULL DEFAULT '',
            PRIMARY KEY (`id`) USING BTREE
        ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ;
    </update>
    <select id="selectExist" resultType="java.lang.Integer">
        select count(*) num from information_schema.TABLES t where t.TABLE_SCHEMA ='web_site' and t.TABLE_NAME ='tb_license'
    </select>
</mapper>