Browse Source

!281 merge
Merge pull request !281 from AE86/v_2.0

AE86 7 months ago
parent
commit
f26a06e98d

+ 1 - 1
dbsyncer-biz/src/main/java/org/dbsyncer/biz/AppConfigService.java

@@ -17,5 +17,5 @@ public interface AppConfigService {
      *
      *
      * @return
      * @return
      */
      */
-    VersionVo getVersionInfo();
+    VersionVo getVersionInfo(String username);
 }
 }

+ 7 - 2
dbsyncer-biz/src/main/java/org/dbsyncer/biz/impl/AppConfigServiceImpl.java

@@ -7,6 +7,7 @@ import org.dbsyncer.biz.AppConfigService;
 import org.dbsyncer.biz.SystemConfigService;
 import org.dbsyncer.biz.SystemConfigService;
 import org.dbsyncer.biz.vo.VersionVo;
 import org.dbsyncer.biz.vo.VersionVo;
 import org.dbsyncer.common.config.AppConfig;
 import org.dbsyncer.common.config.AppConfig;
+import org.dbsyncer.common.util.StringUtil;
 import org.dbsyncer.parser.model.SystemConfig;
 import org.dbsyncer.parser.model.SystemConfig;
 import org.springframework.stereotype.Component;
 import org.springframework.stereotype.Component;
 
 
@@ -26,13 +27,17 @@ public class AppConfigServiceImpl implements AppConfigService {
     @Resource
     @Resource
     private SystemConfigService systemConfigService;
     private SystemConfigService systemConfigService;
 
 
+    public static final String WATERMARK_USERNAME = "${username}";
+
     @Override
     @Override
-    public VersionVo getVersionInfo() {
+    public VersionVo getVersionInfo(String username) {
         VersionVo versionVo = new VersionVo(appConfig.getName(), appConfig.getCopyright());
         VersionVo versionVo = new VersionVo(appConfig.getName(), appConfig.getCopyright());
         // 是否启用水印
         // 是否启用水印
         SystemConfig systemConfig = systemConfigService.getSystemConfig();
         SystemConfig systemConfig = systemConfigService.getSystemConfig();
         if (systemConfig.isEnableWatermark()) {
         if (systemConfig.isEnableWatermark()) {
-            versionVo.setWatermark(systemConfigService.getWatermark(systemConfig));
+            String watermark = systemConfigService.getWatermark(systemConfig);
+            watermark = StringUtil.replace(watermark, WATERMARK_USERNAME, username);
+            versionVo.setWatermark(watermark);
         }
         }
         return versionVo;
         return versionVo;
     }
     }

+ 1 - 1
dbsyncer-biz/src/main/java/org/dbsyncer/biz/impl/SystemConfigServiceImpl.java

@@ -127,6 +127,6 @@ public class SystemConfigServiceImpl implements SystemConfigService {
 
 
     @Override
     @Override
     public String getWatermark(SystemConfig systemConfig) {
     public String getWatermark(SystemConfig systemConfig) {
-        return StringUtil.isNotBlank(systemConfig.getWatermark()) ? systemConfig.getWatermark() : appConfig.getName() + "<br />" + appConfig.getCompany();
+        return StringUtil.isNotBlank(systemConfig.getWatermark()) ? systemConfig.getWatermark() : appConfig.getName() + "-${username}<br />" + appConfig.getCompany();
     }
     }
 }
 }

+ 5 - 2
dbsyncer-biz/src/main/java/org/dbsyncer/biz/impl/UserConfigServiceImpl.java

@@ -56,6 +56,7 @@ public class UserConfigServiceImpl implements UserConfigService {
         String password = params.get("password");
         String password = params.get("password");
         Assert.hasText(password, "The password is null.");
         Assert.hasText(password, "The password is null.");
         String mail = params.get("mail");
         String mail = params.get("mail");
+        String phone = params.get("phone");
 
 
         // 验证当前登录用户合法身份(必须是管理员操作)
         // 验证当前登录用户合法身份(必须是管理员操作)
         UserConfig userConfig = getUserConfig();
         UserConfig userConfig = getUserConfig();
@@ -64,7 +65,7 @@ public class UserConfigServiceImpl implements UserConfigService {
         // 新用户合法性(用户不能重复)
         // 新用户合法性(用户不能重复)
         Assert.isNull(userConfig.getUserInfo(username), "用户已存在,请换个账号");
         Assert.isNull(userConfig.getUserInfo(username), "用户已存在,请换个账号");
         // 注册新用户
         // 注册新用户
-        userConfig.getUserInfoList().add(new UserInfo(username, nickname, SHA1Util.b64_sha1(password), UserRoleEnum.USER.getCode(), mail));
+        userConfig.getUserInfoList().add(new UserInfo(username, nickname, SHA1Util.b64_sha1(password), UserRoleEnum.USER.getCode(), mail, phone));
 
 
         logService.log(LogType.UserLog.INSERT, String.format("[%s]添加[%s]账号成功", currentUser.getUsername(), username));
         logService.log(LogType.UserLog.INSERT, String.format("[%s]添加[%s]账号成功", currentUser.getUsername(), username));
         return profileComponent.editConfigModel(userConfig);
         return profileComponent.editConfigModel(userConfig);
@@ -78,6 +79,7 @@ public class UserConfigServiceImpl implements UserConfigService {
         Assert.hasText(nickname, "The nickname is null.");
         Assert.hasText(nickname, "The nickname is null.");
         String newPwd = params.get("newPwd");
         String newPwd = params.get("newPwd");
         String mail = params.get("mail");
         String mail = params.get("mail");
+        String phone = params.get("phone");
 
 
         // 验证当前登录用户合法身份(管理员或本人操作)
         // 验证当前登录用户合法身份(管理员或本人操作)
         UserConfig userConfig = getUserConfig();
         UserConfig userConfig = getUserConfig();
@@ -93,6 +95,7 @@ public class UserConfigServiceImpl implements UserConfigService {
         // 用户昵称
         // 用户昵称
         updateUser.setNickname(nickname);
         updateUser.setNickname(nickname);
         updateUser.setMail(mail);
         updateUser.setMail(mail);
+        updateUser.setPhone(phone);
         // 修改密码
         // 修改密码
         if (StringUtil.isNotBlank(newPwd)) {
         if (StringUtil.isNotBlank(newPwd)) {
             // 修改自己的密码需要验证
             // 修改自己的密码需要验证
@@ -181,7 +184,7 @@ public class UserConfigServiceImpl implements UserConfigService {
             if (null == config) {
             if (null == config) {
                 config = (UserConfig) userConfigChecker.checkAddConfigModel(new HashMap<>());
                 config = (UserConfig) userConfigChecker.checkAddConfigModel(new HashMap<>());
                 UserRoleEnum admin = UserRoleEnum.ADMIN;
                 UserRoleEnum admin = UserRoleEnum.ADMIN;
-                config.getUserInfoList().add(new UserInfo(DEFAULT_USERNAME, DEFAULT_USERNAME, DEFAULT_PASSWORD, admin.getCode(), ""));
+                config.getUserInfoList().add(new UserInfo(DEFAULT_USERNAME, DEFAULT_USERNAME, DEFAULT_PASSWORD, admin.getCode(), StringUtil.EMPTY, StringUtil.EMPTY));
                 profileComponent.addConfigModel(config);
                 profileComponent.addConfigModel(config);
             }
             }
             return config;
             return config;

+ 5 - 1
dbsyncer-parser/src/main/java/org/dbsyncer/parser/LogType.java

@@ -278,7 +278,11 @@ public interface LogType {
         UPDATE("81", "修改"),
         UPDATE("81", "修改"),
         DELETE("82", "删除"),
         DELETE("82", "删除"),
         UPLOAD_LICENSE_FILE("83", "上传授权许可文件"),
         UPLOAD_LICENSE_FILE("83", "上传授权许可文件"),
-        UPLOAD_LICENSE_FILE_ERROR("84", "上传授权许可文件失败");
+        UPLOAD_LICENSE_FILE_ERROR("84", "上传授权许可文件失败"),
+        ACTIVATE_FREE_LICENSE_FILE("85", "免费激活授权许可文件"),
+        ACTIVATE_FREE_LICENSE_FILE_ERROR("86", "免费激活授权许可文件失败"),
+        DELETE_LICENSE_FILE("87", "删除授权许可文件"),
+        DELETE_LICENSE_FILE_ERROR("88", "删除授权许可文件失败");
 
 
         private String type;
         private String type;
         private String message;
         private String message;

+ 15 - 1
dbsyncer-parser/src/main/java/org/dbsyncer/parser/model/UserInfo.java

@@ -32,15 +32,21 @@ public class UserInfo {
      */
      */
     private String mail;
     private String mail;
 
 
+    /**
+     * 手机
+     */
+    private String phone;
+
     public UserInfo() {
     public UserInfo() {
     }
     }
 
 
-    public UserInfo(String username, String nickname, String password, String roleCode, String mail) {
+    public UserInfo(String username, String nickname, String password, String roleCode, String mail, String phone) {
         this.username = username;
         this.username = username;
         this.nickname = nickname;
         this.nickname = nickname;
         this.password = password;
         this.password = password;
         this.roleCode = roleCode;
         this.roleCode = roleCode;
         this.mail = mail;
         this.mail = mail;
+        this.phone = phone;
     }
     }
 
 
     public String getUsername() {
     public String getUsername() {
@@ -83,6 +89,14 @@ public class UserInfo {
         this.mail = mail;
         this.mail = mail;
     }
     }
 
 
+    public String getPhone() {
+        return phone;
+    }
+
+    public void setPhone(String phone) {
+        this.phone = phone;
+    }
+
     @Override
     @Override
     public boolean equals(Object obj) {
     public boolean equals(Object obj) {
         if(obj instanceof UserInfo){
         if(obj instanceof UserInfo){

+ 4 - 1
dbsyncer-web/src/main/java/org/dbsyncer/web/controller/index/IndexController.java

@@ -9,6 +9,8 @@ import org.dbsyncer.biz.vo.ProjectGroupVo;
 import org.dbsyncer.biz.vo.RestResult;
 import org.dbsyncer.biz.vo.RestResult;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.stereotype.Controller;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.ModelMap;
 import org.springframework.ui.ModelMap;
 import org.springframework.web.bind.annotation.GetMapping;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -47,6 +49,7 @@ public class IndexController {
     @GetMapping("/version.json")
     @GetMapping("/version.json")
     @ResponseBody
     @ResponseBody
     public RestResult version() {
     public RestResult version() {
-        return RestResult.restSuccess(appConfigService.getVersionInfo());
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        return RestResult.restSuccess(appConfigService.getVersionInfo(authentication.getName()));
     }
     }
 }
 }

+ 130 - 1
dbsyncer-web/src/main/java/org/dbsyncer/web/controller/license/LicenseController.java

@@ -4,25 +4,49 @@
 package org.dbsyncer.web.controller.license;
 package org.dbsyncer.web.controller.license;
 
 
 import org.apache.commons.io.FileUtils;
 import org.apache.commons.io.FileUtils;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.conn.HttpHostConnectException;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.dbsyncer.biz.UserConfigService;
 import org.dbsyncer.biz.vo.RestResult;
 import org.dbsyncer.biz.vo.RestResult;
+import org.dbsyncer.common.config.AppConfig;
+import org.dbsyncer.common.util.CollectionUtils;
+import org.dbsyncer.common.util.JsonUtil;
+import org.dbsyncer.common.util.StringUtil;
 import org.dbsyncer.parser.LogService;
 import org.dbsyncer.parser.LogService;
 import org.dbsyncer.parser.LogType;
 import org.dbsyncer.parser.LogType;
+import org.dbsyncer.parser.model.UserInfo;
+import org.dbsyncer.sdk.model.ProductInfo;
 import org.dbsyncer.sdk.spi.LicenseService;
 import org.dbsyncer.sdk.spi.LicenseService;
+import org.dbsyncer.web.controller.BaseController;
 import org.slf4j.Logger;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.slf4j.LoggerFactory;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
 import org.springframework.stereotype.Controller;
 import org.springframework.stereotype.Controller;
 import org.springframework.ui.ModelMap;
 import org.springframework.ui.ModelMap;
+import org.springframework.util.Assert;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.PostMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.bind.annotation.ResponseBody;
 import org.springframework.web.multipart.MultipartFile;
 import org.springframework.web.multipart.MultipartFile;
 
 
 import javax.annotation.Resource;
 import javax.annotation.Resource;
+import javax.servlet.http.HttpServletRequest;
 import java.io.File;
 import java.io.File;
+import java.io.IOException;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Map;
 
 
 @Controller
 @Controller
 @RequestMapping("/license")
 @RequestMapping("/license")
-public class LicenseController {
+public class LicenseController extends BaseController {
 
 
     private final Logger logger = LoggerFactory.getLogger(getClass());
     private final Logger logger = LoggerFactory.getLogger(getClass());
 
 
@@ -32,9 +56,23 @@ public class LicenseController {
     @Resource
     @Resource
     private LicenseService licenseService;
     private LicenseService licenseService;
 
 
+    @Resource
+    private AppConfig appConfig;
+
+    @Resource
+    private UserConfigService userConfigService;
+
+    public static final Integer SUCCESS = 200;
+    public static final String STATUS = "status";
+    public static final String DATA = "data";
+    public static final String MSG = "msg";
+    public static final String SERVER_ADDRESS = "http://117.72.11.38:8989/api/license/create";
+
     @RequestMapping("")
     @RequestMapping("")
     public String index(ModelMap model) {
     public String index(ModelMap model) {
         model.put("key", licenseService.getKey());
         model.put("key", licenseService.getKey());
+        model.put("company", appConfig.getCompany());
+        model.put("userInfo", getUserInfo());
         model.put("productInfo", licenseService.getProductInfo());
         model.put("productInfo", licenseService.getProductInfo());
         return "license/license";
         return "license/license";
     }
     }
@@ -60,4 +98,95 @@ public class LicenseController {
         }
         }
     }
     }
 
 
+    @PostMapping(value = "/activate")
+    @ResponseBody
+    public RestResult activate(HttpServletRequest request) {
+        try {
+            ProductInfo productInfo = licenseService.getProductInfo();
+            if (productInfo == null || (productInfo != null && CollectionUtils.isEmpty(productInfo.getProducts()))) {
+                String content = getLicenseContent(getParams(request));
+                if (StringUtil.isNotBlank(content)) {
+                    String filename = "license";
+                    File dest = new File(licenseService.getLicensePath() + filename);
+                    FileUtils.writeStringToFile(dest, content, Charset.defaultCharset());
+                    licenseService.updateLicense();
+                    logger.info("{}:{}", LogType.UserLog.ACTIVATE_FREE_LICENSE_FILE.getMessage(), filename);
+                    logService.log(LogType.UserLog.ACTIVATE_FREE_LICENSE_FILE);
+                }
+            }
+            return RestResult.restSuccess("ok");
+        } catch (Exception e) {
+            logService.log(LogType.UserLog.ACTIVATE_FREE_LICENSE_FILE_ERROR);
+            logger.error(e.getLocalizedMessage(), e.getClass());
+            return RestResult.restFail(e.getMessage());
+        }
+    }
+
+    @PostMapping(value = "/remove")
+    @ResponseBody
+    public RestResult remove() {
+        try {
+            String filename = "license";
+            File dest = new File(licenseService.getLicensePath() + filename);
+            FileUtils.deleteQuietly(dest);
+            licenseService.updateLicense();
+            logger.info("{}:{}", LogType.UserLog.DELETE_LICENSE_FILE.getMessage(), filename);
+            logService.log(LogType.UserLog.DELETE_LICENSE_FILE);
+            return RestResult.restSuccess("ok");
+        } catch (Exception e) {
+            logService.log(LogType.UserLog.DELETE_LICENSE_FILE_ERROR);
+            logger.error(e.getLocalizedMessage(), e.getClass());
+            return RestResult.restFail(e.getMessage());
+        }
+    }
+
+    private String getLicenseContent(Map<String, String> params) throws IOException {
+        Map<String, Object> map = new HashMap<>();
+        String company = params.get("company");
+        String owner = params.get("owner");
+        String phone = params.get("phone");
+        String mail = params.get("mail");
+        String remark = params.get("remark");
+        map.put("licenseKey", licenseService.getKey());
+        map.put("company", StringUtil.isNotBlank(company) ? company : appConfig.getCompany());
+        UserInfo userInfo = getUserInfo();
+        Assert.notNull(userInfo, "会话过期,请重新登录");
+        map.put("owner", StringUtil.isNotBlank(owner) ? owner : userInfo.getNickname());
+        map.put("phone", StringUtil.isNotBlank(phone) ? phone : userInfo.getPhone());
+        map.put("mail", StringUtil.isNotBlank(mail) ? mail : userInfo.getMail());
+        map.put("remark", StringUtil.isNotBlank(remark) ? remark : StringUtil.EMPTY);
+        return invoke(SERVER_ADDRESS, map);
+    }
+
+    public String invoke(String url, Map params) throws IOException {
+        String data = URLEncoder.encode(JsonUtil.objToJson(params), "UTF-8");
+        StringEntity se = new StringEntity(data);
+        se.setContentEncoding("UTF-8");
+        se.setContentType("application/json");
+        HttpPost httpPost = new HttpPost(url);
+        httpPost.setEntity(se);
+        CloseableHttpClient httpClient = HttpClients.createDefault();
+        try {
+            CloseableHttpResponse response = httpClient.execute(httpPost);
+            if (response.getStatusLine().getStatusCode() == SUCCESS) {
+                Map<String, String> result = JsonUtil.jsonToObj(EntityUtils.toString(response.getEntity()), Map.class);
+                if (result.containsKey(DATA)) {
+                    String status = String.valueOf(result.get(STATUS));
+                    if (Integer.parseInt(status) == SUCCESS) {
+                        return result.get(DATA);
+                    }
+                }
+                throw new IllegalArgumentException(result.get(MSG));
+            }
+        } catch (HttpHostConnectException e) {
+            throw new IllegalArgumentException("网络连接异常,无法激活");
+        }
+        throw new IllegalArgumentException("授权服务地址异常,无法激活");
+    }
+
+    private UserInfo getUserInfo() {
+        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
+        return userConfigService.getUserInfo(authentication.getName());
+    }
+
 }
 }

+ 68 - 22
dbsyncer-web/src/main/resources/public/license/license.html

@@ -6,15 +6,18 @@
     <form th:if="${#strings.isEmpty(key)}" class="form-horizontal" role="form">
     <form th:if="${#strings.isEmpty(key)}" class="form-horizontal" role="form">
         <div class="row text-center">
         <div class="row text-center">
             <div class="page-header">
             <div class="page-header">
-                <h3>功能暂未开放</h3>
+                <h3>社区版暂未开放,请下载<a href="https://gitee.com/ghi/dbsyncer/releases" title="下载专业版dbsyncer-enterprise-x.x.x-bin.zip " target='_blank'>专业版</a></h3>
+            </div>
+            <div class="page-information">
+                <img draggable="false" th:src="@{'/img/license/enterprise.png'}">
             </div>
             </div>
         </div>
         </div>
     </form>
     </form>
 
 
-    <form th:if="${not #strings.isEmpty(key)}" class="form-horizontal" role="form">
+    <form id="licenseForm" th:if="${not #strings.isEmpty(key)}" class="form-horizontal" role="form">
         <div class="row text-center">
         <div class="row text-center">
             <div class="page-header">
             <div class="page-header">
-                <h3>授权许可<img th:if="${productInfo?.products?.size() gt 0}" draggable="false" title="授权成功" width="28px" height="28px" th:src="@{'/img/check.png'}"></h3>
+                <h3>授权许可<img th:if="${productInfo?.products?.size() gt 0}" draggable="false" title="授权成功" width="28px" height="28px" th:src="@{'/img/license/check.png'}"></h3>
             </div>
             </div>
         </div>
         </div>
         <div class="form-group" th:if="${productInfo?.products?.size() gt 0}">
         <div class="form-group" th:if="${productInfo?.products?.size() gt 0}">
@@ -22,7 +25,7 @@
                 <strong>[[${productInfo?.company}]]</strong><br>
                 <strong>[[${productInfo?.company}]]</strong><br>
                 申请人:[[${productInfo?.owner}]]<br>
                 申请人:[[${productInfo?.owner}]]<br>
                 授权时间:[[${#dates.format(productInfo?.createTime, 'yyyy-MM-dd HH:mm:ss')}]]<br>
                 授权时间:[[${#dates.format(productInfo?.createTime, 'yyyy-MM-dd HH:mm:ss')}]]<br>
-                <abbr>备注:</abbr>[[${productInfo?.remark}]]
+                <abbr>备注</abbr>[[${productInfo?.remark}]]
             </address>
             </address>
             <table class="table table-hover">
             <table class="table table-hover">
                 <thead>
                 <thead>
@@ -36,36 +39,79 @@
                 <tr th:id="${p?.name}" th:each="p,state : ${productInfo?.products}">
                 <tr th:id="${p?.name}" th:each="p,state : ${productInfo?.products}">
                     <td>[[${state.index+1}]]</td>
                     <td>[[${state.index+1}]]</td>
                     <!-- 有效期 -->
                     <!-- 有效期 -->
-                    <td th:if="${#dates.createNow()?.time} < ${p?.effectiveTime} and ${p?.effectiveTime} - 864000000 > ${#dates.createNow()?.time}">[[${p?.name}]]&nbsp;<img draggable="false" width="20px" height="20px" th:src="@{'/img/check.png'}"></td>
+                    <td th:if="${#dates.createNow()?.time} < ${p?.effectiveTime} and ${p?.effectiveTime} - 864000000 > ${#dates.createNow()?.time}">[[${p?.name}]]&nbsp;<img draggable="false" width="20px" height="20px" th:src="@{'/img/license/check.png'}"></td>
                     <!-- 即将过期 -->
                     <!-- 即将过期 -->
-                    <td th:if="${#dates.createNow()?.time} < ${p?.effectiveTime} and ${p?.effectiveTime} - 864000000 <= ${#dates.createNow()?.time}">[[${p?.name}]]&nbsp;<img draggable="false" width="20px" height="20px" title="即将过期, 请联系星河同步官方续期" th:src="@{'/img/remind.png'}"></td>
+                    <td th:if="${#dates.createNow()?.time} < ${p?.effectiveTime} and ${p?.effectiveTime} - 864000000 <= ${#dates.createNow()?.time}">[[${p?.name}]]&nbsp;<img draggable="false" width="20px" height="20px" title="即将过期, 请联系星河同步官方续期" th:src="@{'/img/license/remind.png'}"></td>
                     <!-- 已过期 -->
                     <!-- 已过期 -->
-                    <td th:if="${#dates.createNow()?.time} > ${p?.effectiveTime}">[[${p?.name}]]&nbsp;<img draggable="false" width="20px" height="20px" title="已过期, 请联系星河同步官方续期" th:src="@{'/img/warning.png'}"></td>
+                    <td th:if="${#dates.createNow()?.time} > ${p?.effectiveTime}">[[${p?.name}]]&nbsp;<img draggable="false" width="20px" height="20px" title="已过期, 请联系星河同步官方续期" th:src="@{'/img/license/warning.png'}"></td>
                     <td th:text="${#dates.format(p?.effectiveTime, 'yyyy-MM-dd HH:mm:ss')}"/>
                     <td th:text="${#dates.format(p?.effectiveTime, 'yyyy-MM-dd HH:mm:ss')}"/>
                 </tr>
                 </tr>
                 </tbody>
                 </tbody>
             </table>
             </table>
+            <div class="text-right">
+                <button id="removeBtn" class="btn btn-default" type="button">删除激活码</button>
+            </div>
         </div>
         </div>
 
 
-        <div class="panel panel-info">
-            <div class="panel-heading">
-                <h3 class="panel-title">1.复制机器码(请发送给 <a href="javascript:;" id="myService" class="text-success"><span class='fa fa-wechat'></span>星河同步官方</a> 帮您生成License文件)</h3>
+        <div id="qrcode" class="hidden"></div>
+        <div th:if="${productInfo?.products?.size() lt 0}">
+            <div class="form-group">
+                <div class="row">
+                    <div class="col-md-6">
+                        <label class="control-label">公司名称</label>
+                        <input class="form-control" type="text" name="company" maxlength="64" th:value="${company}"/>
+                    </div>
+                    <div class="col-md-6">
+                        <label>申请人</label>
+                        <input class="form-control" type="text" name="owner" maxlength="32" th:value="${userInfo?.nickname}"/>
+                    </div>
+                </div>
             </div>
             </div>
-            <div class="panel-body">
-                <div class="input-group">
-                    <input id="licenseKey" type="text" class="form-control text-primary" readonly="readonly" th:value="${key}">
-                    <span class="input-group-btn"><button id="copyBtn" class="btn btn-default" type="button" th:title="复制机器码"><i class="fa fa-copy"></i>&nbsp;复制</button></span>
+            <div class="form-group">
+                <div class="row">
+                    <div class="col-md-6">
+                        <label>手机号</label>
+                        <input class="form-control" type="text" name="phone" maxlength="11" th:value="${userInfo?.phone}"/>
+                        <small class="text-muted">便于通过短信提前通知您授权情况</small>
+                    </div>
+                    <div class="col-md-6">
+                        <label>邮箱</label>
+                        <input class="form-control" type="text" name="mail" maxlength="64" th:value="${userInfo?.mail}"/>
+                    </div>
                 </div>
                 </div>
-                <div id="qrcode" class="hidden"></div>
             </div>
             </div>
-        </div>
-        <div class="panel panel-success">
-            <div class="panel-heading">
-                <h3 class="panel-title">2.上传License文件</h3>
+            <div class="form-group">
+                <div class="row">
+                    <div class="col-md-12">
+                        <label>备注</label>
+                        <input class="form-control" type="text" maxlength="64" name="remark"/>
+                    </div>
+                </div>
+            </div>
+            <div class="form-group">
+                <div class="row">
+                    <div class="col-md-12">
+                        <button id="activateBtn" th:if="${productInfo?.products?.size() lt 0}" class="btn btn-primary" type="button" th:title="填写的信息会严格保密,我们不会向其他人共享您的信息">在线激活(免费使用15天)</button>
+                    </div>
+                </div>
             </div>
             </div>
-            <div class="panel-body">
-                <div class="file-loading">
-                    <input id="fileLicense" type="file" name="files" multiple="multiple"/>
+
+            <hr/>
+            <div class="row">
+                <div class="panel panel-default">
+                    <div class="panel-heading">
+                        <h3 class="panel-title">离线激活(无法访问互联网时) 1.复制机器码给 <a href="javascript:;" id="myService" class="text-success"><span class='fa fa-wechat'></span>星河同步官方</a> 帮您生成License文件; 2.上传License文件</h3>
+                    </div>
+                    <div class="panel-body">
+                        <div class="input-group">
+                            <input id="licenseKey" type="text" class="form-control text-primary" readonly="readonly" th:value="${key}">
+                            <span class="input-group-btn"><button id="copyBtn" class="btn btn-default" type="button" th:title="复制机器码"><i class="fa fa-copy"></i>&nbsp;复制</button></span>
+                        </div>
+                        <br />
+                        <div class="file-loading">
+                            <input id="fileLicense" type="file" name="files" multiple="multiple"/>
+                        </div>
+                    </div>
                 </div>
                 </div>
             </div>
             </div>
         </div>
         </div>

+ 8 - 0
dbsyncer-web/src/main/resources/public/user/add.html

@@ -66,6 +66,14 @@ xmlns:th="http://www.thymeleaf.org" lang="zh-CN">
                 </div>
                 </div>
               </div>
               </div>
 
 
+              <!-- 手机 -->
+              <div class="form-group">
+                <label class="col-sm-2 control-label">手机</label>
+                <div class="col-sm-10">
+                  <input type="number" class="form-control" dbsyncer-valid="false" maxlength="13" name="phone"/>
+                </div>
+              </div>
+
             </div>
             </div>
           </div>
           </div>
         </div>
         </div>

+ 8 - 0
dbsyncer-web/src/main/resources/public/user/edit.html

@@ -81,6 +81,14 @@ xmlns:th="http://www.thymeleaf.org" lang="zh-CN">
                 </div>
                 </div>
               </div>
               </div>
 
 
+              <!-- 手机 -->
+              <div class="form-group">
+                <label class="col-sm-2 control-label">手机</label>
+                <div class="col-sm-10">
+                  <input type="number" class="form-control" dbsyncer-valid="false" maxlength="13" name="phone" th:value="${currentUser?.phone}"/>
+                </div>
+              </div>
+
             </div>
             </div>
           </div>
           </div>
         </div>
         </div>

+ 2 - 0
dbsyncer-web/src/main/resources/public/user/user.html

@@ -31,6 +31,7 @@
                         <th>用户</th>
                         <th>用户</th>
                         <th>角色</th>
                         <th>角色</th>
                         <th>邮箱</th>
                         <th>邮箱</th>
+                        <th>手机</th>
                         <th>操作</th>
                         <th>操作</th>
                     </tr>
                     </tr>
                     </thead>
                     </thead>
@@ -43,6 +44,7 @@
                                 <span class="label label-success">[[${m}]]</span>
                                 <span class="label label-success">[[${m}]]</span>
                             </span>
                             </span>
                         </td>
                         </td>
+                        <td th:text="${u?.phone}"/>
                         <td>
                         <td>
                             <!-- 管理员或本人权限 -->
                             <!-- 管理员或本人权限 -->
                             <button th:if="${currentUser?.roleCode eq 'admin' or currentUser?.username eq u?.username}" type="button" th:id="${u?.username}" class="btn btn-primary editUserBtn">
                             <button th:if="${currentUser?.roleCode eq 'admin' or currentUser?.username eq u?.username}" type="button" th:id="${u?.username}" class="btn btn-primary editUserBtn">

+ 0 - 0
dbsyncer-web/src/main/resources/static/img/check.png → dbsyncer-web/src/main/resources/static/img/license/check.png


BIN
dbsyncer-web/src/main/resources/static/img/license/enterprise.png


+ 0 - 0
dbsyncer-web/src/main/resources/static/img/remind.png → dbsyncer-web/src/main/resources/static/img/license/remind.png


+ 0 - 0
dbsyncer-web/src/main/resources/static/img/warning.png → dbsyncer-web/src/main/resources/static/img/license/warning.png


+ 75 - 32
dbsyncer-web/src/main/resources/static/js/license/license.js

@@ -4,47 +4,90 @@ $(function () {
         width: 200,
         width: 200,
         height: 200
         height: 200
     });
     });
-    let imgSelector = qrcode.querySelector("img");
-    imgSelector.onload = () => {
-        let content = "<img src='" + imgSelector.src + "' />";
-        $("#myService").popover({
-            title: "<span class='fa fa-wechat'></span> 微信扫码",
-            trigger: 'hover',
-            placement: 'bottom',
-            html: 'true',
-            content: content
-        }).on('shown.bs.popover', function (event) {
-            const that = this;
-            $(this).parent().find('div.popover').on('mouseenter', function () {
-                $(that).attr('in', true);
-            }).on('mouseleave', function () {
-                $(that).removeAttr('in');
-                $(that).popover('hide');
+    let $myService = $("#myService");
+    $myService.hover(function () {
+        if(!$myService.attr('init') == true){
+            $myService.attr("init", true);
+            $myService.unbind('mouseenter').unbind('mouseleave');
+            $myService.popover({
+                title: "<span class='fa fa-wechat'></span> 微信扫码",
+                trigger: 'hover',
+                placement: 'bottom',
+                html: 'true',
+                content: "<img src='" + $("#qrcode").find("img:first").attr('src') + "' />"
+            }).on('shown.bs.popover', function (event) {
+                const that = this;
+                $(this).parent().find('div.popover').on('mouseenter', function () {
+                    $(that).attr('in', true);
+                }).on('mouseleave', function () {
+                    $(that).removeAttr('in');
+                    $(that).popover('hide');
+                });
+            }).on('hide.bs.popover', function (event) {
+                if ($(this).attr('in')) {
+                    event.preventDefault();
+                }
             });
             });
-        }).on('hide.bs.popover', function (event) {
-            if ($(this).attr('in')) {
-                event.preventDefault();
-            }
-        });
-    }
+            $myService.popover('show');
+        }
+    })
 
 
-    document.getElementById("copyBtn").addEventListener('click', async event => {
-        //Get the copied text
-        const text = document.getElementById("licenseKey").value;
-        fallbackCopyTextToClipboard(text)
+    // 删除激活码
+    $("#removeBtn").bind('click', function(){
+        // 如果当前为恢复状态
+        BootstrapDialog.show({
+            title: "警告",
+            type: BootstrapDialog.TYPE_DANGER,
+            message: "删除激活码后,产品功能将不可用,确认是否删除?",
+            size: BootstrapDialog.SIZE_NORMAL,
+            buttons: [{
+                label: "确定",
+                action: function (dialog) {
+                    doPoster("/license/remove", {}, function (data) {
+                        if (data.success == true) {
+                            bootGrowl("删除激活码成功!", "success");
+                            doLoader("/license");
+                        } else {
+                            bootGrowl(data.resultValue, "danger");
+                        }
+                    });
+                    dialog.close();
+                }
+            }, {
+                label: "取消",
+                action: function (dialog) {
+                    dialog.close();
+                }
+            }]
+        });
     });
     });
 
 
+    // 在线激活
+    $("#activateBtn").bind('click', function(){
+        const $form = $("#licenseForm");
+        if ($form.formValidate() == true) {
+            const data = $form.serializeJson();
+            doPoster("/license/activate", data, function (data) {
+                if (data.success == true) {
+                    bootGrowl("在线激活成功!", "success");
+                    doLoader("/license");
+                } else {
+                    bootGrowl(data.resultValue, "danger");
+                }
+            });
+        }
+    });
 
 
-    // 旧浏览器的回退方案
-    function fallbackCopyTextToClipboard(text) {
-        var textArea = document.createElement("textarea");
-        textArea.value = text;
+    $("#copyBtn").bind('click', function(){
+        //Get the copied text
+        let textArea = document.createElement("textarea");
+        textArea.value = document.getElementById("licenseKey").value;
         textArea.style.height='0px';
         textArea.style.height='0px';
         document.body.appendChild(textArea);
         document.body.appendChild(textArea);
         textArea.focus();
         textArea.focus();
         textArea.select();
         textArea.select();
         try {
         try {
-            var successful = document.execCommand('copy');
+            let successful = document.execCommand('copy');
             if (successful) {
             if (successful) {
                 bootGrowl("复制机器码成功!", "success");
                 bootGrowl("复制机器码成功!", "success");
             }
             }
@@ -52,7 +95,7 @@ $(function () {
             console.error('复制失败', err);
             console.error('复制失败', err);
         }
         }
         document.body.removeChild(textArea);
         document.body.removeChild(textArea);
-    }
+    });
 
 
     $("#fileLicense").fileinput({
     $("#fileLicense").fileinput({
         theme: 'fas',
         theme: 'fas',