From b7d0c5e9ddfedc068d2f82fab530e71043a7f4af Mon Sep 17 00:00:00 2001
From: zj <1772600164@qq.com>
Date: Tue, 23 Apr 2024 19:50:14 +0800
Subject: [PATCH] 理财
---
src/main/java/com/nq/utils/task/YEBTask.java | 2
src/main/java/com/nq/pojo/OrderEchoBean.java | 18 ++--
src/main/java/com/nq/service/IEchoServices.java | 3
src/main/java/com/nq/service/impl/ApplyLeverServicesimpl.java | 1
src/main/java/com/nq/controller/echo/EChoController.java | 23 +++--
target/classes/mapper/UserMapper.xml | 4
src/main/java/com/nq/service/impl/EchoServices.java | 57 +++++++++++---
src/main/java/com/nq/pojo/EChoBean.java | 8 ++
target/classes/mapper/OrderEchoMapper.xml | 21 ++--
src/main/java/com/nq/common/interceptor/ApiUserAuthorityInterceptor.java | 5 +
src/main/java/com/nq/service/impl/UserServiceImpl.java | 23 +++++
src/main/java/com/nq/dao/OrderEchoMapper.java | 3
src/main/java/com/nq/controller/backend/AdminController.java | 10 +
src/main/java/com/nq/pojo/reponse/OrderEChoReponse.java | 40 ++++++++++
src/main/resources/mapper/OrderEchoMapper.xml | 21 ++--
15 files changed, 179 insertions(+), 60 deletions(-)
diff --git a/src/main/java/com/nq/common/interceptor/ApiUserAuthorityInterceptor.java b/src/main/java/com/nq/common/interceptor/ApiUserAuthorityInterceptor.java
index a1bc556..01403d5 100644
--- a/src/main/java/com/nq/common/interceptor/ApiUserAuthorityInterceptor.java
+++ b/src/main/java/com/nq/common/interceptor/ApiUserAuthorityInterceptor.java
@@ -11,6 +11,7 @@
import com.nq.utils.PropertiesUtil;
import com.nq.utils.redis.JsonUtil;
import com.nq.utils.redis.RedisShardedPoolUtils;
+import com.nq.utils.translate.GoogleTranslateUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
@@ -73,11 +74,13 @@
return true;
}
User currentUser = getCurrentUser(httpServletRequest);
+ GoogleTranslateUtil googleTranslateUtil = new GoogleTranslateUtil();
+ String lang = httpServletRequest.getHeader("lang");
if (null == currentUser) {
httpServletResponse.setCharacterEncoding("UTF-8");
httpServletResponse.setContentType("application/json;charset=UTF-8");
PrintWriter writer = httpServletResponse.getWriter();
- writer.print( new Gson().toJson(ServerResponse.createByErrorCodeMsg(401,"请登录")));
+ writer.print( new Gson().toJson(ServerResponse.createByErrorCodeMsg(401,googleTranslateUtil.translate("请登录",lang ))));
writer.flush();
writer.close();
return false;
diff --git a/src/main/java/com/nq/controller/backend/AdminController.java b/src/main/java/com/nq/controller/backend/AdminController.java
index 632a2f2..47c257a 100644
--- a/src/main/java/com/nq/controller/backend/AdminController.java
+++ b/src/main/java/com/nq/controller/backend/AdminController.java
@@ -244,13 +244,15 @@
@RequestParam("edesc") String desc,
@RequestParam("isOpen") String isOpen,
@RequestParam("returnTime") String returnTime,
- @RequestParam("title") String title){
+ @RequestParam("title") String title,
+ @RequestParam("buyLowestNum") Integer buyLowestNum){
EChoBean eChoBean = new EChoBean();
eChoBean.setEdesc(desc);
eChoBean.setIsOpen(isOpen);
eChoBean.setReturnTime(returnTime);
eChoBean.setTitle(title);
eChoBean.setReturnOfRate(returnOfRate);
+ eChoBean.setBuyLowestNum(buyLowestNum);
if(iEchoServices.insertEcho(eChoBean) >= 1){
return ServerResponse.createBySuccess();
}else{
@@ -269,8 +271,9 @@
@RequestParam("isOpen") String isOpen,
@RequestParam("returnTime") String returnTime,
@RequestParam("title") String title,
- @RequestParam("id") Integer id
- ){
+ @RequestParam("id") Integer id,
+ @RequestParam("buyLowestNum") Integer buyLowestNum
+ ){
EChoBean eChoBean = new EChoBean();
eChoBean.setId(id);
eChoBean.setEdesc(desc);
@@ -278,6 +281,7 @@
eChoBean.setReturnTime(returnTime);
eChoBean.setTitle(title);
eChoBean.setReturnOfRate(returnOfRate);
+ eChoBean.setBuyLowestNum(buyLowestNum);
if(iEchoServices.updateEcho(eChoBean) >= 1){
return ServerResponse.createBySuccess();
}else{
diff --git a/src/main/java/com/nq/controller/echo/EChoController.java b/src/main/java/com/nq/controller/echo/EChoController.java
index a22df4e..8db1b85 100644
--- a/src/main/java/com/nq/controller/echo/EChoController.java
+++ b/src/main/java/com/nq/controller/echo/EChoController.java
@@ -1,9 +1,10 @@
package com.nq.controller.echo;
import com.nq.common.ServerResponse;
-import com.nq.pojo.User;
+import com.nq.dao.EChoMapper;
+import com.nq.pojo.EChoBean;
import com.nq.service.IEchoServices;
-import com.nq.service.IUserService;
+import com.nq.utils.translate.GoogleTranslateUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
@@ -24,7 +25,7 @@
IEchoServices iEchoServices;
@Autowired
- IUserService iUserService;
+ EChoMapper eChoMapper;
@PostMapping("queryEcho.do")
@ResponseBody
@@ -36,14 +37,18 @@
@PostMapping("buyEcho.do")
@ResponseBody
public ServerResponse buyEcho(@RequestParam("eId") String eid, @RequestParam("money") Integer money, HttpServletRequest request) {
- User user = this.iUserService.getCurrentRefreshUser(request);
- if(user == null){
- return ServerResponse.createByErrorMsg("Please Login");
+ EChoBean eChoBean = eChoMapper.selectById(Integer.parseInt(eid));
+ if (eChoBean == null) {
+ return ServerResponse.createByErrorMsg("基金产品不存在",request);
+ } else {
+ if (null != eChoBean.getBuyLowestNum() && money < eChoBean.getBuyLowestNum()) {
+ return ServerResponse.createByErrorMsg("购买失败,最低购买数量:"+eChoBean.getBuyLowestNum(),request);
+ }
}
if (iEchoServices.buyECho(eid, money,request)) {
- return ServerResponse.createBySuccess("Buy Successed");
+ return ServerResponse.createBySuccess("购买成功",request);
} else {
- return ServerResponse.createByErrorMsg("Buy Fail");
+ return ServerResponse.createByErrorMsg("购买失败",request);
}
}
@@ -51,7 +56,7 @@
@PostMapping("queryOrderEcho.do")
@ResponseBody
public ServerResponse queryOrderEcho(HttpServletRequest request) {
- return ServerResponse.createBySuccess(iEchoServices.queryOrderEcho(request));
+ return ServerResponse.createBySuccess(iEchoServices.queryOrderEcho(request));
}
diff --git a/src/main/java/com/nq/dao/OrderEchoMapper.java b/src/main/java/com/nq/dao/OrderEchoMapper.java
index 2f7cc55..06b4115 100644
--- a/src/main/java/com/nq/dao/OrderEchoMapper.java
+++ b/src/main/java/com/nq/dao/OrderEchoMapper.java
@@ -2,6 +2,7 @@
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.nq.pojo.OrderEchoBean;
+import com.nq.pojo.reponse.OrderEChoReponse;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
@@ -16,5 +17,5 @@
List<OrderEchoBean> queryByUserId(Integer id);
- List<OrderEchoBean> queryAll();
+ List<OrderEChoReponse> queryAll();
}
diff --git a/src/main/java/com/nq/pojo/EChoBean.java b/src/main/java/com/nq/pojo/EChoBean.java
index 995fa5b..a46c0c9 100644
--- a/src/main/java/com/nq/pojo/EChoBean.java
+++ b/src/main/java/com/nq/pojo/EChoBean.java
@@ -3,8 +3,10 @@
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
@TableName("echo")
+@Data
public class EChoBean {
@TableId(value = "id",type = IdType.AUTO)
@@ -34,6 +36,12 @@
* */
private String title;
+ /**
+ * 最低购买数量
+ * @return
+ */
+ private Integer buyLowestNum;
+
public Integer getId() {
return id;
}
diff --git a/src/main/java/com/nq/pojo/OrderEchoBean.java b/src/main/java/com/nq/pojo/OrderEchoBean.java
index f7144ef..be52d98 100644
--- a/src/main/java/com/nq/pojo/OrderEchoBean.java
+++ b/src/main/java/com/nq/pojo/OrderEchoBean.java
@@ -1,11 +1,14 @@
package com.nq.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
import org.springframework.data.annotation.Transient;
@TableName("order_echo")
+@Data
public class OrderEchoBean {
@TableId(value = "id",type = IdType.AUTO)
@@ -27,11 +30,14 @@
private String state;
+ private String earnings;
+
+ @TableField(exist = false)
+ private String returnTime;
+
@Transient
private String title;
- @Transient
- private String returnTime;
public Integer getId() {
return id;
@@ -99,14 +105,6 @@
public void setTitle(String title) {
this.title = title;
- }
-
- public String getReturnTime() {
- return returnTime;
- }
-
- public void setReturnTime(String returnTime) {
- this.returnTime = returnTime;
}
public void setMoney(String money) {
diff --git a/src/main/java/com/nq/pojo/reponse/OrderEChoReponse.java b/src/main/java/com/nq/pojo/reponse/OrderEChoReponse.java
new file mode 100644
index 0000000..b883e10
--- /dev/null
+++ b/src/main/java/com/nq/pojo/reponse/OrderEChoReponse.java
@@ -0,0 +1,40 @@
+package com.nq.pojo.reponse;
+
+import lombok.Data;
+
+@Data
+public class OrderEChoReponse {
+
+ private Integer id;
+ private String eid;
+ /**
+ * 收益率
+ * */
+ private String returnOfRate;
+ /**
+ * 买入时间
+ * */
+ private Long buyTime;
+ /**
+ * 发放时间 结束时间
+ * */
+ private Long endTime;
+ /**
+ * 购买人名字
+ * */
+ private String userId;
+ /**
+ * 购买的金额
+ * */
+ private String money;
+ /**
+ * 结算金额
+ * */
+ private String state;
+
+ private String title;
+
+ private String phone;
+
+ private String earnings;
+}
diff --git a/src/main/java/com/nq/service/IEchoServices.java b/src/main/java/com/nq/service/IEchoServices.java
index 4e18c57..3eb5118 100644
--- a/src/main/java/com/nq/service/IEchoServices.java
+++ b/src/main/java/com/nq/service/IEchoServices.java
@@ -2,6 +2,7 @@
import com.nq.pojo.EChoBean;
import com.nq.pojo.OrderEchoBean;
+import com.nq.pojo.reponse.OrderEChoReponse;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
@@ -17,7 +18,7 @@
List<OrderEchoBean> queryOrderEcho(HttpServletRequest request);
- List<OrderEchoBean> queryALLOrderEcho();
+ List<OrderEChoReponse> queryALLOrderEcho();
diff --git a/src/main/java/com/nq/service/impl/ApplyLeverServicesimpl.java b/src/main/java/com/nq/service/impl/ApplyLeverServicesimpl.java
index 0cf6399..b95d131 100644
--- a/src/main/java/com/nq/service/impl/ApplyLeverServicesimpl.java
+++ b/src/main/java/com/nq/service/impl/ApplyLeverServicesimpl.java
@@ -33,6 +33,7 @@
QueryWrapper<ApplyLever> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("user_id",userService.getCurrentUser(request).getId());
+ queryWrapper.ne("state",2);
if(applyLeverMapper.selectCount(queryWrapper)>0){
return ServerResponse.createByErrorMsg("你已经申请杠杆了",request);
}
diff --git a/src/main/java/com/nq/service/impl/EchoServices.java b/src/main/java/com/nq/service/impl/EchoServices.java
index cfb954f..75755a8 100644
--- a/src/main/java/com/nq/service/impl/EchoServices.java
+++ b/src/main/java/com/nq/service/impl/EchoServices.java
@@ -1,12 +1,17 @@
package com.nq.service.impl;
-import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.nq.dao.EChoMapper;
import com.nq.dao.OrderEchoMapper;
+import com.nq.dao.UserAssetsMapper;
+import com.nq.enums.EStockType;
+import com.nq.enums.EUserAssets;
import com.nq.pojo.EChoBean;
import com.nq.pojo.OrderEchoBean;
import com.nq.pojo.User;
+import com.nq.pojo.UserAssets;
+import com.nq.pojo.reponse.OrderEChoReponse;
import com.nq.service.IEchoServices;
import com.nq.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -14,6 +19,7 @@
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
+import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.List;
@@ -28,6 +34,12 @@
@Autowired
IUserService iUserService;
+
+ @Autowired
+ private UserAssetsServices serAssetsServices;
+
+ @Autowired
+ private UserAssetsMapper userAssetsMapper;
@Override
public List<EChoBean> queryList() {
@@ -60,7 +72,12 @@
orderEchoBean.setMoney(money.toString());
orderEchoBean.setState("1");
- iUserService.updateAmt(user.getId(),money.toString(),1);
+ UserAssets userAssets = userAssetsMapper.selectOne(new LambdaQueryWrapper<UserAssets>()
+ .eq(UserAssets::getUserId, orderEchoBean.getUserId())
+ .eq(UserAssets::getAccectType, "IN")
+ );
+ userAssets.setAvailableBalance(userAssets.getAvailableBalance().subtract(new BigDecimal(money)));
+ userAssetsMapper.updateById(userAssets);
return orderEchoMapper.insert(orderEchoBean) >= 1;
}
}
@@ -75,24 +92,38 @@
}
@Override
- public List<OrderEchoBean> queryALLOrderEcho() {
+ public List<OrderEChoReponse> queryALLOrderEcho() {
return orderEchoMapper.queryAll();
}
@Override
public void sendMoney() {
QueryWrapper<OrderEchoBean> queryWrapper = new QueryWrapper<>();
- queryWrapper.eq("state",1);
- queryWrapper.ge("end_time",System.currentTimeMillis());
- List<OrderEchoBean> list = orderEchoMapper.selectList(queryWrapper);
- for (int i = 0; i <list.size() ; i++) {
- OrderEchoBean orderEchoBean = list.get(i);
- BigDecimal money = BigDecimal.valueOf(Long.parseLong(orderEchoBean.getMoney()));
- BigDecimal rate = BigDecimal.valueOf(Long.parseLong(orderEchoBean.getReturnOfRate()));
- String money1 =rate.divide(BigDecimal.valueOf(100)).add(BigDecimal.valueOf(1)).multiply(money).setScale(2).toString();
- iUserService.updateAmt(Integer.parseInt(orderEchoBean.getUserId()),money1,0);
- orderEchoBean.setState("2");
+ queryWrapper.eq("state", 1);
+ queryWrapper.lt("end_time", System.currentTimeMillis());
+ List<OrderEchoBean> list = orderEchoMapper.selectList(queryWrapper);
+ for (OrderEchoBean orderEchoBean : list) {
+ BigDecimal money = new BigDecimal(orderEchoBean.getMoney()); // 本金
+ BigDecimal rate = new BigDecimal(orderEchoBean.getReturnOfRate()); // 收益率
+ // 将rate除以100,并向上取整
+ rate = rate.divide(new BigDecimal(100), 2, RoundingMode.HALF_UP);
+
+ // 本金*收益率*周期/365=收益金额
+ BigDecimal incomeAmount = money.multiply(rate);
+ BigDecimal aml = incomeAmount.add(money);
+ if (aml.compareTo(BigDecimal.ZERO) > 0) {
+ UserAssets userAssets = userAssetsMapper.selectOne(new LambdaQueryWrapper<UserAssets>()
+ .eq(UserAssets::getUserId, orderEchoBean.getUserId())
+ .eq(UserAssets::getAccectType, "IN")
+ );
+ userAssets.setAvailableBalance(userAssets.getAvailableBalance().add(aml));
+ userAssetsMapper.updateById(userAssets);
+ orderEchoBean.setState("2");
+ orderEchoBean.setEarnings(incomeAmount.toString());
+ orderEchoMapper.updateById(orderEchoBean);
+ }
}
+
}
@Override
diff --git a/src/main/java/com/nq/service/impl/UserServiceImpl.java b/src/main/java/com/nq/service/impl/UserServiceImpl.java
index afdaf50..4467d0d 100644
--- a/src/main/java/com/nq/service/impl/UserServiceImpl.java
+++ b/src/main/java/com/nq/service/impl/UserServiceImpl.java
@@ -117,6 +117,8 @@
@Autowired
ISiteMessageService iSiteMessageService;
+ @Autowired
+ private ApplyLeverMapper applyLeverMapper;
@@ -284,11 +286,32 @@
String userJson = RedisShardedPoolUtils.get(loginToken);
User user = (User) JsonUtil.string2Obj(userJson, User.class);
User dbuser = this.userMapper.selectById(user.getId());
+
SiteSetting siteSetting = iSiteSettingService.getSiteSetting();
UserInfoVO userInfoVO = assembleUserInfoVO(dbuser, siteSetting);
+ ApplyLever applyLever = applyLeverMapper.selectOne(new LambdaQueryWrapper<ApplyLever>()
+ .eq(ApplyLever::getUserId, user.getId())
+ .eq(ApplyLever::getState, 1)
+ .orderByDesc(ApplyLever::getCreateTime)
+ .last(" limit 1"));
+ if(null == applyLever || applyLever.getLever().equals("1")){
+ userInfoVO.setSiteLever("1");
+ }else{
+ userInfoVO.setSiteLever(leverSplit(applyLever.getLever()));
+ }
return ServerResponse.createBySuccess(userInfoVO);
}
+ public String leverSplit(String lever){
+ String levers = "1/2/5/10";
+ String[] parts = levers.split("/");
+ int index = Arrays.asList(parts).indexOf(lever);
+ if (index != -1) {
+ return String.join("/", Arrays.copyOfRange(parts, 0, index + 1));
+ }
+ return null;
+ }
+
public ServerResponse updatePwd(String oldPwd, String newPwd, HttpServletRequest request) {
if (StringUtils.isBlank(oldPwd) || StringUtils.isBlank(newPwd)) {
diff --git a/src/main/java/com/nq/utils/task/YEBTask.java b/src/main/java/com/nq/utils/task/YEBTask.java
index cbef6d8..fd8b1b0 100644
--- a/src/main/java/com/nq/utils/task/YEBTask.java
+++ b/src/main/java/com/nq/utils/task/YEBTask.java
@@ -21,7 +21,7 @@
/**
* 余额宝发放利息
* */
-// @Scheduled(cron = "0 0 0/1 * * ?")
+ @Scheduled(cron = "0 0/1 * * * ?")
public void sendYEBMoney(){
echoServices.sendMoney();
}
diff --git a/src/main/resources/mapper/OrderEchoMapper.xml b/src/main/resources/mapper/OrderEchoMapper.xml
index 5e1484e..beedfe0 100644
--- a/src/main/resources/mapper/OrderEchoMapper.xml
+++ b/src/main/resources/mapper/OrderEchoMapper.xml
@@ -10,8 +10,8 @@
<result column="end_time" property="endTime"/>
<result column="user_id" property="userId"/>
<result column="money" property="money"/>
- <result column="return_time" property="returnTime"/>
<result column="title" property="title"/>
+ <result column="return_time" property="returnTime"/>
</resultMap>
<sql id="Base_Column_List">
@@ -30,18 +30,19 @@
<select id="queryByUserId" resultMap="BaseResultMap">
select o.id,o.eid,o.return_of_rate,
o.buy_time,o.end_time,o.user_id,o.state,o.money,e.title,e.return_time
- from order_echo o
- join echo e
- on o.eid = e.id
- where user_id = #{id}
- </select>
-
- <select id="queryAll" resultMap="BaseResultMap">
- select o.id,o.eid,o.return_of_rate,
- o.buy_time,o.end_time,o.user_id,o.state,o.money,e.title,e.return_time
from order_echo o
join echo e
on o.eid = e.id
+ where user_id = #{id}
+ </select>
+
+ <select id="queryAll" resultType="com.nq.pojo.reponse.OrderEChoReponse">
+ select o.id,o.eid,o.return_of_rate,
+ o.buy_time,o.end_time,o.user_id,o.state,o.money,o.earnings,e.title,u.phone
+ from order_echo o
+ join echo e
+ on o.eid = e.id
+ join user u on u.id = o.user_id
</select>
diff --git a/target/classes/mapper/OrderEchoMapper.xml b/target/classes/mapper/OrderEchoMapper.xml
index 5e1484e..beedfe0 100644
--- a/target/classes/mapper/OrderEchoMapper.xml
+++ b/target/classes/mapper/OrderEchoMapper.xml
@@ -10,8 +10,8 @@
<result column="end_time" property="endTime"/>
<result column="user_id" property="userId"/>
<result column="money" property="money"/>
- <result column="return_time" property="returnTime"/>
<result column="title" property="title"/>
+ <result column="return_time" property="returnTime"/>
</resultMap>
<sql id="Base_Column_List">
@@ -30,18 +30,19 @@
<select id="queryByUserId" resultMap="BaseResultMap">
select o.id,o.eid,o.return_of_rate,
o.buy_time,o.end_time,o.user_id,o.state,o.money,e.title,e.return_time
- from order_echo o
- join echo e
- on o.eid = e.id
- where user_id = #{id}
- </select>
-
- <select id="queryAll" resultMap="BaseResultMap">
- select o.id,o.eid,o.return_of_rate,
- o.buy_time,o.end_time,o.user_id,o.state,o.money,e.title,e.return_time
from order_echo o
join echo e
on o.eid = e.id
+ where user_id = #{id}
+ </select>
+
+ <select id="queryAll" resultType="com.nq.pojo.reponse.OrderEChoReponse">
+ select o.id,o.eid,o.return_of_rate,
+ o.buy_time,o.end_time,o.user_id,o.state,o.money,o.earnings,e.title,u.phone
+ from order_echo o
+ join echo e
+ on o.eid = e.id
+ join user u on u.id = o.user_id
</select>
diff --git a/target/classes/mapper/UserMapper.xml b/target/classes/mapper/UserMapper.xml
index 8efe003..5665a62 100644
--- a/target/classes/mapper/UserMapper.xml
+++ b/target/classes/mapper/UserMapper.xml
@@ -24,13 +24,15 @@
<result column="auth_msg" property="authMsg"/>
<result column="withdrawal_Pwd" property="withdrawalPwd"/>
<result column="vaild_number" property="vaildNumber"/>
+ <result column="lever" property="lever"/>
+ <result column="fund_ratio" property="fundRatio"/>
</resultMap>
<sql id="Base_Column_List">
id, agent_id, agent_name, phone, user_pwd, with_pwd, nick_name, real_name, id_card,
account_type, is_lock, is_login,
reg_time, reg_ip, reg_address, img1_key, img2_key, img3_key, is_active, auth_msg,
- withdrawal_Pwd,vaild_number
+ withdrawal_Pwd,vaild_number,lever,fund_ratio
</sql>
--
Gitblit v1.9.3