1
0
mirror of https://github.com/chatopera/cosin.git synced 2025-07-24 08:31:45 +08:00

https://gitee.com/cskefu/cskefu/issues/I836RO add billing resource for user, add server instance section

Signed-off-by: Hai Liang Wang <hai@chatopera.com>
This commit is contained in:
Hai Liang Wang 2023-09-24 14:48:59 +08:00
parent c6f9726ef3
commit 3cb64fa664
7 changed files with 170 additions and 14 deletions

View File

@ -45,7 +45,7 @@ public class LicenseController extends Handler {
private LicenseProxy licenseProxy;
@RequestMapping("/index")
@Menu(type = "admin", subtype = "license")
@Menu(type = "admin", subtype = "licenseList")
public ModelAndView index(ModelMap map, HttpServletRequest request) {
User user = super.getUser(request);
if (user.isSuperadmin()) {
@ -65,7 +65,7 @@ public class LicenseController extends Handler {
}
@RequestMapping("/add")
@Menu(type = "admin", subtype = "license")
@Menu(type = "admin", subtype = "licenseList")
public ModelAndView add(ModelMap map, HttpServletRequest request) {
User user = super.getUser(request);
if (user.isSuperadmin()) {
@ -84,7 +84,7 @@ public class LicenseController extends Handler {
* @return
*/
@RequestMapping("/save")
@Menu(type = "admin", subtype = "license")
@Menu(type = "admin", subtype = "licenseList")
public ModelAndView save(ModelMap map,
HttpServletRequest request,
@Valid String licenseShortId) throws MetaKvInvalidKeyException, InvalidRequestException {
@ -152,7 +152,7 @@ public class LicenseController extends Handler {
// 跳转回到证书列表
List<JSONObject> licenses = licenseProxy.getLicensesInStore();
map.addAttribute(Constants.LICENSES, licenses);
map.addAttribute("updateTime", new Date());
map.addAttribute(Constants.UPDATETIME, new Date());
map.addAttribute(Constants.LICENSESTOREPROVIDER, licenseProxy.getLicenseStoreProvider());
return request(super.createView("/admin/license/index"));
@ -173,7 +173,7 @@ public class LicenseController extends Handler {
}
@RequestMapping("/delete/{licenseShortId}")
@Menu(type = "admin", subtype = "license")
@Menu(type = "admin", subtype = "licenseList")
public ModelAndView delete(ModelMap map,
HttpServletRequest request,
@PathVariable String licenseShortId) throws MetaKvInvalidKeyException {
@ -215,4 +215,18 @@ public class LicenseController extends Handler {
}
}
@RequestMapping("/instance")
@Menu(type = "admin", subtype = "licenseInst")
public ModelAndView getInstanceInfo(ModelMap map, HttpServletRequest request) {
User user = super.getUser(request);
if (user.isSuperadmin()) {
map.addAttribute(Constants.LICENSE_SERVICE_NAME, licenseProxy.resolveServicename());
map.addAttribute(Constants.LICENSE_SERVER_INST_ID, licenseProxy.resolveServerinstId());
map.addAttribute(Constants.LICENSESTOREPROVIDER, licenseProxy.getLicenseStoreProvider());
return request(super.createView("/admin/license/instance"));
} else {
return request(super.createView("/public/error"));
}
}
}

View File

@ -95,11 +95,11 @@ public class LicenseProxy {
*
* @return
*/
private String resolveServerinstId() {
public String resolveServerinstId() {
Optional<MetaKv> metaServerinstIdOpt = metaKvRes.findFirstByMetakey(Constants.LICENSE_SERVER_INST_ID);
if (metaServerinstIdOpt.isEmpty()) {
// 没有 serverinstId 信息初始化
final String serverinstId = MainUtils.getUUID();
final String serverinstId = Base62.generateShortId();
createMetaKv(Constants.LICENSE_SERVER_INST_ID, serverinstId, Constants.METAKV_DATATYPE_STRING);
return serverinstId;
}
@ -122,7 +122,7 @@ public class LicenseProxy {
*
* @return
*/
private String resolveServicename() {
public String resolveServicename() {
Optional<MetaKv> metaServicenameOpt = metaKvRes.findFirstByMetakey(Constants.LICENSE_SERVICE_NAME);
if (metaServicenameOpt.isEmpty()) {
// 没有 Service Name 信息初始化

View File

@ -15,8 +15,11 @@
package com.cskefu.cc.util;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
public class Base62 {
@ -63,8 +66,20 @@ public class Base62 {
System.out.println(messagr);
}
/**
* 生成 16 位随机字符串作为 ID
* https://stackoverflow.com/questions/4267475/generating-8-character-only-uuids
*/
public static String generateShortId() {
SimpleDateFormat df = new SimpleDateFormat("yyMMdd");
String randomStr = RandomStringUtils.randomAlphanumeric(10);
return df.format(new Date()) + randomStr;
}
/**
* 生成随机字符串
*
* @param targetStringLength
* @return
*/

View File

@ -12,6 +12,8 @@ package com.cskefu.cc.util;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.chatopera.store.enums.LICSTATUS;
import com.chatopera.store.exceptions.EnumValueException;
import org.apache.commons.lang3.StringUtils;
import java.text.SimpleDateFormat;
@ -104,4 +106,58 @@ public class PugHelper {
Collections.reverse(result);
return result;
}
/**
* 获得证书状态的中文
*
* @param status
* @return
*/
public String getLicstatusInChinese(final String status) {
try {
LICSTATUS licstatus = LICSTATUS.toValue(status);
switch (licstatus) {
case NOTFOUND -> {
return "未找到";
}
case EXHAUSTED -> {
return "配额耗尽";
}
case INUSE -> {
return "使用中";
}
case EXPIRED -> {
return "已过期";
}
default -> {
return status;
}
}
} catch (EnumValueException e) {
return "未知";
}
}
/**
* 截取字符串首先根据分隔符分隔然后选取前 N 使用连接符连接返回
* @param orignal
* @param splitBy
* @param firstN
* @param joinWith
* @return
*/
public String splitStringAndJoinWith(final String orignal, final String splitBy, final int firstN, final String joinWith) {
String[] splits = StringUtils.split(orignal, splitBy);
int n = Math.min(splits.length, firstN);
List<String> joined = new ArrayList<>();
for (int i = 0; i < n; i++) {
joined.add(splits[i]);
}
if (joined.size() > 0) {
return StringUtils.join(joined, joinWith);
} else {
return "";
}
}
}

View File

@ -70,13 +70,18 @@ ul.layui-nav.layui-nav-tree(lay-filter='demo')
a(href='/admin/metadata/index.html') 元数据
dd(class={'layui-this': subtype == 'template'})
a(href='/admin/template/index.html') 系统模板
dd(class={'layui-this': subtype == 'license'})
a(href='/admin/license/index.html') 使用授权证书
//dd(class={'layui-this': subtype == 'email'})
// a(href='/admin/email/index.html') 邮件通知设置
//dd(class={'layui-this': subtype == 'sms'})
// a(href='/admin/sms/index.html') 短信通知设置
if user.superadmin
li.layui-nav-item.layui-nav-itemed
a.layui-nav-title(href='javascript:;') 使用授权
dl.layui-nav-child
dd(class={'layui-this': subtype == 'licenseInst'})
a(href='/admin/license/instance.html') 实例信息
dd(class={'layui-this': subtype == 'licenseList'})
a(href='/admin/license/index.html') 授权证书列表
script.
function openChatbot() {
window.parent.active.tabAdd($(".iframe_btn").data('href'), $(".iframe_btn").data('title'), $(".iframe_btn").data('id'));

View File

@ -23,12 +23,17 @@ block content
span 打开证书商店
.row(style='padding:5px;')
blockquote.layui-elem-quote.layui-quote-nm
i.layui-icon(style="color:gray") &#xe60b;
font(color="#999").layui-word-aux balala ...
.col-lg-12
table.layui-table(lay-skin='line')
colgroup
col(width='10%')
col(width='10%')
col(width='15%')
col(width='10%')
col(width='20%')
col(width='10%')
col(width='10%')
col(width='10%')
@ -37,6 +42,7 @@ block content
thead
tr
th 证书 ID
th 状态
th 产品标识
th 产品名称
th 有效期截止
@ -49,12 +55,13 @@ block content
tr
- var messupLicenseShortId = pugHelper.messupStringWithStars(item.license.shortId)
td= messupLicenseShortId
td= pugHelper.getLicstatusInChinese(item.license.status)
td= item.product.shortId
td= item.product.name
td= item.license.effectivedateend
td= pugHelper.splitStringAndJoinWith(item.license.effectivedateend, " ", 1, "")
td= item.license.quotaeffectiveremaining
td= item.user.nickname
td= pugHelper.formatDate('yyyy-MM-dd HH:mm:ss', item.addDate)
td= pugHelper.formatDate('yyyy-MM-dd', item.addDate)
td(style="white-space:nowrap;" nowrap="nowrap")
a(href="#", onclick="copyLicenseId2ClipboardOnOS('" + item.license.shortId + "');return false;")
i.layui-icon &#xe642;

View File

@ -0,0 +1,59 @@
//- Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd.
//- <https://www.chatopera.com>, Licensed under the Chunsong Public
//- License, Version 1.0 (the "License"), https://docs.cskefu.com/licenses/v1.html
//- Unless required by applicable law or agreed to in writing, software
//- distributed under the License is distributed on an "AS IS" BASIS,
//- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//- See the License for the specific language governing permissions and
//- limitations under the License.
extends /admin/include/layout.pug
block content
.row: .col-lg-12
h1.site-h1(style='background-color:#FFFFFF;')
| 实例信息
span(style='float:right;')
.layui-btn-group.ukefu-btn-group
button.layui-btn.layui-btn-small(onclick='openLicenseStorePage()')
span 打开证书商店
.row(style='padding:5px;')
blockquote.layui-elem-quote.layui-quote-nm
i.layui-icon(style="color:gray") &#xe60b;
font(color="#999").layui-word-aux balala ...
.col-lg-12
fieldset.layui-elem-field
legend(style='font-size:13px!important;') 实例 ID
span.layui-field-box #{SERVERINSTID}
span &nbsp;&nbsp;
button(style='font-size:10px!important;' onclick='copyServerinstId2ClipboardOnOS("' +SERVERINSTID + '")')
span 复制
fieldset.layui-elem-field
legend(style='font-size:13px!important;') 实例名称
span.layui-field-box #{SERVICENAME}
span &nbsp;&nbsp;
button(style='font-size:10px!important;' onclick='copyServicename2ClipboardOnOS("' +SERVICENAME + '")')
span 复制
script.
layui.use(['laypage', 'layer'], function () {
var laypage = layui.laypage
, layer = layui.layer;
});
function copyServerinstId2ClipboardOnOS(val){
copyValue2ClipboardOnOS(val, (err) => {
top.layer.msg('复制完成', {icon: 1, time: 2000, offset: 't'});
})
}
function copyServicename2ClipboardOnOS(val){
copyValue2ClipboardOnOS(val, (err) => {
top.layer.msg('复制完成', {icon: 1, time: 2000, offset: 't'});
})
}
function openLicenseStorePage() {
var licenseStoreProvider = "#{licenseStoreProvider}";
window.open(licenseStoreProvider, "_blank");
}