1
0
mirror of https://github.com/chatopera/cosin.git synced 2025-06-16 18:30:03 +08:00

https://gitee.com/cskefu/cskefu/issues/I836RO enable writedown qutoa for user account creation

Signed-off-by: Hai Liang Wang <hai@chatopera.com>
This commit is contained in:
Hai Liang Wang 2023-09-24 07:48:08 +08:00
parent 1cc39f5901
commit c6f9726ef3
12 changed files with 500 additions and 45 deletions

View File

@ -0,0 +1,47 @@
/*
* 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.
*/
package com.cskefu.cc.aspect;
import com.cskefu.cc.basic.MainContext;
import com.cskefu.cc.exception.BillingQuotaException;
import com.cskefu.cc.exception.BillingResourceException;
import com.cskefu.cc.model.User;
import com.cskefu.cc.proxy.LicenseProxy;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class UserAspect {
private final static Logger logger = LoggerFactory.getLogger(UserAspect.class);
@Autowired
private LicenseProxy licenseProxy;
@Before("execution(* com.cskefu.cc.persistence.repository.UserRepository.save(..))")
public void beforeSave(final JoinPoint joinPoint) throws BillingResourceException, BillingQuotaException {
final User user = (User) joinPoint.getArgs()[0];
logger.info("[save] before user id {}", user.getId());
if (StringUtils.isBlank(user.getId())) {
// 执行配额扣除
licenseProxy.writeDownResourceUsageInStore(MainContext.BillingResource.USER, 1);
} else {
// update existed user
}
}
}

View File

@ -220,12 +220,13 @@ public class Constants {
/** /**
* License * License
*/ */
public static final String PRODUCT_BASIC_ID = "cskefu001"; public static final String PRODUCT_ID_CSKEFU001 = "cskefu001";
public static final String LICENSE_SERVER_INST_ID = "SERVERINSTID"; public static final String LICENSE_SERVER_INST_ID = "SERVERINSTID";
public static final String LICENSE_SERVICE_NAME = "SERVICENAME"; public static final String LICENSE_SERVICE_NAME = "SERVICENAME";
public static final String LICENSE_SERVICE_NAME_PREFIX = "春松客服"; public static final String LICENSE_SERVICE_NAME_PREFIX = "春松客服";
public static final String LICENSEIDS = "LICENSEIDS"; public static final String LICENSEIDS = "LICENSEIDS";
public static final String METAKV_DATATYPE_STRING = "string"; public static final String METAKV_DATATYPE_STRING = "string";
public static final String METAKV_DATATYPE_INT = "int";
public static final String SHORTID = "shortId"; public static final String SHORTID = "shortId";
public static final String LICENSES = "licenses"; public static final String LICENSES = "licenses";
public static final String ADDDATE = "addDate"; public static final String ADDDATE = "addDate";
@ -234,4 +235,8 @@ public class Constants {
public static final String STATUS = "status"; public static final String STATUS = "status";
public static final String PRODUCT = "product"; public static final String PRODUCT = "product";
public static final String LICENSESTOREPROVIDER = "licenseStoreProvider"; public static final String LICENSESTOREPROVIDER = "licenseStoreProvider";
public static final String USER = "user";
public static final String RESOURCES_USAGE_KEY_PREFIX = "RESOURCES_USAGE";
public static final String NEW_USER_SUCCESS = "new_user_success";
public static final String PRODUCT_ID = "productId";
} }

View File

@ -1,15 +1,15 @@
/* /*
* Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd. * Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd.
* <https://www.chatopera.com>, Licensed under the Chunsong Public * <https://www.chatopera.com>, Licensed under the Chunsong Public
* License, Version 1.0 (the "License"), https://docs.cskefu.com/licenses/v1.html * License, Version 1.0 (the "License"), https://docs.cskefu.com/licenses/v1.html
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
* Copyright (C) 2018- Jun. 2023 Chatopera Inc, <https://www.chatopera.com>, Licensed under the Apache License, Version 2.0, * Copyright (C) 2018- Jun. 2023 Chatopera Inc, <https://www.chatopera.com>, Licensed under the Apache License, Version 2.0,
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* Copyright (C) 2017 优客服-多渠道客服系统, Licensed under the Apache License, Version 2.0, * Copyright (C) 2017 优客服-多渠道客服系统, Licensed under the Apache License, Version 2.0,
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
*/ */
@ -929,6 +929,31 @@ public class MainContext {
} }
} }
/**
* 计费资源
*/
public enum BillingResource {
USER, // 系统用户
AGENGUSER, // 访客会话
CONTACT, // 联系人
ORGAN, // 组织机构
CHANNELWEBIM; // 网页渠道
@Override
public String toString() {
return super.toString().toLowerCase();
}
public static BillingResource toValue(final String str) {
for (final BillingResource item : values()) {
if (StringUtils.equalsIgnoreCase(item.toString(), str)) {
return item;
}
}
throw new IllegalArgumentException();
}
}
public static void setApplicationContext(ApplicationContext context) { public static void setApplicationContext(ApplicationContext context) {
applicationContext = context; applicationContext = context;
context.getBean(TerminateBean.class); context.getBean(TerminateBean.class);

View File

@ -38,8 +38,8 @@ import java.util.List;
@Controller @Controller
@RequestMapping("/admin/license") @RequestMapping("/admin/license")
public class LicenseCtrl extends Handler { public class LicenseController extends Handler {
private final static Logger logger = LoggerFactory.getLogger(LicenseCtrl.class); private final static Logger logger = LoggerFactory.getLogger(LicenseController.class);
@Autowired @Autowired
private LicenseProxy licenseProxy; private LicenseProxy licenseProxy;
@ -50,7 +50,7 @@ public class LicenseCtrl extends Handler {
User user = super.getUser(request); User user = super.getUser(request);
if (user.isSuperadmin()) { if (user.isSuperadmin()) {
try { try {
List<JSONObject> licenses = licenseProxy.getLicensesFromStore(); List<JSONObject> licenses = licenseProxy.getLicensesInStore();
map.addAttribute(Constants.UPDATETIME, new Date()); map.addAttribute(Constants.UPDATETIME, new Date());
map.addAttribute(Constants.LICENSES, licenses); map.addAttribute(Constants.LICENSES, licenses);
map.addAttribute(Constants.LICENSESTOREPROVIDER, licenseProxy.getLicenseStoreProvider()); map.addAttribute(Constants.LICENSESTOREPROVIDER, licenseProxy.getLicenseStoreProvider());
@ -119,11 +119,11 @@ public class LicenseCtrl extends Handler {
licenseProxy.existLicenseInStore(licenseShortId); licenseProxy.existLicenseInStore(licenseShortId);
// 验证该证书的所属产品没有现在没有其它证书同一个产品最多只有一个证书 // 验证该证书的所属产品没有现在没有其它证书同一个产品最多只有一个证书
JSONObject licBasic = licenseProxy.getLicenseBasicsFromStore(licenseShortId); JSONObject licBasic = licenseProxy.getLicenseBasicsInStore(licenseShortId);
final String productId = licBasic.getJSONObject(Constants.PRODUCT).getString(Constants.SHORTID); final String productId = licBasic.getJSONObject(Constants.PRODUCT).getString(Constants.SHORTID);
boolean isProductAdded = false; boolean isProductAdded = false;
JSONArray addedLicenseBasicsFromStore = licenseProxy.getAddedLicenseBasicsFromStore(); JSONArray addedLicenseBasicsFromStore = licenseProxy.getAddedLicenseBasicsInStore();
for (int i = 0; i < addedLicenseBasicsFromStore.length(); i++) { for (int i = 0; i < addedLicenseBasicsFromStore.length(); i++) {
JSONObject item = (JSONObject) addedLicenseBasicsFromStore.get(i); JSONObject item = (JSONObject) addedLicenseBasicsFromStore.get(i);
@ -145,11 +145,12 @@ public class LicenseCtrl extends Handler {
JSONObject licenseKvData = new JSONObject(); JSONObject licenseKvData = new JSONObject();
licenseKvData.put(Constants.SHORTID, licenseShortId); licenseKvData.put(Constants.SHORTID, licenseShortId);
licenseKvData.put(Constants.ADDDATE, new Date()); licenseKvData.put(Constants.ADDDATE, new Date());
licenseKvData.put(Constants.PRODUCT_ID, productId);
currents.put(0, licenseKvData); currents.put(0, licenseKvData);
licenseProxy.createOrUpdateMetaKv(Constants.LICENSEIDS, currents.toString(), Constants.METAKV_DATATYPE_STRING); licenseProxy.createOrUpdateMetaKv(Constants.LICENSEIDS, currents.toString(), Constants.METAKV_DATATYPE_STRING);
// 跳转回到证书列表 // 跳转回到证书列表
List<JSONObject> licenses = licenseProxy.getLicensesFromStore(); List<JSONObject> licenses = licenseProxy.getLicensesInStore();
map.addAttribute(Constants.LICENSES, licenses); map.addAttribute(Constants.LICENSES, licenses);
map.addAttribute("updateTime", new Date()); map.addAttribute("updateTime", new Date());
map.addAttribute(Constants.LICENSESTOREPROVIDER, licenseProxy.getLicenseStoreProvider()); map.addAttribute(Constants.LICENSESTOREPROVIDER, licenseProxy.getLicenseStoreProvider());
@ -197,7 +198,7 @@ public class LicenseCtrl extends Handler {
licenseProxy.createOrUpdateMetaKv(Constants.LICENSEIDS, post.toString(), Constants.METAKV_DATATYPE_STRING); licenseProxy.createOrUpdateMetaKv(Constants.LICENSEIDS, post.toString(), Constants.METAKV_DATATYPE_STRING);
// 跳转回到证书列表 // 跳转回到证书列表
List<JSONObject> licenses = licenseProxy.getLicensesFromStore(); List<JSONObject> licenses = licenseProxy.getLicensesInStore();
map.addAttribute(Constants.LICENSES, licenses); map.addAttribute(Constants.LICENSES, licenses);
map.addAttribute("updateTime", new Date()); map.addAttribute("updateTime", new Date());
map.addAttribute(Constants.LICENSESTOREPROVIDER, licenseProxy.getLicenseStoreProvider()); map.addAttribute(Constants.LICENSESTOREPROVIDER, licenseProxy.getLicenseStoreProvider());

View File

@ -1,15 +1,15 @@
/* /*
* Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd. * Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd.
* <https://www.chatopera.com>, Licensed under the Chunsong Public * <https://www.chatopera.com>, Licensed under the Chunsong Public
* License, Version 1.0 (the "License"), https://docs.cskefu.com/licenses/v1.html * License, Version 1.0 (the "License"), https://docs.cskefu.com/licenses/v1.html
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
* Copyright (C) 2018- Jun. 2023 Chatopera Inc, <https://www.chatopera.com>, Licensed under the Apache License, Version 2.0, * Copyright (C) 2018- Jun. 2023 Chatopera Inc, <https://www.chatopera.com>, Licensed under the Apache License, Version 2.0,
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* Copyright (C) 2017 优客服-多渠道客服系统, Licensed under the Apache License, Version 2.0, * Copyright (C) 2017 优客服-多渠道客服系统, Licensed under the Apache License, Version 2.0,
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
*/ */
package com.cskefu.cc.controller.api; package com.cskefu.cc.controller.api;
@ -183,7 +183,9 @@ public class ApiUserController extends Handler {
User user = userProxy.parseUserFromJson(payload); User user = userProxy.parseUserFromJson(payload);
JsonObject resp = userProxy.createNewUser(user, parentOrgan); JsonObject resp = userProxy.createNewUser(user, parentOrgan);
if (StringUtils.isNotEmpty(roleId)) { if (StringUtils.isNotEmpty(roleId) &&
StringUtils.equals(resp.get(RestUtils.RESP_KEY_DATA).getAsString(),
Constants.NEW_USER_SUCCESS)) {
Role role = roleRes.findById(roleId).orElse(null); Role role = roleRes.findById(roleId).orElse(null);
UserRole userRole = new UserRole(); UserRole userRole = new UserRole();
userRole.setUser(user); userRole.setUser(user);

View File

@ -0,0 +1,44 @@
/*
* 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.
* Copyright (C) 2019-Jun. 2023 Chatopera Inc, <https://www.chatopera.com>,
* Licensed under the Apache License, Version 2.0,
* http://www.apache.org/licenses/LICENSE-2.0
*/
package com.cskefu.cc.exception;
public class BillingQuotaException extends Exception {
public final static String SUFFIX = "billingquotaexception.";
// metakv 中没有找到可以支持配额的证书类型
public final static String NO_LICENSE_FOUND = SUFFIX + "no_license_found";
// 返回值异常可能是网络连接不上稍后再试
public static final String RESPONSE_UNEXPECTED = SUFFIX + "response_unexpected";
// 请求参数不合法
public static final String INVALID_REQUEST_BODY = SUFFIX + "invalid_request_body";
// 证书在证书商店不存在
public static final String LICENSE_INVALID = SUFFIX + "license_invalid";
// 证书关联的产品信息不合法
public static final String PRODUCT_INVALID = SUFFIX + "product_invalid";
// 证书失效或耗尽
public static final String LICENSE_EXPIRED_OR_EXHAUSTED = SUFFIX + "license_expired_or_exhausted";
// 证书关闭了对该 serverinst 的支持
public static final String LICENSE_DISABLED_SERVERINST = SUFFIX + "license_disabled_serverinst";
// 证书不支持配额回退
public static final String LICENSE_UNSUPPORT_REFUND = SUFFIX + "license_unsupport_refund";
// 证书配额余量不足不能完成本次请求
public static final String LICENSE_QUOTA_INADEQUATE = SUFFIX + "license_quota_inadequate";
// 内部错误不应该发生
public static final String INTERNAL_ERROR = SUFFIX + "internal_error";
public BillingQuotaException(final String s) {
super(s);
}
}

View File

@ -0,0 +1,20 @@
/*
* 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.
* Copyright (C) 2019-Jun. 2023 Chatopera Inc, <https://www.chatopera.com>,
* Licensed under the Apache License, Version 2.0,
* http://www.apache.org/licenses/LICENSE-2.0
*/
package com.cskefu.cc.exception;
public class BillingResourceException extends Exception{
public BillingResourceException(final String s){
super(s);
}
}

View File

@ -0,0 +1,91 @@
/*
* 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.
*/
package com.cskefu.cc.model;
import org.json.JSONObject;
import java.io.Serializable;
public class ExecuteResult implements Serializable {
public final static int RC_SUCC = 0;
public final static int RC_ERR1 = 1;
public final static int RC_ERR2 = 2;
public final static int RC_ERR3 = 3;
public final static int RC_ERR4 = 4;
public final static int RC_ERR5 = 5;
public final static int RC_ERR6 = 6;
public final static int RC_ERR7 = 7;
public final static int RC_ERR8 = 8;
public final static int RC_ERR9 = 9;
private int rc; // 0 for success, errors other
private String error;
private String msg;
private JSONObject data;
public ExecuteResult() {
}
public ExecuteResult(final int rc, final String msg) {
this.rc = rc;
this.msg = msg;
}
public ExecuteResult(final int rc, final String msg, final String error) {
this.rc = rc;
this.msg = msg;
this.error = error;
}
public ExecuteResult(final int rc,
final String msg,
final String error,
final JSONObject data) {
this.rc = rc;
this.msg = msg;
this.error = error;
this.data = data;
}
public int getRc() {
return rc;
}
public void setRc(int rc) {
this.rc = rc;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public JSONObject getData() {
return data;
}
public void setData(JSONObject data) {
this.data = data;
}
}

View File

@ -18,9 +18,8 @@ import com.chatopera.store.sdk.exceptions.InvalidResponseException;
import com.cskefu.cc.basic.Constants; import com.cskefu.cc.basic.Constants;
import com.cskefu.cc.basic.MainContext; import com.cskefu.cc.basic.MainContext;
import com.cskefu.cc.basic.MainUtils; import com.cskefu.cc.basic.MainUtils;
import com.cskefu.cc.exception.LicenseNotFoundException; import com.cskefu.cc.exception.*;
import com.cskefu.cc.exception.MetaKvInvalidKeyException; import com.cskefu.cc.model.ExecuteResult;
import com.cskefu.cc.exception.MetaKvNotExistException;
import com.cskefu.cc.model.MetaKv; import com.cskefu.cc.model.MetaKv;
import com.cskefu.cc.persistence.repository.MetaKvRepository; import com.cskefu.cc.persistence.repository.MetaKvRepository;
import com.cskefu.cc.util.Base62; import com.cskefu.cc.util.Base62;
@ -50,6 +49,16 @@ public class LicenseProxy {
@Autowired @Autowired
private QuotaWdClient quotaWdClient; private QuotaWdClient quotaWdClient;
private static final Map<MainContext.BillingResource, Integer> BILLING_RES_QUOTA_MAPPINGS = new HashMap<>();
static {
BILLING_RES_QUOTA_MAPPINGS.put(MainContext.BillingResource.USER, 100);
BILLING_RES_QUOTA_MAPPINGS.put(MainContext.BillingResource.AGENGUSER, 1);
BILLING_RES_QUOTA_MAPPINGS.put(MainContext.BillingResource.CHANNELWEBIM, 100);
BILLING_RES_QUOTA_MAPPINGS.put(MainContext.BillingResource.CONTACT, 1);
BILLING_RES_QUOTA_MAPPINGS.put(MainContext.BillingResource.ORGAN, 10);
}
/** /**
* 初始化 serverinstId * 初始化 serverinstId
* serverinstId 作为服务唯一的实例ID * serverinstId 作为服务唯一的实例ID
@ -76,20 +85,31 @@ public class LicenseProxy {
/** /**
* Init local data for License * Init local data for License
*/ */
resolveServerinstId();
resolveServicename();
resolveLicenseIds();
}
/**
* 读取或初始化 serverinstId
*
* @return
*/
private String resolveServerinstId() {
Optional<MetaKv> metaServerinstIdOpt = metaKvRes.findFirstByMetakey(Constants.LICENSE_SERVER_INST_ID); Optional<MetaKv> metaServerinstIdOpt = metaKvRes.findFirstByMetakey(Constants.LICENSE_SERVER_INST_ID);
if (metaServerinstIdOpt.isEmpty()) { if (metaServerinstIdOpt.isEmpty()) {
// 没有 serverinstId 信息初始化 // 没有 serverinstId 信息初始化
final String serverinstId = MainUtils.getUUID(); final String serverinstId = MainUtils.getUUID();
createMetaKv(Constants.LICENSE_SERVER_INST_ID, serverinstId, Constants.METAKV_DATATYPE_STRING); createMetaKv(Constants.LICENSE_SERVER_INST_ID, serverinstId, Constants.METAKV_DATATYPE_STRING);
return serverinstId;
} }
return metaServerinstIdOpt.get().getMetavalue();
}
Optional<MetaKv> metaServicenameOpt = metaKvRes.findFirstByMetakey(Constants.LICENSE_SERVICE_NAME); /**
if (metaServicenameOpt.isEmpty()) { * 读取或初始化 licenseIds
// 没有 Service Name 信息初始化 */
final String serviceName = generateLicenseServiceName(); private void resolveLicenseIds() {
createMetaKv(Constants.LICENSE_SERVICE_NAME, serviceName, Constants.METAKV_DATATYPE_STRING);
}
Optional<MetaKv> metaLicensesOpt = metaKvRes.findFirstByMetakey(Constants.LICENSEIDS); Optional<MetaKv> metaLicensesOpt = metaKvRes.findFirstByMetakey(Constants.LICENSEIDS);
if (metaLicensesOpt.isEmpty()) { if (metaLicensesOpt.isEmpty()) {
// 没有 license 信息初始化 // 没有 license 信息初始化
@ -97,6 +117,22 @@ public class LicenseProxy {
} }
} }
/**
* 读取或初始化 serviceName
*
* @return
*/
private String resolveServicename() {
Optional<MetaKv> metaServicenameOpt = metaKvRes.findFirstByMetakey(Constants.LICENSE_SERVICE_NAME);
if (metaServicenameOpt.isEmpty()) {
// 没有 Service Name 信息初始化
final String serviceName = generateLicenseServiceName();
createMetaKv(Constants.LICENSE_SERVICE_NAME, serviceName, Constants.METAKV_DATATYPE_STRING);
return serviceName;
}
return metaServicenameOpt.get().getMetavalue();
}
/** /**
* MetaKv 表中取得数据 MetaKv * MetaKv 表中取得数据 MetaKv
* *
@ -159,6 +195,27 @@ public class LicenseProxy {
return metakv; return metakv;
} }
/**
* 增加 MetaKv 中的 Key 的值作为 Integer 做增量不存在则初始化其值为 0然后增量操作
*
* @param key
* @param incrValue
*/
public MetaKv increValueInMetaKv(final String key, final int incrValue) {
try {
MetaKv kv = retrieveMetaKv(key);
int pre = Integer.parseInt(kv.getMetavalue());
kv.setMetavalue(Integer.toString(pre + incrValue));
kv.setUpdatetime(new Date());
metaKvRes.save(kv);
return kv;
} catch (MetaKvNotExistException e) {
return createMetaKv(key, Integer.toString(incrValue), Constants.METAKV_DATATYPE_INT);
} catch (MetaKvInvalidKeyException e) {
throw new RuntimeException(e);
}
}
/** /**
* 生成随机字符串作为服务名称 * 生成随机字符串作为服务名称
* *
@ -178,7 +235,7 @@ public class LicenseProxy {
* *
* @return * @return
*/ */
public List<JSONObject> getLicensesFromStore() throws InvalidResponseException { public List<JSONObject> getLicensesInStore() throws InvalidResponseException {
List<JSONObject> result = new ArrayList<>(); List<JSONObject> result = new ArrayList<>();
try { try {
@ -240,7 +297,7 @@ public class LicenseProxy {
* @return * @return
* @throws InvalidResponseException * @throws InvalidResponseException
*/ */
public JSONObject getLicenseBasicsFromStore(final String licenseShortId) throws InvalidResponseException, InvalidRequestException { public JSONObject getLicenseBasicsInStore(final String licenseShortId) throws InvalidResponseException, InvalidRequestException {
Response resp = quotaWdClient.getLicenseBasics(licenseShortId); Response resp = quotaWdClient.getLicenseBasics(licenseShortId);
if (resp.getRc() == 0) { if (resp.getRc() == 0) {
JSONArray data = (JSONArray) resp.getData(); JSONArray data = (JSONArray) resp.getData();
@ -260,7 +317,7 @@ public class LicenseProxy {
* @return * @return
* @throws InvalidResponseException * @throws InvalidResponseException
*/ */
public JSONArray getAddedLicenseBasicsFromStore() throws InvalidResponseException { public JSONArray getAddedLicenseBasicsInStore() throws InvalidResponseException {
JSONArray arr = getLicensesInMetakv(); JSONArray arr = getLicensesInMetakv();
List<String> ids = new ArrayList<>(); List<String> ids = new ArrayList<>();
@ -314,4 +371,120 @@ public class LicenseProxy {
public String getLicenseStoreProvider() { public String getLicenseStoreProvider() {
return quotaWdClient.getBaseUrl(); return quotaWdClient.getBaseUrl();
} }
/**
* 获得资源用量存储
*
* @param resourceKey
* @return
*/
public String getResourceUsageKey(final String resourceKey) {
StringBuffer sb = new StringBuffer();
sb.append(Constants.RESOURCES_USAGE_KEY_PREFIX);
sb.append("_");
sb.append(StringUtils.toRootUpperCase(resourceKey));
return sb.toString();
}
/**
* 增加计费资源用量
*
* @param billingResource
* @param consume
*/
public void increResourceUsageInMetaKv(final MainContext.BillingResource billingResource, int consume) throws BillingResourceException {
switch (billingResource) {
case USER:
case CONTACT:
case ORGAN:
case AGENGUSER:
case CHANNELWEBIM:
increValueInMetaKv(getResourceUsageKey(billingResource.toString()), consume);
break;
default:
throw new BillingResourceException("invalid_billing_resource_type");
}
}
/**
* 获得春松客服 cskefu001 产品的证书标识 ID
* 春松客服证书基本类型
*
* @return
*/
private String getLicenseIdAsCskefu001InMetaKv() throws BillingQuotaException {
JSONArray curr = getLicensesInMetakv();
for (int i = 0; i < curr.length(); i++) {
JSONObject jo = (JSONObject) curr.get(i);
if (jo.has(Constants.PRODUCT_ID) &&
StringUtils.equals(jo.getString(Constants.PRODUCT_ID), Constants.PRODUCT_ID_CSKEFU001)) {
return jo.getString(Constants.SHORTID);
}
}
throw new BillingQuotaException(BillingQuotaException.NO_LICENSE_FOUND);
}
/**
* 执行配额变更操作
*
* @param billingResource
* @param consume
* @return
*/
public ExecuteResult writeDownResourceUsageInStore(final MainContext.BillingResource billingResource, int consume) throws BillingQuotaException, BillingResourceException {
ExecuteResult er = new ExecuteResult();
// 请求操作配额
String licenseId = getLicenseIdAsCskefu001InMetaKv();
String serverinstId = resolveServerinstId();
String servicename = resolveServicename();
try {
Response resp = quotaWdClient.write(licenseId,
serverinstId, servicename, consume * BILLING_RES_QUOTA_MAPPINGS.get(billingResource));
// 识别操作是否完成并处理
if (resp.getRc() == 0) {
final JSONObject data = (JSONObject) resp.getData();
// 配额操作成功执行计数
increResourceUsageInMetaKv(billingResource, consume);
} else if (resp.getRc() == 1 || resp.getRc() == 2) {
throw new BillingQuotaException(BillingQuotaException.INVALID_REQUEST_BODY);
} else if (resp.getRc() == 3) {
// 证书商店中不存在
throw new BillingQuotaException(BillingQuotaException.LICENSE_INVALID);
} else if (resp.getRc() == 4) {
// 证书商店中不存在该产品或产品类型无效
throw new BillingQuotaException(BillingQuotaException.PRODUCT_INVALID);
} else if (resp.getRc() == 5) {
// 该证书不支持资源回退
throw new BillingQuotaException(BillingQuotaException.LICENSE_UNSUPPORT_REFUND);
} else if (resp.getRc() == 6) {
// 证书失效或耗尽不支持继续扣除配额
throw new BillingQuotaException(BillingQuotaException.LICENSE_EXPIRED_OR_EXHAUSTED);
} else if (resp.getRc() == 7) {
// 该证书禁用了该 serverinstId
throw new BillingQuotaException(BillingQuotaException.LICENSE_DISABLED_SERVERINST);
} else if (resp.getRc() == 8) {
// 配额扣除额度超过该证书目前的剩余量
throw new BillingQuotaException(BillingQuotaException.LICENSE_QUOTA_INADEQUATE);
} else {
// 未知情况
logger.error("[writeDownResourceUsageInStore] resp data {}", resp.toString());
throw new BillingQuotaException(BillingQuotaException.INTERNAL_ERROR);
}
} catch (InvalidResponseException e) {
// TODO 处理异常信息
logger.error("[writeDownResourceUsageInStore] error ", e);
throw new BillingQuotaException(BillingQuotaException.RESPONSE_UNEXPECTED);
}
// er.setRc(ExecuteResult.RC_ERR1);
// if (er.getRc() != ExecuteResult.RC_SUCC) {
// throw new BillingQuotaException(BillingQuotaException.NO_LICENSE_FOUND);
// }
return er;
}
} }

View File

@ -1,13 +1,13 @@
/* /*
* Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd. * Copyright (C) 2023 Beijing Huaxia Chunsong Technology Co., Ltd.
* <https://www.chatopera.com>, Licensed under the Chunsong Public * <https://www.chatopera.com>, Licensed under the Chunsong Public
* License, Version 1.0 (the "License"), https://docs.cskefu.com/licenses/v1.html * License, Version 1.0 (the "License"), https://docs.cskefu.com/licenses/v1.html
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
* Copyright (C) 2019-2022 Chatopera Inc, All rights reserved. * Copyright (C) 2019-2022 Chatopera Inc, All rights reserved.
* <https://www.chatopera.com> * <https://www.chatopera.com>
*/ */
@ -16,6 +16,7 @@ package com.cskefu.cc.proxy;
import com.cskefu.cc.basic.Constants; import com.cskefu.cc.basic.Constants;
import com.cskefu.cc.basic.MainContext; import com.cskefu.cc.basic.MainContext;
import com.cskefu.cc.basic.MainUtils; import com.cskefu.cc.basic.MainUtils;
import com.cskefu.cc.exception.BillingQuotaException;
import com.cskefu.cc.model.*; import com.cskefu.cc.model.*;
import com.cskefu.cc.persistence.repository.*; import com.cskefu.cc.persistence.repository.*;
import com.cskefu.cc.util.restapi.RestUtils; import com.cskefu.cc.util.restapi.RestUtils;
@ -29,6 +30,8 @@ import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import jakarta.persistence.criteria.Predicate; import jakarta.persistence.criteria.Predicate;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -79,22 +82,34 @@ public class UserProxy {
public JsonObject createNewUser(final User user, Organ organ) { public JsonObject createNewUser(final User user, Organ organ) {
JsonObject result = new JsonObject(); JsonObject result = new JsonObject();
String msg = validUser(user); String msg = validUser(user);
if (StringUtils.equalsIgnoreCase(msg, "new_user_success")) {
if (StringUtils.equalsIgnoreCase(msg, Constants.NEW_USER_SUCCESS)) {
// 此时 msg new_user_success // 此时 msg new_user_success
user.setSuperadmin(false); // 不支持创建第二个系统管理员 user.setSuperadmin(false); // 不支持创建第二个系统管理员
try {
if (StringUtils.isNotBlank(user.getPassword())) {
user.setPassword(MainUtils.md5(user.getPassword()));
}
userRes.save(user);
if (StringUtils.isNotBlank(user.getPassword())) { if (organ != null) {
user.setPassword(MainUtils.md5(user.getPassword())); OrganUser ou = new OrganUser();
} ou.setUserid(user.getId());
userRes.save(user); ou.setOrgan(organ.getId());
organUserRes.save(ou);
if (organ != null) { }
OrganUser ou = new OrganUser(); } catch (Exception e) {
ou.setUserid(user.getId()); if (e instanceof UndeclaredThrowableException) {
ou.setOrgan(organ.getId()); logger.error("[createNewUser] BillingQuotaException", e);
organUserRes.save(ou); if (StringUtils.startsWith(e.getCause().getMessage(), BillingQuotaException.SUFFIX)) {
msg = e.getCause().getMessage();
}
} else {
logger.error("[createNewUser] err", e);
}
} }
} }
// 新账号未通过验证返回创建失败信息msg // 新账号未通过验证返回创建失败信息msg
result.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_SUCC); result.addProperty(RestUtils.RESP_KEY_RC, RestUtils.RESP_RC_SUCC);
result.addProperty(RestUtils.RESP_KEY_DATA, msg); result.addProperty(RestUtils.RESP_KEY_DATA, msg);

View File

@ -56,5 +56,35 @@ function processUserAddOrUpdateResult(responsecode, cb){
layer.msg('用户编辑成功',{icon: 1, time: 1000}); layer.msg('用户编辑成功',{icon: 1, time: 1000});
cb(); cb();
break; break;
case 'billingquotaexception.no_license_found':
layer.msg('【使用授权证书】证书不存在,联系系统超级管理员导入。',{icon: 2, time: 5000});
break;
case 'billingquotaexception.response_unexpected':
layer.msg('【使用授权证书】证书商店返回异常,稍后再试。',{icon: 2, time: 5000});
break;
case 'billingquotaexception.invalid_request_body':
layer.msg('【使用授权证书】请求证书商店参数不合法,请获取最新软件代码。',{icon: 2, time: 5000});
break;
case 'billingquotaexception.license_invalid':
layer.msg('【使用授权证书】证书商店中不存在该证书,请联系系统超级管理员导入。',{icon: 2, time: 5000});
break;
case 'billingquotaexception.product_invalid':
layer.msg('【使用授权证书】产品或产品款式不存在,请联系系统超级管理员导入新证书。',{icon: 2, time: 5000});
break;
case 'billingquotaexception.license_expired_or_exhausted':
layer.msg('【使用授权证书】证书过期或耗尽,请升级证书或绑定新证书。',{icon: 2, time: 5000});
break;
case 'billingquotaexception.license_disabled_serverinst':
layer.msg('【使用授权证书】证书商店禁用了本服务实例。',{icon: 2, time: 5000});
break;
case 'billingquotaexception.license_unsupport_refund':
layer.msg('【使用授权证书】目前使用的证书不支持回退配额。',{icon: 2, time: 5000});
break;
case 'billingquotaexception.license_quota_inadequate':
layer.msg('【使用授权证书】本次操作需要使用的配额资源超过证书中剩余配额,请联系系统超级管理员升级证书。',{icon: 2, time: 5000});
break;
case 'billingquotaexception.internal_error':
layer.msg('【使用授权证书】系统错误,稍后再试。',{icon: 2, time: 5000});
break;
} }
} }

View File

@ -121,6 +121,8 @@ block content
layer.msg('新用户创建成功',{icon: 1, time: 1000}) layer.msg('新用户创建成功',{icon: 1, time: 1000})
else if (msg == 'edit_user_success') else if (msg == 'edit_user_success')
layer.msg('用户编辑成功', {icon: 1, time: 1000}) layer.msg('用户编辑成功', {icon: 1, time: 1000})
else if (msg == 'billingquotaexception.no_license_found')
layer.msg('证书不存在,联系系统超级管理员导入授权使用证书',{icon: 2, time: 3000})
}); });
layui.use(['laypage', 'layer'], function(){ layui.use(['laypage', 'layer'], function(){
var laypage = layui.laypage var laypage = layui.laypage