Compare commits
262 Commits
pre_202511
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 8af7ba4748 | |||
| 77afa952d7 | |||
| bdc0372f49 | |||
| db73ca67e9 | |||
| a929635bfc | |||
| b7b57d04d0 | |||
| e919a934dd | |||
| 7f0d3e9897 | |||
| d9d9efd233 | |||
| 3e3a8a32a3 | |||
| 50872500b3 | |||
| f8ca35d20c | |||
| 2ff93df44b | |||
| 1897281b38 | |||
| 43dacf4744 | |||
| 352f936318 | |||
| fc780c3513 | |||
| 657dc8b4e3 | |||
| a9532a1d2e | |||
| 144cf92c27 | |||
| 52ff9dabe0 | |||
| 9136af8a52 | |||
| d2746dc5b6 | |||
| 7c3e025d76 | |||
| 2040b57fb6 | |||
| cbc47d14e4 | |||
| 59274f9941 | |||
| 1157bf3229 | |||
| 2d50db41d0 | |||
| a23801eb31 | |||
| 01dd0687de | |||
| 1567742f84 | |||
| 0e5a378706 | |||
| f6bbf99c10 | |||
| b31aabc085 | |||
| 951a1f3cf7 | |||
| 7938cad0cc | |||
| 74ba36ebd6 | |||
| 23a3b1c659 | |||
| e8c964ddcb | |||
| 6257fb29bd | |||
| eae16d6957 | |||
| 16d19d79b2 | |||
| cf80b869fe | |||
| 8cdc88e00a | |||
| 381ede08ba | |||
| f7887e6b79 | |||
| 3b5f48fdc6 | |||
| 396181c62e | |||
| 7d2330f18f | |||
| 542ce3cf16 | |||
| 4c4f6db86c | |||
| 09154bcdb4 | |||
| 390eca8b63 | |||
| 3fbccbfd31 | |||
| dc2c26ae74 | |||
| 1637c1bf92 | |||
| b1f95f4ecf | |||
| c96983ec72 | |||
| 78dce9f77b | |||
| c1dc9994eb | |||
| 6185956410 | |||
| fe5c884427 | |||
| 8bd8c49f38 | |||
| eb8fb4a4e9 | |||
| 992f25a224 | |||
| 97ee21b5fb | |||
| 295275b3dc | |||
| f9192c74ff | |||
| a53ccaadc7 | |||
| 7a26bdec49 | |||
| 89a326a05b | |||
| def93e54fa | |||
| 48da403179 | |||
| ca48e78dd1 | |||
| 76b7832908 | |||
| 87f777ca78 | |||
| 9051b0702d | |||
| fc177025a0 | |||
| 28e5b83f6c | |||
| b1f401ff81 | |||
| 950b7b9b32 | |||
| 46b27539b5 | |||
| 7326d0505f | |||
| 1684e43663 | |||
| 6575858004 | |||
| cb0d5e0cc3 | |||
| 2ea022091c | |||
| 2eed30bdf4 | |||
| dcaf638b58 | |||
| e59fa56784 | |||
| d530054e03 | |||
| 81abf15dcd | |||
| 655998c805 | |||
| 24a9def012 | |||
| b4e2b92730 | |||
| d9bb03770a | |||
| 1d328b38f7 | |||
| a73615f350 | |||
| e7d8a126fc | |||
| 62239d4dec | |||
| 30b24dda04 | |||
| e1bd542c12 | |||
| 7e4c2a9ccf | |||
| ed29dcd419 | |||
| 857447ae8b | |||
| c0f11aa224 | |||
| 504f03e5f6 | |||
| 4e2a75520c | |||
| fb10c6e173 | |||
| 45469be52d | |||
| 71cef21472 | |||
| 75795bad0e | |||
| 200f66839b | |||
| 9085dc7681 | |||
| 5c6a4e985a | |||
| d8aadb2d47 | |||
| 3bdb5d73d1 | |||
| 06df245d59 | |||
| d4c3bce80f | |||
| d2c9b6c492 | |||
| da771d42f7 | |||
| f06a166693 | |||
| c14cdb0967 | |||
| b628fffca7 | |||
| 3802044eea | |||
| 32bd2fab91 | |||
| de19921581 | |||
| 2cb58bffa3 | |||
| 9c8f78f3cf | |||
| 9bc2d32c45 | |||
| 8f20e755fb | |||
| db5bd06c35 | |||
| 528c76a912 | |||
| 1022468778 | |||
| 9d59fbcc9c | |||
| 8eeee63f58 | |||
| 3971c5f504 | |||
| 47331a5b2a | |||
| 0b6c87aa31 | |||
| c8699f0bf2 | |||
| ac3b9216d8 | |||
| a3fa5a3fa8 | |||
| 3ca6dcbec9 | |||
| 1b97836bea | |||
| fddd1750a1 | |||
| 4ce40ede17 | |||
| 023bd790df | |||
| 8c9661c77c | |||
| 928b16da60 | |||
| 7dd3822e44 | |||
| 4259a7e454 | |||
| da449a5921 | |||
| a2e0854a7d | |||
| a047598703 | |||
| 485cad13ed | |||
| 4666d9b098 | |||
| 5977cbcf80 | |||
| a6c1b9bf95 | |||
| e79b5727b3 | |||
| 3d8a84126d | |||
| 33f83bc82d | |||
| 9cbf01f2ab | |||
| d97523231d | |||
| 171cbe9ea1 | |||
| bd8e11a663 | |||
| c0b3b74533 | |||
| bd6d44ebd9 | |||
| 8c3c81d523 | |||
| 3518b927da | |||
| 8ce6c3e79f | |||
| 5df67db07e | |||
| c8857caadf | |||
| ff7acb1b73 | |||
| c5f5978b41 | |||
| 040c6d3cb1 | |||
| 6ca0af397b | |||
| ed10a03442 | |||
| 6c479e744a | |||
| ad7f89cb42 | |||
| b04f2d095d | |||
| b06826593a | |||
| a43df002dc | |||
| 2ecfce1f2f | |||
| e9c42bdb99 | |||
| af9cce0db1 | |||
| 645ad45d08 | |||
| 41dbafffab | |||
| 5e45474807 | |||
| c3414925a0 | |||
| 615d478b4c | |||
| cb74d80aab | |||
| 0152518315 | |||
| c8a85d5119 | |||
| 31160d4806 | |||
| d59096f6d3 | |||
| 10fa6fd739 | |||
| accd09409d | |||
| eaf1a87db8 | |||
| d4a7c4b3fb | |||
| 62eb843629 | |||
| ef36db0b44 | |||
| 2d612ecc7a | |||
| 2a2ec2f9b3 | |||
| c587ad5532 | |||
| d2cdc097f8 | |||
| ee31d1f75d | |||
| 80607c8642 | |||
| ce974276a8 | |||
| da259b15e9 | |||
| d1ef71669e | |||
| 44794636f7 | |||
| 19756ee7e3 | |||
| 2e7612ac93 | |||
| 38a1ce0c66 | |||
| 415bbfd4ba | |||
| cfb8714410 | |||
| 070c1fc625 | |||
| eeb7f05706 | |||
| 2661277bd2 | |||
| db33dd255e | |||
| d2289f2d12 | |||
| 507b65838c | |||
| bb0eb917c1 | |||
| f3cf329f19 | |||
| 3287be123e | |||
| 57ccf4f599 | |||
| 3c19ee7064 | |||
| 3ee9eaee8d | |||
| 30bf1446d4 | |||
| 45a2f7a07d | |||
| 1aea5d0281 | |||
| 38b674e05a | |||
| 0c15b2c034 | |||
| 4814ca202d | |||
| 2ab4495920 | |||
| ea0afa5bdb | |||
| 3a4a8d7772 | |||
| 12fbd15a66 | |||
| d711a70e34 | |||
| b9d4d8744f | |||
| 5c6bf1bb3f | |||
| ef4db7a0bf | |||
| fa80c2fca3 | |||
| 85294f332a | |||
| 81fe3a88b8 | |||
| eb5ddd03df | |||
| ddd20b825e | |||
| f7a6cecd31 | |||
| 88e9a52071 | |||
| d3790fae0d | |||
| 2746f11442 | |||
| 7932c2cdf7 | |||
| 3b7a923109 | |||
| 98f5a160af | |||
| ee3437005b | |||
| 3d31e4cd74 | |||
| 2af8bc966a | |||
| abfefb6f57 | |||
| 6acf94acb5 | |||
| 60c4df7625 | |||
| fd88f45625 |
@ -34,16 +34,21 @@ public class DynamicTaskScheduler {
|
|||||||
this.sxDataService = sxDataService;
|
this.sxDataService = sxDataService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@PostConstruct
|
@PostConstruct
|
||||||
public void initTasks() {
|
public void initTasks() {
|
||||||
//初始化 start
|
try {
|
||||||
CommentModel commentModel =sxDataService.getCommentModel();
|
//初始化 start
|
||||||
DataBaseInfo enabledTask = sxDataService.getDataBaseInfo(commentModel);
|
CommentModel commentModel =sxDataService.getCommentModel();
|
||||||
executeTask(enabledTask.getDataBaseName(),commentModel);
|
DataBaseInfo enabledTask = sxDataService.getDataBaseInfo(commentModel);
|
||||||
//初始化 end
|
executeTask(enabledTask.getDataBaseName(),commentModel);
|
||||||
refreshTasks();
|
//初始化 end
|
||||||
// 每5分钟检查一次数据库更新
|
refreshTasks();
|
||||||
taskScheduler.scheduleAtFixedRate(this::refreshTasks, Duration.ofHours(1));
|
// 每5分钟检查一次数据库更新
|
||||||
|
taskScheduler.scheduleAtFixedRate(this::refreshTasks, Duration.ofHours(1));
|
||||||
|
}catch (Exception e){
|
||||||
|
log.info("系统异常:{}",e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void refreshTasks() {
|
public void refreshTasks() {
|
||||||
@ -96,13 +101,26 @@ public class DynamicTaskScheduler {
|
|||||||
commentModel =sxDataService.getCommentModel();
|
commentModel =sxDataService.getCommentModel();
|
||||||
}
|
}
|
||||||
DataBaseInfo dataBaseInfo=sxDataService.getDataBaseInfo(commentModel);
|
DataBaseInfo dataBaseInfo=sxDataService.getDataBaseInfo(commentModel);
|
||||||
if(ObjectUtil.isNotEmpty(dataBaseInfo.getRefreshTime())){
|
if(dataBaseInfo==null||StringUtils.isEmpty(dataBaseInfo.getUserName())){
|
||||||
commentModel.setSyncTime(DateUtil.formatDateTime(dataBaseInfo.getRefreshTime()));
|
return;
|
||||||
}
|
}
|
||||||
|
String refreshTime="";
|
||||||
|
if(ObjectUtil.isNotEmpty(dataBaseInfo.getRefreshTime())){
|
||||||
|
refreshTime=DateUtil.formatDateTime(dataBaseInfo.getRefreshTime());
|
||||||
|
commentModel.setSyncTime(refreshTime);
|
||||||
|
}
|
||||||
|
sxDataService.syncStoreData(dataBaseInfo,commentModel);//同步网上售卖流水
|
||||||
|
|
||||||
sxDataService.syncStoreData(dataBaseInfo,commentModel);
|
if(StringUtils.isNotEmpty(refreshTime)){//有刷新时间,证明不是全量,需要判断是否有新商品,有新商品才同步品牌和分类
|
||||||
sxDataService.SyncBranchList(dataBaseInfo,commentModel);
|
if(sxDataService.isNewShop(dataBaseInfo,refreshTime)){
|
||||||
sxDataService.SyncCategory(dataBaseInfo,commentModel);
|
sxDataService.SyncBranchList(dataBaseInfo,commentModel);
|
||||||
|
sxDataService.SyncCategory(dataBaseInfo,commentModel);
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
log.info("首次同步,无刷新时间");
|
||||||
|
sxDataService.SyncBranchList(dataBaseInfo,commentModel);
|
||||||
|
sxDataService.SyncCategory(dataBaseInfo,commentModel);
|
||||||
|
}
|
||||||
sxDataService.SyncGoods(dataBaseInfo,commentModel);//todo 暂时同步全部的商品如果后期修改,需要增加服务器的字段
|
sxDataService.SyncGoods(dataBaseInfo,commentModel);//todo 暂时同步全部的商品如果后期修改,需要增加服务器的字段
|
||||||
try {
|
try {
|
||||||
if(ObjectUtil.isNotEmpty(dataBaseInfo.getShopGapTime())){
|
if(ObjectUtil.isNotEmpty(dataBaseInfo.getShopGapTime())){
|
||||||
@ -114,9 +132,25 @@ public class DynamicTaskScheduler {
|
|||||||
} catch (InterruptedException e) {
|
} catch (InterruptedException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
sxDataService.SyncVipList(dataBaseInfo,commentModel);
|
if(ObjectUtil.isNotEmpty(dataBaseInfo.getRefreshTime())){
|
||||||
sxDataService.syncAtive(dataBaseInfo,commentModel);
|
commentModel.setSyncTime(refreshTime);
|
||||||
sxDataService.syncAtiveShops(dataBaseInfo,commentModel);
|
}
|
||||||
|
if("1".equals(dataBaseInfo.getIsSyncMember())){//同步会员
|
||||||
|
sxDataService.SyncVipList(dataBaseInfo,commentModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
//同步活动数据,判断是否同步活动数据,有新活动,且有活动数据才同步活动
|
||||||
|
if("1".equals(dataBaseInfo.getIsSyncActive())){
|
||||||
|
boolean isNewActives=sxDataService.syncAtive(dataBaseInfo,commentModel);
|
||||||
|
if(isNewActives){
|
||||||
|
log.info("---有新增的活动,同步活动商品开始--");
|
||||||
|
sxDataService.syncAtiveShops(dataBaseInfo,commentModel);
|
||||||
|
}else {
|
||||||
|
log.info("---无新增的活动,无需同步活动商品--");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sxDataService.refreshTime(commentModel);
|
||||||
isRuning=false;
|
isRuning=false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,7 @@ public class FileUtils {
|
|||||||
public static final String okEnd = "ok";//后缀
|
public static final String okEnd = "ok";//后缀
|
||||||
public static final String txtEnd = "txt";//后缀
|
public static final String txtEnd = "txt";//后缀
|
||||||
|
|
||||||
public static String fileFormat = "%s_%s.%s";//good_1
|
public static String fileFormat = "%s_%s_%s.%s";//good_1
|
||||||
|
|
||||||
public static String getSyncTypeFlag(String syncType){
|
public static String getSyncTypeFlag(String syncType){
|
||||||
Calendar calendar=Calendar.getInstance();
|
Calendar calendar=Calendar.getInstance();
|
||||||
@ -96,12 +96,21 @@ public class FileUtils {
|
|||||||
* @param filePath
|
* @param filePath
|
||||||
*/
|
*/
|
||||||
public void writeFile(String filePath,String fileName,String content){
|
public void writeFile(String filePath,String fileName,String content){
|
||||||
|
FileWriter writer =null;
|
||||||
try {
|
try {
|
||||||
FileWriter writer = new FileWriter(filePath+pathSeparator+fileName);
|
writer = new FileWriter(filePath+pathSeparator+fileName,false);
|
||||||
writer.write(content);
|
writer.write(content);
|
||||||
|
writer.flush();
|
||||||
writer.close();
|
writer.close();
|
||||||
log.info("文件写入成功!");
|
log.info("文件写入成功!");
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
if(writer!=null){
|
||||||
|
try {
|
||||||
|
writer.close();
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new RuntimeException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
log.info("文件写入失败:{}", e.getMessage());
|
log.info("文件写入失败:{}", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,7 +121,7 @@ public class FileUtils {
|
|||||||
* @param page
|
* @param page
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public String getFileName(String syncType ,Integer page,String endFix){
|
public String getFileName(String syncType ,Integer page,String endFix,String fileEndFix){
|
||||||
String result="";
|
String result="";
|
||||||
switch (syncType){
|
switch (syncType){
|
||||||
case "1":
|
case "1":
|
||||||
@ -130,7 +139,7 @@ public class FileUtils {
|
|||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return String.format(fileFormat, result,page,endFix);
|
return String.format(fileFormat, result,page,fileEndFix,endFix);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -170,7 +179,7 @@ public class FileUtils {
|
|||||||
FileUtils fileUtils= new FileUtils();
|
FileUtils fileUtils= new FileUtils();
|
||||||
File file=fileUtils.createFile("1",1);
|
File file=fileUtils.createFile("1",1);
|
||||||
System.out.printf("--"+file.getAbsoluteFile());
|
System.out.printf("--"+file.getAbsoluteFile());
|
||||||
fileUtils.writeFile(file.getAbsolutePath(),fileUtils.getFileName("1",2,txtEnd),"456");
|
fileUtils.writeFile(file.getAbsolutePath(),fileUtils.getFileName("1",2,txtEnd,"operate"),"456");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,6 +36,8 @@ public class HttpUtils {
|
|||||||
|
|
||||||
public static final String URL_SYNC_GET_STOR_DATA_RELEASE="/shop/sync/third/syncStoreDataRelease";//库存同步
|
public static final String URL_SYNC_GET_STOR_DATA_RELEASE="/shop/sync/third/syncStoreDataRelease";//库存同步
|
||||||
|
|
||||||
|
public static final String URL_SYNC_POST_STORE_DATA_RESPONSE="/shop/sync/third/syncStoreDataReleaseResponse";//等待服务器相应消费
|
||||||
|
|
||||||
public static final String URL_SYNC_GOODS_NOTICE_UPLOAD_TO_OSS="/shop/sync/third/uploudToCos";//通知上传文件到cos
|
public static final String URL_SYNC_GOODS_NOTICE_UPLOAD_TO_OSS="/shop/sync/third/uploudToCos";//通知上传文件到cos
|
||||||
|
|
||||||
public static final String URL_SYNC_ACTIVE="/shop/sync/third/syncAtive";//同步活动到服务器
|
public static final String URL_SYNC_ACTIVE="/shop/sync/third/syncAtive";//同步活动到服务器
|
||||||
|
|||||||
38
client/src/main/java/com/small/client/Utils/SqlBuilder.java
Normal file
38
client/src/main/java/com/small/client/Utils/SqlBuilder.java
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package com.small.client.Utils;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Iterator;
|
||||||
|
|
||||||
|
public class SqlBuilder {
|
||||||
|
/**
|
||||||
|
* 构建IN查询条件
|
||||||
|
* @param fieldName 字段名
|
||||||
|
* @param values 值集合
|
||||||
|
* @return IN条件字符串
|
||||||
|
*/
|
||||||
|
public static String buildInCondition(String fieldName, Collection<?> values) {
|
||||||
|
if (values == null || values.isEmpty()) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append(" ").append(fieldName).append(" in (");
|
||||||
|
|
||||||
|
Iterator<?> iterator = values.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
Object value = iterator.next();
|
||||||
|
if (value instanceof String) {
|
||||||
|
sb.append("'").append(value).append("'");
|
||||||
|
} else {
|
||||||
|
sb.append(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (iterator.hasNext()) {
|
||||||
|
sb.append(",");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sb.append(")");
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -75,7 +75,7 @@ public class WebController {
|
|||||||
//sxDataService.getAppSign();
|
//sxDataService.getAppSign();
|
||||||
CommentModel commentModel= sxDataService.getCommentModel();
|
CommentModel commentModel= sxDataService.getCommentModel();
|
||||||
DataBaseInfo dataBaseInfo=sxDataService.getDataBaseInfo(commentModel);
|
DataBaseInfo dataBaseInfo=sxDataService.getDataBaseInfo(commentModel);
|
||||||
if(dataBaseInfo==null){
|
if(dataBaseInfo==null||StringUtils.isEmpty(dataBaseInfo.getUserName())){
|
||||||
log.error("syncGoods dataBaseInfo is null");
|
log.error("syncGoods dataBaseInfo is null");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -186,8 +186,8 @@ public class BaseDao {
|
|||||||
int total=0;
|
int total=0;
|
||||||
Connection connection=getConnection(ip,username,password,portNumber,dataBaseName);
|
Connection connection=getConnection(ip,username,password,portNumber,dataBaseName);
|
||||||
try {
|
try {
|
||||||
String sql="select count(1) from t_pub_plan_detail_b d left join t_pub_plan_master m on " +
|
String sql="select count(1) from t_pub_plan_detail_b d left join t_pub_plan_master t on " +
|
||||||
" d.plan_no =m.plan_no %s";
|
" d.plan_no =t.plan_no %s";
|
||||||
sql=String.format(sql,where);
|
sql=String.format(sql,where);
|
||||||
log.info(sql);
|
log.info(sql);
|
||||||
PreparedStatement ps= connection.prepareStatement(sql);
|
PreparedStatement ps= connection.prepareStatement(sql);
|
||||||
@ -266,7 +266,7 @@ public class BaseDao {
|
|||||||
return total;
|
return total;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getBaseGoodsJoinTotal(String ip, String username, String password,Integer portNumber, String dataBaseName,String where){
|
public Integer getBaseGoodsJoinTotal(String ip, String username, String password,Integer portNumber, String dataBaseName,String where,String stockOperateWhere){
|
||||||
int total=0;
|
int total=0;
|
||||||
Connection connection=getConnection(ip,username,password,portNumber,dataBaseName);
|
Connection connection=getConnection(ip,username,password,portNumber,dataBaseName);
|
||||||
try {
|
try {
|
||||||
@ -278,19 +278,18 @@ public class BaseDao {
|
|||||||
" ROW_NUMBER() OVER(PARTITION BY tib.item_no ORDER BY tib.oper_date DESC) AS rn " +
|
" ROW_NUMBER() OVER(PARTITION BY tib.item_no ORDER BY tib.oper_date DESC) AS rn " +
|
||||||
" FROM t_im_branch_stock tib\n" +
|
" FROM t_im_branch_stock tib\n" +
|
||||||
") " +
|
") " +
|
||||||
"SELECT " +
|
"SELECT count(1) " +
|
||||||
" b.*, " +
|
// " b.*, " +
|
||||||
" ls.stock_qty, " +
|
// " ls.stock_qty, " +
|
||||||
" ls.oper_date " +
|
// " ls.oper_date " +
|
||||||
"FROM ( " +
|
"FROM ( " +
|
||||||
" SELECT " +
|
" SELECT " +
|
||||||
" ROW_NUMBER() OVER(ORDER BY item_clsno) AS rowId, " +
|
" ROW_NUMBER() OVER(ORDER BY item_clsno,item_no) AS rowId, " +
|
||||||
" * " +
|
" * " +
|
||||||
" FROM t_bd_item_info " +
|
" FROM t_bd_item_info shop %s" +
|
||||||
") b " +
|
") b " +
|
||||||
"LEFT JOIN LatestStock ls ON b.item_no = ls.item_no AND ls.rn = 1 " +
|
"LEFT JOIN LatestStock ls ON b.item_no = ls.item_no AND ls.rn = 1 %s";
|
||||||
" %s";
|
sql=String.format(sql,where,stockOperateWhere);
|
||||||
sql=String.format(sql,where);
|
|
||||||
log.info(sql);
|
log.info(sql);
|
||||||
PreparedStatement ps= connection.prepareStatement(sql);
|
PreparedStatement ps= connection.prepareStatement(sql);
|
||||||
ResultSet rs=ps.executeQuery();
|
ResultSet rs=ps.executeQuery();
|
||||||
@ -344,7 +343,8 @@ public class BaseDao {
|
|||||||
return resultDto;
|
return resultDto;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResultDto baseFindGoodsListJoinPage(String ip, String username, String password,Integer portNumber, String dataBaseName, int pageNo, int pageSize,String where){
|
public ResultDto baseFindGoodsListJoinPage(String ip, String username, String password,Integer portNumber, String dataBaseName, int pageNo, int pageSize,
|
||||||
|
String where,String stockOperateWhere){
|
||||||
Connection connection=getConnection(ip,username,password,portNumber,dataBaseName);
|
Connection connection=getConnection(ip,username,password,portNumber,dataBaseName);
|
||||||
int start=(pageNo-1)*pageSize+1;
|
int start=(pageNo-1)*pageSize+1;
|
||||||
int end=pageNo*pageSize;
|
int end=pageNo*pageSize;
|
||||||
@ -355,19 +355,76 @@ public class BaseDao {
|
|||||||
" tib.oper_date," +
|
" tib.oper_date," +
|
||||||
" ROW_NUMBER() OVER(PARTITION BY tib.item_no ORDER BY tib.oper_date DESC) AS rn " +
|
" ROW_NUMBER() OVER(PARTITION BY tib.item_no ORDER BY tib.oper_date DESC) AS rn " +
|
||||||
" FROM t_im_branch_stock tib " +
|
" FROM t_im_branch_stock tib " +
|
||||||
") " +
|
"), " +
|
||||||
"SELECT " +
|
"PagedData As( SELECT " +
|
||||||
|
"ROW_NUMBER() OVER (ORDER BY b.rowId) AS rowIdA, "+//-- 这里重新生成从1开始连续的行号
|
||||||
" b.*, " +
|
" b.*, " +
|
||||||
" ls.stock_qty, " +
|
" ls.stock_qty, " +
|
||||||
" ls.oper_date " +
|
" ls.oper_date " +
|
||||||
"FROM ( " +
|
"FROM ( " +
|
||||||
" SELECT " +
|
" SELECT " +
|
||||||
" ROW_NUMBER() OVER(ORDER BY item_clsno) AS rowId ," +
|
" ROW_NUMBER() OVER(ORDER BY item_clsno,item_no) AS rowId ," +
|
||||||
" * " +
|
" * " +
|
||||||
" FROM t_bd_item_info\n" +
|
" FROM t_bd_item_info shop %s" +
|
||||||
") b " +
|
") b " +
|
||||||
"LEFT JOIN LatestStock ls ON b.item_no = ls.item_no AND ls.rn = 1 " +
|
"LEFT JOIN LatestStock ls ON b.item_no = ls.item_no AND ls.rn = 1 %s) " +
|
||||||
" %s b.rowId BETWEEN %s AND %s ";
|
"SELECT * FROM PagedData where rowIdA BETWEEN %s AND %s ";
|
||||||
|
sql=String.format(sql,where,stockOperateWhere,start,end);
|
||||||
|
log.info(sql);
|
||||||
|
ResultDto resultDto=new ResultDto();
|
||||||
|
ResultSet rs=null;
|
||||||
|
try {
|
||||||
|
PreparedStatement ps= connection.prepareStatement(sql);
|
||||||
|
rs = ps.executeQuery();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
log.info("数据库查询异常方法{},异常信息{}","com.suisung.mall.shop.sixun.dao.BaseDao.baseFindListJoinPage",e.getMessage());
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
resultDto.setResultSet(rs);
|
||||||
|
resultDto.setConnection(connection);
|
||||||
|
return resultDto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 全量查询
|
||||||
|
* @param ip
|
||||||
|
* @param username
|
||||||
|
* @param password
|
||||||
|
* @param portNumber
|
||||||
|
* @param dataBaseName
|
||||||
|
* @param pageNo
|
||||||
|
* @param pageSize
|
||||||
|
* @param where
|
||||||
|
* @param stockOperateWhere
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public ResultDto baseFindAllGoodsListJoinPage(String ip, String username, String password,Integer portNumber, String dataBaseName, int pageNo, int pageSize,
|
||||||
|
String where,String stockOperateWhere){
|
||||||
|
Connection connection=getConnection(ip,username,password,portNumber,dataBaseName);
|
||||||
|
int start=(pageNo-1)*pageSize+1;
|
||||||
|
int end=pageNo*pageSize;
|
||||||
|
String sql="SELECT * " +
|
||||||
|
"FROM (" +
|
||||||
|
" SELECT " +
|
||||||
|
" ROW_NUMBER() OVER(ORDER BY shop.item_clsno, shop.item_no) AS rowIdA," +
|
||||||
|
" shop.*," +
|
||||||
|
" (\n" +
|
||||||
|
" SELECT TOP 1 stock_qty " +
|
||||||
|
" FROM t_im_branch_stock " +
|
||||||
|
" WHERE item_no = shop.item_no " +
|
||||||
|
" ORDER BY oper_date DESC " +
|
||||||
|
" ) as stock_qty,\n" +
|
||||||
|
" (\n" +
|
||||||
|
" SELECT TOP 1 oper_date " +
|
||||||
|
" FROM t_im_branch_stock " +
|
||||||
|
" WHERE item_no = shop.item_no " +
|
||||||
|
" ORDER BY oper_date DESC " +
|
||||||
|
" ) as oper_date " +
|
||||||
|
" FROM t_bd_item_info shop " +
|
||||||
|
" %s " +
|
||||||
|
") t " +
|
||||||
|
"WHERE rowIdA BETWEEN %s AND %s " +
|
||||||
|
"ORDER BY rowIdA;";
|
||||||
sql=String.format(sql,where,start,end);
|
sql=String.format(sql,where,start,end);
|
||||||
log.info(sql);
|
log.info(sql);
|
||||||
ResultDto resultDto=new ResultDto();
|
ResultDto resultDto=new ResultDto();
|
||||||
|
|||||||
@ -9,8 +9,10 @@ import com.google.gson.Gson;
|
|||||||
import com.small.client.Utils.BigDecimalFormatter;
|
import com.small.client.Utils.BigDecimalFormatter;
|
||||||
import com.small.client.Utils.CommonUtil;
|
import com.small.client.Utils.CommonUtil;
|
||||||
import com.small.client.Utils.HttpUtils;
|
import com.small.client.Utils.HttpUtils;
|
||||||
|
import com.small.client.Utils.SqlBuilder;
|
||||||
import com.small.client.dto.*;
|
import com.small.client.dto.*;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.math.NumberUtils;
|
import org.apache.commons.lang3.math.NumberUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
@ -20,6 +22,8 @@ import org.springframework.web.client.RestTemplate;
|
|||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
|
import java.sql.Date;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@ -50,6 +54,10 @@ public class SxDataDao extends BaseDao{
|
|||||||
|
|
||||||
//private final static String T_IM_BRANCH_STOCK="t_im_branch_stock";//库存表
|
//private final static String T_IM_BRANCH_STOCK="t_im_branch_stock";//库存表
|
||||||
|
|
||||||
|
private final static String T_BD_BRANCH_INFO="t_bd_branch_info";//仓库表
|
||||||
|
|
||||||
|
private final static String T_RM_CASHIER="t_rm_cashier";//小发收银账号
|
||||||
|
|
||||||
private final static String T_IM_BRANCH_STOCK="(" +
|
private final static String T_IM_BRANCH_STOCK="(" +
|
||||||
"select * from( " +
|
"select * from( " +
|
||||||
" select ROW_NUMBER() OVER( " +
|
" select ROW_NUMBER() OVER( " +
|
||||||
@ -119,9 +127,15 @@ public class SxDataDao extends BaseDao{
|
|||||||
try {
|
try {
|
||||||
while (rs.next()) {
|
while (rs.next()) {
|
||||||
sxSyncCategory=new SxSyncCategory();
|
sxSyncCategory=new SxSyncCategory();
|
||||||
sxSyncCategory.setItem_clsname(rs.getString("item_clsname"));//分类名称
|
if(StringUtils.isNotEmpty(rs.getString("item_clsname"))){
|
||||||
sxSyncCategory.setCls_parent(rs.getString("cls_parent"));//父级编码
|
sxSyncCategory.setItem_clsname(rs.getString("item_clsname").trim());//分类名称
|
||||||
sxSyncCategory.setItem_clsno(rs.getString("item_clsno"));//分类编码
|
}
|
||||||
|
if(StringUtils.isNotEmpty(rs.getString("cls_parent"))){
|
||||||
|
sxSyncCategory.setCls_parent(rs.getString("cls_parent").trim());//父级编码
|
||||||
|
}
|
||||||
|
if(StringUtils.isNotEmpty(rs.getString("item_clsno"))){
|
||||||
|
sxSyncCategory.setItem_clsno(rs.getString("item_clsno").trim());//分类编码
|
||||||
|
}
|
||||||
// System.out.printf(rs.getString("item_clsno"));//分类编码
|
// System.out.printf(rs.getString("item_clsno"));//分类编码
|
||||||
// log.info(rs.getString("item_clsname")+"\t");//分类名称
|
// log.info(rs.getString("item_clsname")+"\t");//分类名称
|
||||||
//log.info(rs.getString("cls_parent")+"\t");//父级编码
|
//log.info(rs.getString("cls_parent")+"\t");//父级编码
|
||||||
@ -170,8 +184,12 @@ public class SxDataDao extends BaseDao{
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public int getTBditemInfoJoninTotal(DataBaseInfo dataBaseInfo){
|
public int getTBditemInfoJoninTotal(DataBaseInfo dataBaseInfo){
|
||||||
|
String stockOperateWhere="";
|
||||||
|
if(StringUtils.isNotEmpty(dataBaseInfo.getStockOperateWhere())){
|
||||||
|
stockOperateWhere="where "+dataBaseInfo.getStockOperateWhere();
|
||||||
|
}
|
||||||
return getBaseGoodsJoinTotal(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName()
|
return getBaseGoodsJoinTotal(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName()
|
||||||
,dataBaseInfo.getWhere()==null?DEFALTWHERE:dataBaseInfo.getWhere());
|
,dataBaseInfo.getWhere()==null?DEFALTWHERE:dataBaseInfo.getWhere(),stockOperateWhere);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -191,9 +209,22 @@ public class SxDataDao extends BaseDao{
|
|||||||
* @param pageNo
|
* @param pageNo
|
||||||
* @param pageSize
|
* @param pageSize
|
||||||
*/
|
*/
|
||||||
public List<SxSyncGoods> findBditemInfoListPage(DataBaseInfo dataBaseInfo,int pageNo,int pageSize){
|
public List<SxSyncGoods> findBditemInfoListPage(DataBaseInfo dataBaseInfo,int pageNo,int pageSize,boolean isAll){
|
||||||
ResultDto resultDto=baseFindGoodsListJoinPage(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName()
|
String stockOperateWhere="";
|
||||||
,pageNo,pageSize,dataBaseInfo.getWhere()==null?DEFALTWHERE:dataBaseInfo.getWhere());
|
if(StringUtils.isNotEmpty(dataBaseInfo.getStockOperateWhere())){
|
||||||
|
stockOperateWhere=" where "+dataBaseInfo.getStockOperateWhere();
|
||||||
|
}
|
||||||
|
|
||||||
|
ResultDto resultDto=null;
|
||||||
|
if(!isAll){
|
||||||
|
resultDto=baseFindGoodsListJoinPage(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName()
|
||||||
|
,pageNo,pageSize,dataBaseInfo.getWhere()==null?DEFALTWHERE:dataBaseInfo.getWhere(),stockOperateWhere);
|
||||||
|
}else {
|
||||||
|
resultDto=baseFindAllGoodsListJoinPage(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName()
|
||||||
|
,pageNo,pageSize,dataBaseInfo.getWhere()==null?DEFALTWHERE:dataBaseInfo.getWhere(),stockOperateWhere);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
ResultSet rs= resultDto.getResultSet();
|
ResultSet rs= resultDto.getResultSet();
|
||||||
List<SxSyncGoods> sxSyncGoodses=new ArrayList<>();
|
List<SxSyncGoods> sxSyncGoodses=new ArrayList<>();
|
||||||
SxSyncGoods sxSyncGoods=null;
|
SxSyncGoods sxSyncGoods=null;
|
||||||
@ -214,6 +245,7 @@ public class SxDataDao extends BaseDao{
|
|||||||
}else {
|
}else {
|
||||||
sxSyncGoods.setGross_margin(new BigDecimal("0"));//毛利率
|
sxSyncGoods.setGross_margin(new BigDecimal("0"));//毛利率
|
||||||
}
|
}
|
||||||
|
sxSyncGoods.setId(rs.getLong("rowIdA"));
|
||||||
sxSyncGoods.setItem_no(rs.getString("item_no"));//货号
|
sxSyncGoods.setItem_no(rs.getString("item_no"));//货号
|
||||||
sxSyncGoods.setItem_subname(rs.getString("item_name"));//商品名称
|
sxSyncGoods.setItem_subname(rs.getString("item_name"));//商品名称
|
||||||
sxSyncGoods.setItem_subno(rs.getString("item_subno"));//商品条码
|
sxSyncGoods.setItem_subno(rs.getString("item_subno"));//商品条码
|
||||||
@ -470,17 +502,21 @@ public class SxDataDao extends BaseDao{
|
|||||||
* @param dataBaseInfo
|
* @param dataBaseInfo
|
||||||
* @param map
|
* @param map
|
||||||
*/
|
*/
|
||||||
public void updateStoreData(DataBaseInfo dataBaseInfo, Map map,List<ProductQuantityConsumptionDto> productQuantityConsumptionDtoList,
|
public void updateStoreData(DataBaseInfo dataBaseInfo, Map map,Map<String,ProductQuantityConsumptionDto> pqMap,List<ProductQuantityConsumptionDto> productQuantityConsumptionDtoList,
|
||||||
CommentModel commentModel){
|
CommentModel commentModel){
|
||||||
if(CollectionUtil.isEmpty(map)){
|
if(CollectionUtil.isEmpty(map)){
|
||||||
log.info("同步数据为空");
|
log.info("同步数据为空");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Connection conn =getConnection(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),
|
String brachNo=getLocalBranch(dataBaseInfo);//获取总店铺分仓编号
|
||||||
|
Map<String,ItemInfo> itemInfoMap= findItemInfoList(dataBaseInfo,map);//查询商品信息
|
||||||
|
Connection conn =getConnection(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),
|
||||||
dataBaseInfo.getPassword(), dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName());
|
dataBaseInfo.getPassword(), dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName());
|
||||||
try {
|
try {
|
||||||
conn.setAutoCommit(false); // 关闭自动提交,开启事务
|
conn.setAutoCommit(false); // 关闭自动提交,开启事务
|
||||||
//String sql = "update t_im_branch_stock set stock_qty= stock_qty+(?),oper_date=? where item_no=?";
|
//String sql = "update t_im_branch_stock set stock_qty= stock_qty+(?),oper_date=? where item_no=?";
|
||||||
|
List<RmSaleflow> rmSaleflowList=new ArrayList<>();
|
||||||
|
List<TRmPayflow> tRmPayflowList=new ArrayList<>();
|
||||||
String sql = "WITH TopStock AS ( " +
|
String sql = "WITH TopStock AS ( " +
|
||||||
" SELECT TOP(1) * " +
|
" SELECT TOP(1) * " +
|
||||||
" FROM t_im_branch_stock " +
|
" FROM t_im_branch_stock " +
|
||||||
@ -489,17 +525,36 @@ public class SxDataDao extends BaseDao{
|
|||||||
") " +
|
") " +
|
||||||
" UPDATE TopStock " +
|
" UPDATE TopStock " +
|
||||||
" SET stock_qty = stock_qty+(?),oper_date=?;";
|
" SET stock_qty = stock_qty+(?),oper_date=?;";
|
||||||
|
|
||||||
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
|
Timestamp timestamp = new Timestamp(System.currentTimeMillis());
|
||||||
try (PreparedStatement ps = conn.prepareStatement(sql)) {
|
try (PreparedStatement ps = conn.prepareStatement(sql)) {
|
||||||
int batchSize = 1000; // 每批处理1000条
|
int batchSize = 100; // 每批处理1000条
|
||||||
int count = 0;
|
int count = 0;
|
||||||
Set<Map.Entry> sme=map.entrySet();
|
Set<Map.Entry> sme=map.entrySet();
|
||||||
for (Map.Entry entry : sme) {
|
for (Map.Entry entry : sme) {
|
||||||
|
String key=entry.getKey().toString();
|
||||||
|
String[] splitKey = key.split("-");
|
||||||
|
if(splitKey.length!=2){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String itemNo = splitKey[1];
|
||||||
BigDecimal stock_qty= (BigDecimal) entry.getValue();
|
BigDecimal stock_qty= (BigDecimal) entry.getValue();
|
||||||
|
|
||||||
if(stock_qty.compareTo(BigDecimal.ZERO)==0){
|
if(stock_qty.compareTo(BigDecimal.ZERO)==0){
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
ps.setString(1, (String) entry.getKey());
|
ProductQuantityConsumptionDto productQuantityConsumptionDto=pqMap.get(key);
|
||||||
|
if(productQuantityConsumptionDto.getSaleAmount().compareTo(BigDecimal.ZERO)==0){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
RmSaleflow rmSaleflow = getRmSaleflow(productQuantityConsumptionDto, itemNo, stock_qty);
|
||||||
|
rmSaleflow= fixRmSaleFlow(rmSaleflow,brachNo,itemInfoMap,dataBaseInfo.getSaleAccount());
|
||||||
|
TRmPayflow trmPayflow=fixTRmPayflow(rmSaleflow);
|
||||||
|
|
||||||
|
rmSaleflowList.add(rmSaleflow);
|
||||||
|
tRmPayflowList.add(trmPayflow);
|
||||||
|
|
||||||
|
ps.setString(1, itemNo);
|
||||||
ps.setDouble(2, stock_qty.doubleValue());
|
ps.setDouble(2, stock_qty.doubleValue());
|
||||||
ps.setTimestamp(3, timestamp);
|
ps.setTimestamp(3, timestamp);
|
||||||
ps.addBatch(); // 添加至批处理
|
ps.addBatch(); // 添加至批处理
|
||||||
@ -513,6 +568,15 @@ public class SxDataDao extends BaseDao{
|
|||||||
// 执行剩余未满 batchSize 的批次
|
// 执行剩余未满 batchSize 的批次
|
||||||
int[] remainingCounts = ps.executeBatch();
|
int[] remainingCounts = ps.executeBatch();
|
||||||
|
|
||||||
|
//todo 新增流水
|
||||||
|
if(dataBaseInfo.getClientVersion().equals(DataBaseInfo.SHANG_YUN)){
|
||||||
|
updateStoreSaleFlow(conn,rmSaleflowList,tRmPayflowList);
|
||||||
|
}
|
||||||
|
if (dataBaseInfo.getClientVersion().equals(DataBaseInfo.SHANG_RUI)){
|
||||||
|
updateStoreSaleFlowShangRui(conn,rmSaleflowList,tRmPayflowList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
List<String> consumIds=productQuantityConsumptionDtoList
|
List<String> consumIds=productQuantityConsumptionDtoList
|
||||||
.stream()
|
.stream()
|
||||||
.map(ProductQuantityConsumptionDto::getConsumeId)
|
.map(ProductQuantityConsumptionDto::getConsumeId)
|
||||||
@ -527,7 +591,7 @@ public class SxDataDao extends BaseDao{
|
|||||||
if (!HttpUtils.SUCCESSCODE.equals(code)) {
|
if (!HttpUtils.SUCCESSCODE.equals(code)) {
|
||||||
throw new Exception("服务器异常");
|
throw new Exception("服务器异常");
|
||||||
}
|
}
|
||||||
log.info("剩余批次更新数: {}", Arrays.toString(remainingCounts));
|
log.info("库存更新剩余批次更新数: {}", Arrays.toString(remainingCounts));
|
||||||
conn.commit(); // 最终提交事务
|
conn.commit(); // 最终提交事务
|
||||||
log.info("批量更新完成,总记录数: {}" , count);
|
log.info("批量更新完成,总记录数: {}" , count);
|
||||||
//baseUpdateImBrancStock(dataBaseInfo);
|
//baseUpdateImBrancStock(dataBaseInfo);
|
||||||
@ -548,6 +612,518 @@ public class SxDataDao extends BaseDao{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 封装对象
|
||||||
|
* @param productQuantityConsumptionDto
|
||||||
|
* @param itemNo
|
||||||
|
* @param stock_qty
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private RmSaleflow getRmSaleflow(ProductQuantityConsumptionDto productQuantityConsumptionDto, String itemNo, BigDecimal stock_qty) {
|
||||||
|
BigDecimal unitPrice = productQuantityConsumptionDto.getUnitPrice();
|
||||||
|
RmSaleflow rmSaleflow=new RmSaleflow();
|
||||||
|
rmSaleflow.setItemNo(itemNo);
|
||||||
|
rmSaleflow.setSalePrice(unitPrice);
|
||||||
|
rmSaleflow.setSaleQnty(stock_qty);
|
||||||
|
BigDecimal saleMoney = unitPrice.multiply(stock_qty).setScale(2, RoundingMode.HALF_UP);
|
||||||
|
if(saleMoney.compareTo(BigDecimal.ZERO)<0){
|
||||||
|
saleMoney=saleMoney.multiply(new BigDecimal(-1));
|
||||||
|
}
|
||||||
|
rmSaleflow.setSaleMoney(saleMoney);
|
||||||
|
// rmSaleflow.setSourcePrice(unitPrice);
|
||||||
|
rmSaleflow.setFlownoRand(productQuantityConsumptionDto.getOrderId());//随机子单号 设置网上订单号
|
||||||
|
if(null!=productQuantityConsumptionDto.getSaleTime()){
|
||||||
|
rmSaleflow.setOperDate(new Timestamp(productQuantityConsumptionDto.getSaleTime()));
|
||||||
|
}else {
|
||||||
|
rmSaleflow.setOperDate(new Timestamp(System.currentTimeMillis()));
|
||||||
|
}
|
||||||
|
return rmSaleflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 补充字段
|
||||||
|
* @param rmSaleflow
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public RmSaleflow fixRmSaleFlow(RmSaleflow rmSaleflow,String branchNo,Map<String,ItemInfo> itemInfoMap,String saleAccount){
|
||||||
|
//自定义单号 start
|
||||||
|
rmSaleflow.setFlowId(1);
|
||||||
|
String orderId=rmSaleflow.getFlownoRand();
|
||||||
|
String[] orderIdSplits= orderId.split("_");
|
||||||
|
//String DateStr=DateUtil.format(new java.util.Date(),"yyyyMMdd");
|
||||||
|
String DateStr=orderIdSplits[1];
|
||||||
|
String number= String.format("%04d", Integer.valueOf(orderIdSplits[orderIdSplits.length-1]));
|
||||||
|
String flowNo="99"+DateStr+number;
|
||||||
|
rmSaleflow.setFlowNo(flowNo);//订单号 todo
|
||||||
|
//自定义 单号 end
|
||||||
|
|
||||||
|
//判断取值 start
|
||||||
|
rmSaleflow.setSellWay("B");
|
||||||
|
rmSaleflow.setRetQnty(BigDecimal.ZERO);
|
||||||
|
if(rmSaleflow.getSaleQnty().compareTo(BigDecimal.ZERO)<0){
|
||||||
|
rmSaleflow.setSellWay("A");
|
||||||
|
BigDecimal saleQnty=rmSaleflow.getSaleQnty();//销售数量
|
||||||
|
rmSaleflow.setSaleQnty(saleQnty);
|
||||||
|
rmSaleflow.setRetQnty(BigDecimal.ZERO);
|
||||||
|
}
|
||||||
|
// BigDecimal saleMoney=rmSaleflow.getSalePrice().multiply(rmSaleflow.getSaleQnty());
|
||||||
|
// if(rmSaleflow.getSaleQnty().compareTo(BigDecimal.ZERO)<0){
|
||||||
|
// saleMoney=saleMoney.multiply(new BigDecimal("-1"));
|
||||||
|
// }
|
||||||
|
// rmSaleflow.setSaleMoney(saleMoney);
|
||||||
|
//判断取值 end
|
||||||
|
|
||||||
|
//数据库查询取值start todo
|
||||||
|
ItemInfo itemInfo=itemInfoMap.get(rmSaleflow.getItemNo());
|
||||||
|
if(itemInfo!=null){
|
||||||
|
rmSaleflow.setInPrice(itemInfo.getPrice());
|
||||||
|
rmSaleflow.setSourcePrice(itemInfo.getSalePrice());
|
||||||
|
}
|
||||||
|
rmSaleflow.setBranchNo(branchNo);
|
||||||
|
|
||||||
|
//数据库查询取值end
|
||||||
|
|
||||||
|
//远程配置取值start todo
|
||||||
|
rmSaleflow.setOperId(saleAccount);
|
||||||
|
rmSaleflow.setSaleMan(saleAccount);
|
||||||
|
rmSaleflow.setCounterNo(saleAccount);
|
||||||
|
//远程配置取值end
|
||||||
|
|
||||||
|
//统一默认值 start
|
||||||
|
rmSaleflow.setNStan(BigDecimal.ZERO);
|
||||||
|
rmSaleflow.setChrStan("0");
|
||||||
|
rmSaleflow.setFlownoType("0");
|
||||||
|
rmSaleflow.setPrefAmt(BigDecimal.ZERO);
|
||||||
|
rmSaleflow.setComFlag("0");
|
||||||
|
rmSaleflow.setRemoteFlag("0");
|
||||||
|
rmSaleflow.setPaysum_flag("0");
|
||||||
|
rmSaleflow.setKz_flag("1");
|
||||||
|
//统一默认值 end
|
||||||
|
return rmSaleflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付流水赋值
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public TRmPayflow fixTRmPayflow(RmSaleflow rmSaleflow){
|
||||||
|
TRmPayflow rmPayflow=new TRmPayflow();
|
||||||
|
rmPayflow.setFlowId(rmSaleflow.getFlowId());
|
||||||
|
rmPayflow.setFlowNo(rmSaleflow.getFlowNo());
|
||||||
|
// BigDecimal saleAmount=rmSaleflow.getSalePrice().multiply(rmSaleflow.getSaleQnty()).setScale(2, RoundingMode.HALF_UP);
|
||||||
|
// if(saleAmount.compareTo(BigDecimal.ZERO)<0){
|
||||||
|
// saleAmount=saleAmount.multiply(new BigDecimal("-1"));
|
||||||
|
// }
|
||||||
|
// rmPayflow.setSaleAmount(saleAmount);
|
||||||
|
rmPayflow.setSaleAmount(rmSaleflow.getSaleMoney());
|
||||||
|
rmPayflow.setBranchNo(rmSaleflow.getBranchNo());
|
||||||
|
rmPayflow.setPayWay("RMB");//人民币
|
||||||
|
rmPayflow.setSellWay(rmSaleflow.getSellWay());
|
||||||
|
rmPayflow.setCardNo("");
|
||||||
|
rmPayflow.setVipNo(null);
|
||||||
|
rmPayflow.setCoinNo("RMB");//
|
||||||
|
rmPayflow.setCoinRate(new BigDecimal("1"));//利率
|
||||||
|
rmPayflow.setPayAmount(rmSaleflow.getSaleMoney());
|
||||||
|
rmPayflow.setOperDate(rmSaleflow.getOperDate());
|
||||||
|
rmPayflow.setOperId(rmSaleflow.getOperId());
|
||||||
|
rmPayflow.setCounterNo(rmSaleflow.getCounterNo());
|
||||||
|
rmPayflow.setSaleMan(rmSaleflow.getSaleMan());
|
||||||
|
rmPayflow.setMemo("");
|
||||||
|
rmPayflow.setVoucherNo("");//退货单号 退货时要填
|
||||||
|
rmPayflow.setRemoteFlag("0");
|
||||||
|
rmPayflow.setExchangeFlag("0");
|
||||||
|
rmPayflow.setShiftNo("");
|
||||||
|
rmPayflow.setComFlag("0");
|
||||||
|
rmPayflow.setPosid("01");
|
||||||
|
rmPayflow.setUptime(null);
|
||||||
|
rmPayflow.setFlownoRand(rmSaleflow.getFlownoRand());
|
||||||
|
rmPayflow.setCiceroniNo("0");
|
||||||
|
rmPayflow.setCiceroniId(null);
|
||||||
|
rmPayflow.setFlownoType(rmSaleflow.getFlownoType());
|
||||||
|
rmPayflow.setTransType(null);
|
||||||
|
rmPayflow.setUpBatchno("");//todo
|
||||||
|
rmPayflow.setUpVipacc("0");
|
||||||
|
rmPayflow.setCashNo("00");
|
||||||
|
if("B".equals(rmSaleflow.getSellWay())){//退货
|
||||||
|
rmPayflow.setMemo("正常退货");
|
||||||
|
rmPayflow.setVoucherNo("");//退货单号 退货时要填
|
||||||
|
rmPayflow.setRemoteFlag("");
|
||||||
|
rmPayflow.setExchangeFlag("0");
|
||||||
|
rmPayflow.setUpVipacc(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return rmPayflow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询商品,返回map
|
||||||
|
* @param dataBaseInfo
|
||||||
|
* @param map
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public Map<String,ItemInfo> findItemInfoList(DataBaseInfo dataBaseInfo,Map map){
|
||||||
|
if(CollectionUtil.isEmpty(map)){
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Map<String,ItemInfo> resultMap=new HashMap<>();
|
||||||
|
Set<Map.Entry> sme=map.entrySet();
|
||||||
|
String where = "where 1=1 and ";
|
||||||
|
List<String> itemNos = sme.stream()
|
||||||
|
.map(entry -> {
|
||||||
|
String key= (String) entry.getKey();
|
||||||
|
String[] split = key.split("-");
|
||||||
|
return split[1];
|
||||||
|
})
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
where += SqlBuilder.buildInCondition("item_no", itemNos);
|
||||||
|
ResultDto resultDto= this.baseFindList(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),
|
||||||
|
dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName(),T_BD_ITEM_INFO,where);
|
||||||
|
ResultSet rs= resultDto.getResultSet();
|
||||||
|
try {
|
||||||
|
while (rs.next()) {
|
||||||
|
ItemInfo itemInfo=new ItemInfo();
|
||||||
|
String itemNo=rs.getString("item_no").trim();
|
||||||
|
itemInfo.setItemNo(itemNo);//
|
||||||
|
if(StringUtils.isNotEmpty(rs.getString("item_subno"))){
|
||||||
|
itemInfo.setItemSubno(rs.getString("item_subno").trim());
|
||||||
|
}
|
||||||
|
if(StringUtils.isNotEmpty(rs.getString("item_name"))){
|
||||||
|
itemInfo.setItemName(rs.getString("item_name").trim());
|
||||||
|
}
|
||||||
|
if(StringUtils.isNotEmpty(rs.getString("item_brand"))){
|
||||||
|
itemInfo.setItemBrand(rs.getString("item_brand").trim());//品牌
|
||||||
|
}
|
||||||
|
if(StringUtils.isNotEmpty(rs.getString("price"))){
|
||||||
|
itemInfo.setPrice(rs.getBigDecimal("price"));//进货价
|
||||||
|
}
|
||||||
|
if(StringUtils.isNotEmpty(rs.getString("item_clsno"))){
|
||||||
|
itemInfo.setItemClsno(rs.getString("item_clsno").trim());
|
||||||
|
}
|
||||||
|
if(StringUtils.isNotEmpty(rs.getString("sale_price"))){
|
||||||
|
itemInfo.setSalePrice(rs.getBigDecimal("sale_price"));
|
||||||
|
}
|
||||||
|
resultMap.put(itemNo,itemInfo);
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
resultDto.getConnection().close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量新增销售流水 商云10
|
||||||
|
* @param conn
|
||||||
|
* @param rmSaleflowList
|
||||||
|
*/
|
||||||
|
public void updateStoreSaleFlow(Connection conn,List<RmSaleflow> rmSaleflowList,List<TRmPayflow> payflowList){
|
||||||
|
if(CollectionUtil.isEmpty(rmSaleflowList)){
|
||||||
|
log.info("销售流水记录为空");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Connection conn =getConnection(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),
|
||||||
|
// dataBaseInfo.getPassword(), dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName());
|
||||||
|
//try {
|
||||||
|
//conn.setAutoCommit(false); // 关闭自动提交,开启事务
|
||||||
|
String sql = "INSERT INTO t_rm_saleflow (" +
|
||||||
|
"flow_id, flow_no, branch_no, item_no, source_price, sale_price, " +
|
||||||
|
"sale_qnty, sale_money, sell_way, oper_id, sale_man, counter_no, " +
|
||||||
|
"oper_date, remote_flag, shift_no, com_flag, spec_flag, pref_amt, " +
|
||||||
|
"in_price, n_stan, chr_stan, posid, uptime, flowno_rand, ret_qnty, " +
|
||||||
|
"flowno_type, spec_sheet_no, cic_sheet_no" +
|
||||||
|
") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||||
|
String sql2 = "INSERT INTO t_rm_payflow (" +
|
||||||
|
"flow_id, flow_no, sale_amount, branch_no, pay_way, sell_way, " +
|
||||||
|
"card_no, vip_no, coin_no, coin_rate, pay_amount, oper_date, " +
|
||||||
|
"oper_id, counter_no, sale_man, memo,remote_flag, " +
|
||||||
|
"exchange_flag, shift_no, com_flag, uptime, flowno_rand, " +
|
||||||
|
"ciceroni_no, ciceroni_id, flowno_type, trans_type, up_batchno, " +
|
||||||
|
"remark, cash_no" +
|
||||||
|
") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||||
|
try {
|
||||||
|
PreparedStatement ps = conn.prepareStatement(sql);//销售流水
|
||||||
|
PreparedStatement ps2 = conn.prepareStatement(sql2);//资金流水
|
||||||
|
int batchSize = 10; // 每批处理10条
|
||||||
|
int count = 0;
|
||||||
|
for (int i=0;i<rmSaleflowList.size();i++) {
|
||||||
|
//rmSaleflow start-------------
|
||||||
|
RmSaleflow rmSaleflow=rmSaleflowList.get(i);
|
||||||
|
ps.setInt(1,rmSaleflow.getFlowId());
|
||||||
|
ps.setString(2, rmSaleflow.getFlowNo());
|
||||||
|
ps.setString(3, rmSaleflow.getBranchNo());
|
||||||
|
ps.setString(4, rmSaleflow.getItemNo());
|
||||||
|
ps.setBigDecimal(5, rmSaleflow.getSourcePrice());
|
||||||
|
ps.setBigDecimal(6, rmSaleflow.getSalePrice());
|
||||||
|
|
||||||
|
BigDecimal saleQnty=rmSaleflow.getSaleQnty();
|
||||||
|
if(saleQnty.compareTo(BigDecimal.ZERO)<0){
|
||||||
|
saleQnty=saleQnty.multiply(new BigDecimal("-1"));
|
||||||
|
}
|
||||||
|
ps.setBigDecimal(7,saleQnty);
|
||||||
|
|
||||||
|
ps.setBigDecimal(8, rmSaleflow.getSaleMoney());
|
||||||
|
ps.setString(9, rmSaleflow.getSellWay());
|
||||||
|
ps.setString(10, rmSaleflow.getOperId());
|
||||||
|
ps.setString(11, rmSaleflow.getSaleMan());
|
||||||
|
ps.setString(12, rmSaleflow.getCounterNo());
|
||||||
|
|
||||||
|
ps.setTimestamp(13, rmSaleflow.getOperDate());//OperDate
|
||||||
|
ps.setString(14, rmSaleflow.getRemoteFlag());
|
||||||
|
ps.setString(15, "");//ShiftNo
|
||||||
|
ps.setString(16, rmSaleflow.getComFlag());
|
||||||
|
ps.setString(17, "");//SpecFlag
|
||||||
|
ps.setBigDecimal(18, rmSaleflow.getPrefAmt());
|
||||||
|
|
||||||
|
ps.setBigDecimal(19, rmSaleflow.getInPrice());
|
||||||
|
ps.setBigDecimal(20, rmSaleflow.getNStan());
|
||||||
|
ps.setString(21, rmSaleflow.getChrStan());
|
||||||
|
ps.setString(22, rmSaleflow.getPosid());
|
||||||
|
ps.setTimestamp(23, null);//Uptime
|
||||||
|
ps.setString(24, rmSaleflow.getFlownoRand());
|
||||||
|
ps.setBigDecimal(25, rmSaleflow.getRetQnty());
|
||||||
|
|
||||||
|
ps.setString(26, rmSaleflow.getFlownoType());
|
||||||
|
ps.setString(27, rmSaleflow.getSpecSheetNo());
|
||||||
|
ps.setString(28, "");//CicSheetNo
|
||||||
|
// ps.setString(29, rmSaleflow.getShareCardid());
|
||||||
|
// ps.setString(30, rmSaleflow.getItemNoFresh());
|
||||||
|
|
||||||
|
ps.addBatch(); // 添加至批处理
|
||||||
|
//rmSaleflow end-------------
|
||||||
|
|
||||||
|
//rmPayflow start-------------
|
||||||
|
TRmPayflow rmPayflow=payflowList.get(i);
|
||||||
|
|
||||||
|
//rmPayflow end-------------
|
||||||
|
ps2.setInt(1,rmPayflow.getFlowId());
|
||||||
|
ps2.setString(2, rmPayflow.getFlowNo());
|
||||||
|
ps2.setBigDecimal(3, rmPayflow.getSaleAmount());
|
||||||
|
ps2.setString(4, rmPayflow.getBranchNo());
|
||||||
|
ps2.setString(5, rmPayflow.getPayWay());
|
||||||
|
ps2.setString(6, rmPayflow.getSellWay());
|
||||||
|
|
||||||
|
ps2.setString(7, rmPayflow.getCardNo());
|
||||||
|
ps2.setString(8, rmPayflow.getVipNo());
|
||||||
|
ps2.setString(9, rmPayflow.getCoinNo());
|
||||||
|
ps2.setBigDecimal(10, rmPayflow.getCoinRate());
|
||||||
|
ps2.setBigDecimal(11, rmPayflow.getPayAmount());
|
||||||
|
ps2.setTimestamp(12, rmPayflow.getOperDate());
|
||||||
|
|
||||||
|
ps2.setString(13, rmPayflow.getOperId());//OperDate
|
||||||
|
ps2.setString(14, rmPayflow.getCounterNo());
|
||||||
|
ps2.setString(15, rmPayflow.getSaleMan());
|
||||||
|
ps2.setString(16, rmPayflow.getMemo());
|
||||||
|
// ps2.setString(17, rmPayflow.getVoucherNo());
|
||||||
|
ps2.setString(17, rmPayflow.getRemoteFlag());
|
||||||
|
|
||||||
|
ps2.setString(18, rmPayflow.getExchangeFlag());
|
||||||
|
ps2.setString(19, rmPayflow.getShiftNo());
|
||||||
|
ps2.setString(20, rmPayflow.getComFlag());
|
||||||
|
//ps2.setString(22, rmPayflow.getPosid());
|
||||||
|
ps2.setTimestamp(21, null);//Uptime
|
||||||
|
ps2.setString(22, rmPayflow.getFlownoRand());
|
||||||
|
|
||||||
|
ps2.setString(23, rmPayflow.getCiceroniNo());
|
||||||
|
ps2.setString(24, rmPayflow.getCiceroniId());
|
||||||
|
ps2.setString(25, rmPayflow.getFlownoType());
|
||||||
|
ps2.setString(26, rmPayflow.getTransType());
|
||||||
|
ps2.setString(27, rmPayflow.getUpBatchno());
|
||||||
|
|
||||||
|
|
||||||
|
// ps2.setString(30, rmPayflow.getUpVipacc());
|
||||||
|
ps2.setString(28, rmPayflow.getRemark());
|
||||||
|
ps2.setString(29, rmPayflow.getCashNo());
|
||||||
|
// ps2.setString(33, rmPayflow.getCheckData());
|
||||||
|
|
||||||
|
ps2.addBatch(); // 添加至批处理
|
||||||
|
count++;
|
||||||
|
if (count % batchSize == 0) {
|
||||||
|
ps.executeBatch();
|
||||||
|
ps2.executeBatch();
|
||||||
|
|
||||||
|
ps.clearBatch();
|
||||||
|
ps2.clearBatch();
|
||||||
|
log.info("已提交批次: {}", count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 执行剩余未满 batchSize 的批次
|
||||||
|
int[] remainingCounts = ps.executeBatch();
|
||||||
|
int[] remainingCounts2 = ps2.executeBatch();
|
||||||
|
log.info("销售流水记录剩余批次更新数: {}", Arrays.toString(remainingCounts));
|
||||||
|
log.info("支付流水记录剩余批次更新数: {}", Arrays.toString(remainingCounts2));
|
||||||
|
//conn.commit(); // 最终提交事务
|
||||||
|
log.info("销售-支付-流水记录批量更新完成,总记录数: {}" , count);
|
||||||
|
//baseUpdateImBrancStock(dataBaseInfo);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// conn.rollback(); // 出错时回滚整个事务
|
||||||
|
log.info("销售支付流水记录业务失败:: {}", e.getMessage());
|
||||||
|
throw new RuntimeException("销售支付流水记录业务失败:: {}"+e.getMessage());
|
||||||
|
}
|
||||||
|
// } catch (SQLException e) {
|
||||||
|
// log.info("sql失败:: {}", e.getMessage());
|
||||||
|
// throw new RuntimeException("sql失败:: {}"+e.getMessage());
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量新增销售流水 商瑞9.7
|
||||||
|
* @param conn
|
||||||
|
* @param rmSaleflowList
|
||||||
|
*/
|
||||||
|
public void updateStoreSaleFlowShangRui(Connection conn,List<RmSaleflow> rmSaleflowList,List<TRmPayflow> payflowList){
|
||||||
|
if(CollectionUtil.isEmpty(rmSaleflowList)){
|
||||||
|
log.info("销售流水记录为空");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Connection conn =getConnection(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),
|
||||||
|
// dataBaseInfo.getPassword(), dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName());
|
||||||
|
//try {
|
||||||
|
//conn.setAutoCommit(false); // 关闭自动提交,开启事务
|
||||||
|
String sql = "INSERT INTO t_rm_saleflow (" +
|
||||||
|
"flow_id, flow_no, branch_no, item_no, source_price, sale_price, " +
|
||||||
|
"sale_qnty, sale_money, sell_way, oper_id, sale_man, counter_no, " +
|
||||||
|
"oper_date, remote_flag, shift_no, com_flag, spec_flag, pref_amt, " +
|
||||||
|
"in_price, n_stan, chr_stan, posid, uptime, " +
|
||||||
|
"paysum_flag, spec_sheet_no,kz_flag, update_date" +
|
||||||
|
") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?,?)";
|
||||||
|
String sql2 = "INSERT INTO t_rm_payflow (" +
|
||||||
|
"flow_id, flow_no, sale_amount, branch_no, pay_way, sell_way, " +
|
||||||
|
"card_no, vip_no, coin_no, coin_rate, pay_amount, oper_date, " +
|
||||||
|
"oper_id, counter_no, sale_man, memo,remote_flag, " +
|
||||||
|
"exchange_flag, shift_no, com_flag, uptime, " +
|
||||||
|
"kz_no" +
|
||||||
|
") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||||
|
try {
|
||||||
|
PreparedStatement ps = conn.prepareStatement(sql);//销售流水
|
||||||
|
PreparedStatement ps2 = conn.prepareStatement(sql2);//资金流水
|
||||||
|
int batchSize = 10; // 每批处理10条
|
||||||
|
int count = 0;
|
||||||
|
for (int i=0;i<rmSaleflowList.size();i++) {
|
||||||
|
//rmSaleflow start-------------
|
||||||
|
RmSaleflow rmSaleflow=rmSaleflowList.get(i);
|
||||||
|
ps.setInt(1,rmSaleflow.getFlowId());
|
||||||
|
ps.setString(2, rmSaleflow.getFlowNo());
|
||||||
|
ps.setString(3, rmSaleflow.getBranchNo());
|
||||||
|
ps.setString(4, rmSaleflow.getItemNo());
|
||||||
|
ps.setBigDecimal(5, rmSaleflow.getSourcePrice());
|
||||||
|
ps.setBigDecimal(6, rmSaleflow.getSalePrice());
|
||||||
|
|
||||||
|
BigDecimal saleQnty=rmSaleflow.getSaleQnty();
|
||||||
|
if(saleQnty.compareTo(BigDecimal.ZERO)<0){
|
||||||
|
saleQnty=saleQnty.multiply(new BigDecimal("-1"));
|
||||||
|
}
|
||||||
|
ps.setBigDecimal(7,saleQnty);
|
||||||
|
|
||||||
|
ps.setBigDecimal(8, rmSaleflow.getSaleMoney());
|
||||||
|
ps.setString(9, rmSaleflow.getSellWay());
|
||||||
|
ps.setString(10, rmSaleflow.getOperId());
|
||||||
|
ps.setString(11, rmSaleflow.getSaleMan());
|
||||||
|
ps.setString(12, rmSaleflow.getCounterNo());
|
||||||
|
|
||||||
|
ps.setTimestamp(13, rmSaleflow.getOperDate());//OperDate
|
||||||
|
ps.setString(14, rmSaleflow.getRemoteFlag());
|
||||||
|
ps.setString(15, "");//ShiftNo
|
||||||
|
ps.setString(16, rmSaleflow.getComFlag());
|
||||||
|
ps.setString(17, "");//SpecFlag
|
||||||
|
ps.setBigDecimal(18, rmSaleflow.getPrefAmt());
|
||||||
|
|
||||||
|
ps.setBigDecimal(19, rmSaleflow.getInPrice());
|
||||||
|
ps.setBigDecimal(20, rmSaleflow.getNStan());
|
||||||
|
ps.setString(21, rmSaleflow.getChrStan());
|
||||||
|
ps.setString(22, rmSaleflow.getPosid());
|
||||||
|
ps.setTimestamp(23, null);//Uptime
|
||||||
|
// ps.setString(24, rmSaleflow.getFlownoRand());
|
||||||
|
// ps.setBigDecimal(25, rmSaleflow.getRetQnty());
|
||||||
|
|
||||||
|
ps.setString(24, rmSaleflow.getPaysum_flag());//paysum_flag
|
||||||
|
ps.setString(25, rmSaleflow.getSpecSheetNo());
|
||||||
|
ps.setString(26, rmSaleflow.getKz_flag());//kz_flag
|
||||||
|
ps.setTimestamp(27, rmSaleflow.getOperDate());//update_date
|
||||||
|
// ps.setString(29, rmSaleflow.getShareCardid());
|
||||||
|
// ps.setString(30, rmSaleflow.getItemNoFresh());
|
||||||
|
|
||||||
|
ps.addBatch(); // 添加至批处理
|
||||||
|
//rmSaleflow end-------------
|
||||||
|
|
||||||
|
//rmPayflow start-------------
|
||||||
|
TRmPayflow rmPayflow=payflowList.get(i);
|
||||||
|
|
||||||
|
//rmPayflow end-------------
|
||||||
|
ps2.setInt(1,rmPayflow.getFlowId());
|
||||||
|
ps2.setString(2, rmPayflow.getFlowNo());
|
||||||
|
ps2.setBigDecimal(3, rmPayflow.getSaleAmount());
|
||||||
|
ps2.setString(4, rmPayflow.getBranchNo());
|
||||||
|
ps2.setString(5, rmPayflow.getPayWay());
|
||||||
|
ps2.setString(6, rmPayflow.getSellWay());
|
||||||
|
|
||||||
|
ps2.setString(7, rmPayflow.getCardNo());
|
||||||
|
ps2.setString(8, rmPayflow.getVipNo());
|
||||||
|
ps2.setString(9, rmPayflow.getCoinNo());
|
||||||
|
ps2.setBigDecimal(10, rmPayflow.getCoinRate());
|
||||||
|
ps2.setBigDecimal(11, rmPayflow.getPayAmount());
|
||||||
|
ps2.setTimestamp(12, rmPayflow.getOperDate());
|
||||||
|
|
||||||
|
ps2.setString(13, rmPayflow.getOperId());//OperDate
|
||||||
|
ps2.setString(14, rmPayflow.getCounterNo());
|
||||||
|
ps2.setString(15, rmPayflow.getSaleMan());
|
||||||
|
ps2.setString(16, rmPayflow.getMemo());
|
||||||
|
// ps2.setString(17, rmPayflow.getVoucherNo());
|
||||||
|
ps2.setString(17, rmPayflow.getRemoteFlag());
|
||||||
|
|
||||||
|
ps2.setString(18, rmPayflow.getExchangeFlag());
|
||||||
|
ps2.setString(19, rmPayflow.getShiftNo());
|
||||||
|
ps2.setString(20, rmPayflow.getComFlag());
|
||||||
|
//ps2.setString(22, rmPayflow.getPosid());
|
||||||
|
ps2.setTimestamp(21, null);//Uptime
|
||||||
|
// ps2.setString(22, rmPayflow.getFlownoRand());
|
||||||
|
|
||||||
|
// ps2.setString(22, rmPayflow.getCiceroniNo());
|
||||||
|
// ps2.setString(23, rmPayflow.getCiceroniId());
|
||||||
|
// ps2.setString(24, rmPayflow.getFlownoType());
|
||||||
|
// ps2.setString(25, rmPayflow.getTransType());
|
||||||
|
// ps2.setString(26, rmPayflow.getUpBatchno());
|
||||||
|
|
||||||
|
|
||||||
|
// ps2.setString(30, rmPayflow.getUpVipacc());
|
||||||
|
ps2.setString(22, rmPayflow.getKz_no());
|
||||||
|
// ps2.setString(29, rmPayflow.getCashNo());
|
||||||
|
// ps2.setString(33, rmPayflow.getCheckData());
|
||||||
|
|
||||||
|
ps2.addBatch(); // 添加至批处理
|
||||||
|
count++;
|
||||||
|
if (count % batchSize == 0) {
|
||||||
|
ps.executeBatch();
|
||||||
|
ps2.executeBatch();
|
||||||
|
|
||||||
|
ps.clearBatch();
|
||||||
|
ps2.clearBatch();
|
||||||
|
log.info("已提交批次: {}", count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 执行剩余未满 batchSize 的批次
|
||||||
|
int[] remainingCounts = ps.executeBatch();
|
||||||
|
int[] remainingCounts2 = ps2.executeBatch();
|
||||||
|
log.info("销售流水记录剩余批次更新数: {}", Arrays.toString(remainingCounts));
|
||||||
|
log.info("支付流水记录剩余批次更新数: {}", Arrays.toString(remainingCounts2));
|
||||||
|
//conn.commit(); // 最终提交事务
|
||||||
|
log.info("销售-支付-流水记录批量更新完成,总记录数: {}" , count);
|
||||||
|
//baseUpdateImBrancStock(dataBaseInfo);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// conn.rollback(); // 出错时回滚整个事务
|
||||||
|
log.info("销售支付流水记录业务失败:: {}", e.getMessage());
|
||||||
|
throw new RuntimeException("销售支付流水记录业务失败:: {}"+e.getMessage());
|
||||||
|
}
|
||||||
|
// } catch (SQLException e) {
|
||||||
|
// log.info("sql失败:: {}", e.getMessage());
|
||||||
|
// throw new RuntimeException("sql失败:: {}"+e.getMessage());
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*获取促销活动价格 时段特价单
|
*获取促销活动价格 时段特价单
|
||||||
* @param dataBaseInfo
|
* @param dataBaseInfo
|
||||||
@ -775,7 +1351,7 @@ public class SxDataDao extends BaseDao{
|
|||||||
,dataBaseInfo.getPassword()
|
,dataBaseInfo.getPassword()
|
||||||
,dataBaseInfo.getDbPort()
|
,dataBaseInfo.getDbPort()
|
||||||
,dataBaseInfo.getDataBaseName()
|
,dataBaseInfo.getDataBaseName()
|
||||||
,T_PUB_PLAN_MASTER
|
,T_PUB_PLAN_MASTER +" b"
|
||||||
,PLAN_NO
|
,PLAN_NO
|
||||||
,pageNo
|
,pageNo
|
||||||
,pageSize
|
,pageSize
|
||||||
@ -895,4 +1471,54 @@ public class SxDataDao extends BaseDao{
|
|||||||
return getTotalSpecShop(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),
|
return getTotalSpecShop(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),dataBaseInfo.getDbPort(),
|
||||||
dataBaseInfo.getDataBaseName(),where);
|
dataBaseInfo.getDataBaseName(),where);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本地仓库
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getLocalBranch(DataBaseInfo dataBaseInfo){
|
||||||
|
ResultDto resultDto= this.baseFindList(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),
|
||||||
|
dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName(),T_BD_BRANCH_INFO,"where property =2 ");
|
||||||
|
ResultSet rs= resultDto.getResultSet();
|
||||||
|
String branch_no = "";
|
||||||
|
try {
|
||||||
|
while (rs.next()) {
|
||||||
|
branch_no=rs.getString("branch_no");//
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
resultDto.getConnection().close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return branch_no;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取小发账号
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public String getTRmCashier(DataBaseInfo dataBaseInfo,String accountNo){
|
||||||
|
ResultDto resultDto= this.baseFindList(dataBaseInfo.getIp(),dataBaseInfo.getUserName(),dataBaseInfo.getPassword(),
|
||||||
|
dataBaseInfo.getDbPort(),dataBaseInfo.getDataBaseName(),T_RM_CASHIER,"where cashier_id='"+accountNo+"'");
|
||||||
|
ResultSet rs= resultDto.getResultSet();
|
||||||
|
String cashier_id = "";
|
||||||
|
try {
|
||||||
|
while (rs.next()) {
|
||||||
|
cashier_id=rs.getString("cashier_id");//
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
resultDto.getConnection().close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cashier_id;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,10 @@ import java.util.Date;
|
|||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class DataBaseInfo {
|
public class DataBaseInfo {
|
||||||
|
public static final String SHANG_YUN="1";//1 商云10
|
||||||
|
public static final String SHANG_RUI="2";//2 商瑞9.7
|
||||||
|
public static final String SHANG_QI="3";//3 商祺
|
||||||
|
|
||||||
@ApiModelProperty("数据库IP")
|
@ApiModelProperty("数据库IP")
|
||||||
private String ip;
|
private String ip;
|
||||||
@ApiModelProperty("用户名")
|
@ApiModelProperty("用户名")
|
||||||
@ -41,4 +45,22 @@ public class DataBaseInfo {
|
|||||||
|
|
||||||
@ApiModelProperty(value = "间隔时间")
|
@ApiModelProperty(value = "间隔时间")
|
||||||
private String shopGapTime;
|
private String shopGapTime;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "调价时间")
|
||||||
|
private String priceOperatime;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "库存条件")
|
||||||
|
private String stockOperateWhere;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "营业员账号")
|
||||||
|
private String saleAccount;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否同步活动(0:否,1:是)")
|
||||||
|
private String isSyncActive;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否同步会员(0:否,1:是)")
|
||||||
|
private String isSyncMember;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "客户端版本:1商云10,2商瑞9.7,3商祺")
|
||||||
|
private String clientVersion;
|
||||||
}
|
}
|
||||||
|
|||||||
50
client/src/main/java/com/small/client/dto/ItemInfo.java
Normal file
50
client/src/main/java/com/small/client/dto/ItemInfo.java
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
package com.small.client.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ItemInfo {
|
||||||
|
/**
|
||||||
|
* 商品货架号
|
||||||
|
*/
|
||||||
|
private String itemNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自定义编号
|
||||||
|
*/
|
||||||
|
private String itemSubno;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品名称
|
||||||
|
*/
|
||||||
|
private String itemName;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品分类编号
|
||||||
|
*/
|
||||||
|
private String itemClsno;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商品品牌
|
||||||
|
*/
|
||||||
|
private String itemBrand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单位
|
||||||
|
*/
|
||||||
|
private String unitNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 进货价格
|
||||||
|
*/
|
||||||
|
private BigDecimal price;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 零售价
|
||||||
|
*/
|
||||||
|
private BigDecimal salePrice;
|
||||||
|
}
|
||||||
@ -23,9 +23,15 @@ public class ProductQuantityConsumptionDto {
|
|||||||
@ApiModelProperty("商品编号")
|
@ApiModelProperty("商品编号")
|
||||||
private String productNumber;
|
private String productNumber;
|
||||||
|
|
||||||
|
@ApiModelProperty("商品单价,如果是切割商品要算回来每公斤多少")
|
||||||
|
private BigDecimal unitPrice;
|
||||||
|
|
||||||
@ApiModelProperty("数量(正数表示入库/增加,负数表示出库/减少)")
|
@ApiModelProperty("数量(正数表示入库/增加,负数表示出库/减少)")
|
||||||
private BigDecimal quantity;
|
private BigDecimal quantity;
|
||||||
|
|
||||||
|
@ApiModelProperty("订单总额")
|
||||||
|
private BigDecimal saleAmount;
|
||||||
|
|
||||||
@ApiModelProperty("消费状态:0-未消费,1-已消费")
|
@ApiModelProperty("消费状态:0-未消费,1-已消费")
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
||||||
@ -39,4 +45,7 @@ public class ProductQuantityConsumptionDto {
|
|||||||
@ApiModelProperty(value = "更新时间")
|
@ApiModelProperty(value = "更新时间")
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private Date updateTime;
|
private Date updateTime;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "下单时间")
|
||||||
|
private Long saleTime;
|
||||||
}
|
}
|
||||||
|
|||||||
55
client/src/main/java/com/small/client/dto/RmSaleflow.java
Normal file
55
client/src/main/java/com/small/client/dto/RmSaleflow.java
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package com.small.client.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.sql.Date;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class RmSaleflow {
|
||||||
|
private Long comNo;
|
||||||
|
private Integer flowId;//第几个商品 从1开始算
|
||||||
|
private String flowNo;//自定义编号
|
||||||
|
private String branchNo;
|
||||||
|
private String itemNo;
|
||||||
|
private BigDecimal sourcePrice;
|
||||||
|
private BigDecimal salePrice;
|
||||||
|
private BigDecimal saleQnty;//销售数量
|
||||||
|
private BigDecimal saleMoney;
|
||||||
|
private String sellWay;//A销售,B退货
|
||||||
|
private String operId;
|
||||||
|
private String saleMan;
|
||||||
|
private String counterNo;
|
||||||
|
private Timestamp operDate;
|
||||||
|
private String remoteFlag;
|
||||||
|
private String shiftNo;
|
||||||
|
private String comFlag;
|
||||||
|
private String specFlag;
|
||||||
|
private BigDecimal prefAmt;
|
||||||
|
private BigDecimal inPrice;
|
||||||
|
private BigDecimal nStan;
|
||||||
|
private String chrStan;
|
||||||
|
private String posid;
|
||||||
|
private Date uptime;
|
||||||
|
private String specSheetNo;
|
||||||
|
|
||||||
|
//商云10字段
|
||||||
|
private String flownoRand;
|
||||||
|
private BigDecimal retQnty;
|
||||||
|
private String flownoType;
|
||||||
|
private String cicSheetNo;
|
||||||
|
private String shareCardid;
|
||||||
|
// private String itemNoFresh;
|
||||||
|
|
||||||
|
//商瑞9.7字段
|
||||||
|
private String paysum_flag;
|
||||||
|
private String voucher_no;
|
||||||
|
private String pr_spec_sheet_no;
|
||||||
|
private String ot_spec_sheet_no;
|
||||||
|
private String ot_spec_pref_amt;
|
||||||
|
private String item_no_Fresh;
|
||||||
|
private String kz_flag;
|
||||||
|
private Timestamp update_date;
|
||||||
|
|
||||||
|
}
|
||||||
@ -71,4 +71,16 @@ public class StoreDbConfig implements Serializable {
|
|||||||
|
|
||||||
@ApiModelProperty("商品同步间隔时间")
|
@ApiModelProperty("商品同步间隔时间")
|
||||||
private String shopGapTime;
|
private String shopGapTime;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "营业员账号")
|
||||||
|
private String saleAccount;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否同步活动(0:否,1:是)")
|
||||||
|
private String isSyncActive;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否同步会员(0:否,1:是)")
|
||||||
|
private String isSyncMember;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "客户端版本:1商云10,2商瑞9.7,3商祺")
|
||||||
|
private String clientVersion;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,9 @@ import java.util.List;
|
|||||||
@Data
|
@Data
|
||||||
public class SxGoosModel {
|
public class SxGoosModel {
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "分页编号")
|
||||||
|
private Long rowIdA;
|
||||||
|
|
||||||
@ApiModelProperty("商品名称")
|
@ApiModelProperty("商品名称")
|
||||||
private String product_name;
|
private String product_name;
|
||||||
|
|
||||||
|
|||||||
181
client/src/main/java/com/small/client/dto/TRmPayflow.java
Normal file
181
client/src/main/java/com/small/client/dto/TRmPayflow.java
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
package com.small.client.dto;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.sql.Date;
|
||||||
|
import java.sql.Timestamp;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class TRmPayflow {
|
||||||
|
/**
|
||||||
|
* 流水ID - 主键,自增
|
||||||
|
*/
|
||||||
|
private Long comNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 编号
|
||||||
|
*/
|
||||||
|
private Integer flowId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流水号
|
||||||
|
*/
|
||||||
|
private String flowNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售金额
|
||||||
|
*/
|
||||||
|
private BigDecimal saleAmount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 门店编号
|
||||||
|
*/
|
||||||
|
private String branchNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付方式
|
||||||
|
*/
|
||||||
|
private String payWay;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售方式
|
||||||
|
*/
|
||||||
|
private String sellWay;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 卡号
|
||||||
|
*/
|
||||||
|
private String cardNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 会员号
|
||||||
|
*/
|
||||||
|
private String vipNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 币种
|
||||||
|
*/
|
||||||
|
private String coinNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 汇率
|
||||||
|
*/
|
||||||
|
private BigDecimal coinRate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支付金额
|
||||||
|
*/
|
||||||
|
private BigDecimal payAmount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作时间
|
||||||
|
*/
|
||||||
|
private Timestamp operDate;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 操作员ID
|
||||||
|
*/
|
||||||
|
private String operId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 柜台号
|
||||||
|
*/
|
||||||
|
private String counterNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 销售员
|
||||||
|
*/
|
||||||
|
private String saleMan;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注
|
||||||
|
*/
|
||||||
|
private String memo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 凭证号
|
||||||
|
*/
|
||||||
|
private String voucherNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 远程标志
|
||||||
|
*/
|
||||||
|
private String remoteFlag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 兑换标志
|
||||||
|
*/
|
||||||
|
private String exchangeFlag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 班次号
|
||||||
|
*/
|
||||||
|
private String shiftNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 完成标志
|
||||||
|
*/
|
||||||
|
private String comFlag;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POS机ID
|
||||||
|
*/
|
||||||
|
private String posid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新时间
|
||||||
|
*/
|
||||||
|
private Date uptime;
|
||||||
|
|
||||||
|
private String checkData;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* 商云10
|
||||||
|
* **/
|
||||||
|
/**
|
||||||
|
* 随机流水号
|
||||||
|
*/
|
||||||
|
private String flownoRand;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导游编号
|
||||||
|
*/
|
||||||
|
private String ciceroniNo;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导游ID
|
||||||
|
*/
|
||||||
|
private String ciceroniId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 流水号类型
|
||||||
|
*/
|
||||||
|
private String flownoType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 交易类型
|
||||||
|
*/
|
||||||
|
private String transType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传批次号
|
||||||
|
*/
|
||||||
|
private String upBatchno;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 上传会员账户
|
||||||
|
*/
|
||||||
|
private String upVipacc;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 备注
|
||||||
|
*/
|
||||||
|
private String remark;
|
||||||
|
|
||||||
|
private String cashNo;
|
||||||
|
|
||||||
|
//商瑞9.7
|
||||||
|
private String kz_no;
|
||||||
|
|
||||||
|
}
|
||||||
@ -60,7 +60,7 @@ public abstract class SxDataAbstService {
|
|||||||
* @param allSxSyncCategories 所有分类
|
* @param allSxSyncCategories 所有分类
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public List<SxCategoryModel> ConVToSxCategoryModel(List<SxSyncCategory> sxSyncCategories,List<SxSyncCategory> allSxSyncCategories, Map<String,String> clsBrandMap) {
|
public List<SxCategoryModel> ConVToSxCategoryModel(List<SxSyncCategory> sxSyncCategories,List<SxSyncCategory> allSxSyncCategories, Map<String,String> clsBrandMap) {
|
||||||
if(CollectionUtil.isEmpty(sxSyncCategories)){
|
if(CollectionUtil.isEmpty(sxSyncCategories)){
|
||||||
return new ArrayList<>();
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
@ -69,37 +69,41 @@ public abstract class SxDataAbstService {
|
|||||||
SxCategoryModel sxCategoryModel=null;
|
SxCategoryModel sxCategoryModel=null;
|
||||||
while (iterator.hasNext()){
|
while (iterator.hasNext()){
|
||||||
SxSyncCategory sxSyncCategory= iterator.next();
|
SxSyncCategory sxSyncCategory= iterator.next();
|
||||||
sxCategoryModel=new SxCategoryModel();
|
try {
|
||||||
sxCategoryModel.setCategory_image(SxDataDao.DEFAULT_IMG);
|
sxCategoryModel=new SxCategoryModel();
|
||||||
sxCategoryModel.setCategory_name(sxSyncCategory.getItem_clsname());
|
sxCategoryModel.setCategory_image(SxDataDao.DEFAULT_IMG);
|
||||||
sxCategoryModel.setBrandName(clsBrandMap.get(sxSyncCategory.getItem_clsname()));
|
sxCategoryModel.setCategory_name(sxSyncCategory.getItem_clsname());
|
||||||
//寻找父级
|
sxCategoryModel.setBrandName(clsBrandMap.get(sxSyncCategory.getItem_clsname()));
|
||||||
if(StringUtils.isNotEmpty(sxSyncCategory.getCls_parent())){
|
//寻找父级
|
||||||
SxSyncCategory firstNode=getParentNode(allSxSyncCategories,sxSyncCategory.getCls_parent());
|
if(StringUtils.isNotEmpty(sxSyncCategory.getCls_parent())){
|
||||||
if(null==firstNode){
|
SxSyncCategory firstNode=getParentNode(allSxSyncCategories,sxSyncCategory.getCls_parent());
|
||||||
sxCategoryModel.setFirst_category_name("");
|
if(null==firstNode){
|
||||||
sxCategoryModel.setProduct_type(sxCategoryModel.getCategory_name());
|
sxCategoryModel.setFirst_category_name("");
|
||||||
sxCategoryModels.add(sxCategoryModel);
|
sxCategoryModel.setProduct_type(sxCategoryModel.getCategory_name());
|
||||||
continue;
|
sxCategoryModels.add(sxCategoryModel);
|
||||||
}
|
continue;
|
||||||
sxCategoryModel.setParent_name(firstNode.getItem_clsname());//todo 暂时无用
|
}
|
||||||
//如何存在上级的上级,则上级为第二层,上上及为第一层
|
sxCategoryModel.setParent_name(firstNode.getItem_clsname());//todo 暂时无用
|
||||||
if(StringUtils.isNotEmpty(firstNode.getCls_parent())) {//还存在上级
|
//如何存在上级的上级,则上级为第二层,上上及为第一层
|
||||||
SxSyncCategory secondNode=getParentNode(allSxSyncCategories,firstNode.getCls_parent());
|
if(StringUtils.isNotEmpty(firstNode.getCls_parent())) {//还存在上级
|
||||||
if(null!=secondNode && secondNode.getItem_clsno().equals(firstNode.getCls_parent())){
|
SxSyncCategory secondNode=getParentNode(allSxSyncCategories,firstNode.getCls_parent());
|
||||||
sxCategoryModel.setFirst_category_name(secondNode.getItem_clsname());
|
if(null!=secondNode && secondNode.getItem_clsno().equals(firstNode.getCls_parent())){
|
||||||
sxCategoryModel.setSecond_category_name(firstNode.getItem_clsname());
|
sxCategoryModel.setFirst_category_name(secondNode.getItem_clsname());
|
||||||
|
sxCategoryModel.setSecond_category_name(firstNode.getItem_clsname());
|
||||||
|
}else {
|
||||||
|
sxCategoryModel.setFirst_category_name(firstNode.getItem_clsname());
|
||||||
|
}
|
||||||
}else {
|
}else {
|
||||||
sxCategoryModel.setFirst_category_name(firstNode.getItem_clsname());
|
sxCategoryModel.setFirst_category_name(firstNode.getItem_clsname());
|
||||||
}
|
}
|
||||||
}else {
|
}else {
|
||||||
sxCategoryModel.setFirst_category_name(firstNode.getItem_clsname());
|
sxCategoryModel.setFirst_category_name("");
|
||||||
}
|
}
|
||||||
}else {
|
sxCategoryModel.setProduct_type(sxCategoryModel.getCategory_name());
|
||||||
sxCategoryModel.setFirst_category_name("");
|
sxCategoryModels.add(sxCategoryModel);
|
||||||
|
}catch (Exception e){
|
||||||
|
log.info("sxSyncCategory:{}",sxSyncCategory);
|
||||||
}
|
}
|
||||||
sxCategoryModel.setProduct_type(sxCategoryModel.getCategory_name());
|
|
||||||
sxCategoryModels.add(sxCategoryModel);
|
|
||||||
}
|
}
|
||||||
sxCategoryModels=filterCategories(sxCategoryModels);
|
sxCategoryModels=filterCategories(sxCategoryModels);
|
||||||
return sxCategoryModels;
|
return sxCategoryModels;
|
||||||
@ -132,7 +136,7 @@ public abstract class SxDataAbstService {
|
|||||||
List<SxSyncCategory> list= sxSyncCategories.stream().filter(cc->
|
List<SxSyncCategory> list= sxSyncCategories.stream().filter(cc->
|
||||||
cc.getItem_clsno().trim().equals(parentId.trim()))
|
cc.getItem_clsno().trim().equals(parentId.trim()))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
return CollectionUtil.isNotEmpty(list)?list.get(0):new SxSyncCategory();
|
return CollectionUtil.isNotEmpty(list)?list.get(0):null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -148,6 +152,7 @@ public abstract class SxDataAbstService {
|
|||||||
SxGoosModel sxGoosModel=null;
|
SxGoosModel sxGoosModel=null;
|
||||||
for (SxSyncGoods sxSyncGood:sxSyncGoods){
|
for (SxSyncGoods sxSyncGood:sxSyncGoods){
|
||||||
sxGoosModel=new SxGoosModel();
|
sxGoosModel=new SxGoosModel();
|
||||||
|
sxGoosModel.setRowIdA(sxSyncGood.getId());
|
||||||
sxGoosModel.setProduct_name(sxSyncGood.getItem_subname());
|
sxGoosModel.setProduct_name(sxSyncGood.getItem_subname());
|
||||||
sxGoosModel.setProduct_number(sxSyncGood.getItem_no().trim());//
|
sxGoosModel.setProduct_number(sxSyncGood.getItem_no().trim());//
|
||||||
sxGoosModel.setProduct_barcode(sxSyncGood.getItem_no().trim());//
|
sxGoosModel.setProduct_barcode(sxSyncGood.getItem_no().trim());//
|
||||||
|
|||||||
@ -63,7 +63,7 @@ public interface SxDataService {
|
|||||||
* @param commentModel
|
* @param commentModel
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
void syncAtive(DataBaseInfo dataBaseInfo,CommentModel commentModel);
|
boolean syncAtive(DataBaseInfo dataBaseInfo,CommentModel commentModel);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 同步活动商品
|
* 同步活动商品
|
||||||
@ -73,4 +73,18 @@ public interface SxDataService {
|
|||||||
void syncAtiveShops(DataBaseInfo dataBaseInfo,CommentModel commentModel);
|
void syncAtiveShops(DataBaseInfo dataBaseInfo,CommentModel commentModel);
|
||||||
|
|
||||||
String getCategoryChildren(DataBaseInfo dataBaseInfo,String parentId);
|
String getCategoryChildren(DataBaseInfo dataBaseInfo,String parentId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验是否有新的入库
|
||||||
|
* @param dataBaseInfo
|
||||||
|
* @param refreshTime
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
boolean isNewShop(DataBaseInfo dataBaseInfo, String refreshTime);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 刷新同步时间
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
void refreshTime(CommentModel commentModel);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,31 +22,22 @@ import com.small.client.service.WebClientService;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.http.client.config.RequestConfig;
|
|
||||||
import org.apache.http.impl.client.CloseableHttpClient;
|
|
||||||
import org.apache.http.impl.client.HttpClients;
|
|
||||||
import org.apache.http.impl.client.LaxRedirectStrategy;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
import org.springframework.core.io.Resource;
|
import org.springframework.core.io.Resource;
|
||||||
import org.springframework.http.*;
|
import org.springframework.http.*;
|
||||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.util.StreamUtils;
|
import org.springframework.util.StreamUtils;
|
||||||
import org.springframework.web.client.RestTemplate;
|
import org.springframework.web.client.RestTemplate;
|
||||||
import org.springframework.web.util.UriComponentsBuilder;
|
import org.springframework.web.util.UriComponentsBuilder;
|
||||||
|
|
||||||
import javax.net.ssl.SSLContext;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.security.KeyManagementException;
|
|
||||||
import java.security.KeyStoreException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -142,9 +133,10 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
|
|||||||
syncAllGoods(dataBaseInfo, commentModel);
|
syncAllGoods(dataBaseInfo, commentModel);
|
||||||
break;
|
break;
|
||||||
case "02"://增量 todo test
|
case "02"://增量 todo test
|
||||||
syncIncrementAddGoods(dataBaseInfo, commentModel);
|
String sycnTime=commentModel.getSyncTime();
|
||||||
syncIncrementModifyGoods(dataBaseInfo, commentModel);
|
syncIncrementAddGoods(dataBaseInfo, commentModel);//同步新商品和修改商品
|
||||||
syncIncrementStock(dataBaseInfo, commentModel);
|
syncIncrementStock(dataBaseInfo, commentModel);//同步库存
|
||||||
|
syncFlowPrice(dataBaseInfo, commentModel,sycnTime);//同步调价单
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -317,6 +309,22 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
|
|||||||
return root;
|
return root;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断是否有新增商品,有新增商品才同步分类
|
||||||
|
* @param dataBaseInfo
|
||||||
|
* @param refreshTime
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean isNewShop(DataBaseInfo dataBaseInfo, String refreshTime){
|
||||||
|
String where="where b.status='1' ";
|
||||||
|
where+=" and ( b.modify_date>'"+refreshTime+"' ";
|
||||||
|
where+=" or b.build_date>'"+refreshTime+"') ";
|
||||||
|
dataBaseInfo.setWhere(where);
|
||||||
|
int total = sxDataDao.getTBditemInfoTotal(dataBaseInfo);
|
||||||
|
log.info("判断库存数量,库存数量:{}",total);
|
||||||
|
return total>0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*同步所有商品
|
*同步所有商品
|
||||||
* @param dataBaseInfo
|
* @param dataBaseInfo
|
||||||
@ -324,34 +332,59 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
|
|||||||
*/
|
*/
|
||||||
private void syncAllGoods(DataBaseInfo dataBaseInfo, CommentModel commentModel){
|
private void syncAllGoods(DataBaseInfo dataBaseInfo, CommentModel commentModel){
|
||||||
//String where="where 1=1";
|
//String where="where 1=1";
|
||||||
String where="where b.status='1'";
|
String where="where shop.status='1'";
|
||||||
Integer total =0;
|
Integer total =0;
|
||||||
|
boolean isAll=false;
|
||||||
String parentId=getAndCacheTree(dataBaseInfo,dataBaseInfo.getCategoryName());//加载缓存用的
|
String parentId=getAndCacheTree(dataBaseInfo,dataBaseInfo.getCategoryName());//加载缓存用的
|
||||||
|
String fileEndFix="";
|
||||||
if(DicEnum.SYNCTYPE_02.getCode().equals(dataBaseInfo.getSyncType())){
|
if(DicEnum.SYNCTYPE_02.getCode().equals(dataBaseInfo.getSyncType())){
|
||||||
if (StringUtils.isNotEmpty(dataBaseInfo.getCategoryName())) {
|
if (StringUtils.isNotEmpty(dataBaseInfo.getCategoryName())) {
|
||||||
//syncGoodsSearchModel.setItemClsno(childrens);
|
//syncGoodsSearchModel.setItemClsno(childrens);
|
||||||
String childrens= commonCache.get(CommonCache.CACHE_CATEGROY+parentId);
|
String childrens= commonCache.get(CommonCache.CACHE_CATEGROY+parentId);
|
||||||
where += " and b.item_clsno in ('" + childrens + "')";
|
where += " and shop.item_clsno in ('" + childrens + "')";
|
||||||
|
fileEndFix="category";
|
||||||
}
|
}
|
||||||
if(StringUtils.isNotEmpty(commentModel.getSyncTime())){
|
if(StringUtils.isNotEmpty(commentModel.getSyncTime())){
|
||||||
where+=" and b.modify_date>'"+commentModel.getSyncTime()+"' ";
|
where+=" and shop.modify_date>'"+commentModel.getSyncTime()+"' ";
|
||||||
where+=" or b.build_date>'"+commentModel.getSyncTime()+"' ";
|
where+=" or shop.build_date>'"+commentModel.getSyncTime()+"' ";
|
||||||
|
fileEndFix="modify";
|
||||||
}
|
}
|
||||||
if(StringUtils.isNotEmpty(dataBaseInfo.getOperDate())){
|
if(StringUtils.isNotEmpty(dataBaseInfo.getOperDate())){
|
||||||
where+=" and ls.oper_date>'"+dataBaseInfo.getOperDate()+"' ";
|
String stockOperateWhere=" ls.oper_date>'"+dataBaseInfo.getOperDate()+"' ";
|
||||||
|
dataBaseInfo.setStockOperateWhere(stockOperateWhere);
|
||||||
|
fileEndFix="operate";
|
||||||
|
}
|
||||||
|
if(StringUtils.isNotEmpty(dataBaseInfo.getPriceOperatime())){
|
||||||
|
where+=" and EXISTS ( " +
|
||||||
|
" select " +
|
||||||
|
" shop.item_no " +
|
||||||
|
" from " +
|
||||||
|
" t_pc_price_flow_detail tpfd " +
|
||||||
|
" left join t_pc_price_flow_master tppfm " +
|
||||||
|
" on " +
|
||||||
|
" tpfd.sheet_no = tppfm.sheet_no " +
|
||||||
|
" where " +
|
||||||
|
" tppfm.valid_flag = '1' " +//有效
|
||||||
|
" and shop.item_no=tpfd.item_no " +
|
||||||
|
" and tppfm.oper_date >'"+dataBaseInfo.getPriceOperatime()+"' " +
|
||||||
|
" )";
|
||||||
|
fileEndFix="price";
|
||||||
}
|
}
|
||||||
dataBaseInfo.setWhere(where);
|
dataBaseInfo.setWhere(where);
|
||||||
// 记录总数
|
// 记录总数
|
||||||
total = sxDataDao.getTBditemInfoJoninTotal(dataBaseInfo);
|
total = sxDataDao.getTBditemInfoJoninTotal(dataBaseInfo);
|
||||||
}else {
|
}else {
|
||||||
|
where="where b.status='1'";
|
||||||
|
isAll=true;
|
||||||
dataBaseInfo.setWhere(where);
|
dataBaseInfo.setWhere(where);
|
||||||
total = sxDataDao.getTBditemInfoTotal(dataBaseInfo);
|
total = sxDataDao.getTBditemInfoTotal(dataBaseInfo);
|
||||||
|
where="where shop.status='1'";
|
||||||
}
|
}
|
||||||
if(total==0){
|
if(total==0){
|
||||||
log.info("暂无商品同步");
|
log.info("暂无商品同步");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
where+=" and";
|
//where+=" and";
|
||||||
dataBaseInfo.setWhere(where);
|
dataBaseInfo.setWhere(where);
|
||||||
|
|
||||||
// 总页数
|
// 总页数
|
||||||
@ -365,7 +398,7 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
|
|||||||
Date refreshDate= DateUtil.date(tenMinutesAgo);
|
Date refreshDate= DateUtil.date(tenMinutesAgo);
|
||||||
List<String> folders=new ArrayList<>();
|
List<String> folders=new ArrayList<>();
|
||||||
for (int i = 1; i <=pages; i++) {
|
for (int i = 1; i <=pages; i++) {
|
||||||
List<SxSyncGoods> sxSyncGoods= sxDataDao.findBditemInfoListPage(dataBaseInfo,i,SxDataDao.PAGESIZE);
|
List<SxSyncGoods> sxSyncGoods= sxDataDao.findBditemInfoListPage(dataBaseInfo,i,SxDataDao.PAGESIZE,isAll);
|
||||||
List<SxGoosModel> sxGoosModelList= CvtToGoosModel(sxSyncGoods,specPriceDtoList);
|
List<SxGoosModel> sxGoosModelList= CvtToGoosModel(sxSyncGoods,specPriceDtoList);
|
||||||
String jsonString="";
|
String jsonString="";
|
||||||
ObjectMapper objectMapper = new ObjectMapper();
|
ObjectMapper objectMapper = new ObjectMapper();
|
||||||
@ -374,11 +407,11 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
|
|||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
String code= writeToFileAndUploud(i,jsonString,commentModel,DicEnum.MUAL_1.getCode());
|
String code= writeToFileAndUploud(i,jsonString,commentModel,DicEnum.MUAL_1.getCode(),fileEndFix);
|
||||||
if (!HttpUtils.SUCCESSCODE.equals(code)) {
|
if (!HttpUtils.SUCCESSCODE.equals(code)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
folders.add(String.valueOf(i));
|
folders.add(i+"_"+fileEndFix);
|
||||||
syncCount+=sxSyncGoods.size();
|
syncCount+=sxSyncGoods.size();
|
||||||
}
|
}
|
||||||
//通知服务器上传cos
|
//通知服务器上传cos
|
||||||
@ -388,7 +421,7 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
|
|||||||
+"&syncType="+DicEnum.MUAL_1.getCode()
|
+"&syncType="+DicEnum.MUAL_1.getCode()
|
||||||
+"&refreshDateStr="+refreshDate,
|
+"&refreshDateStr="+refreshDate,
|
||||||
JSONUtil.parseArray(folders));
|
JSONUtil.parseArray(folders));
|
||||||
log.info("商品分类总共有{}条数据,同步完成{}条",total,syncCount);
|
log.info("商品总共有{}条数据,同步完成{}条",total,syncCount);
|
||||||
String code= HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_GOODS_READ
|
String code= HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_GOODS_READ
|
||||||
+"?appKey="+commentModel.getAppKey()
|
+"?appKey="+commentModel.getAppKey()
|
||||||
+"&sign="+commentModel.getAppId()
|
+"&sign="+commentModel.getAppId()
|
||||||
@ -408,15 +441,15 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
|
|||||||
* @param commentModel
|
* @param commentModel
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private String writeToFileAndUploud(Integer page,String content,CommentModel commentModel,String syncType){
|
private String writeToFileAndUploud(Integer page,String content,CommentModel commentModel,String syncType,String fileEndFix){
|
||||||
FileUtils fileUtils= new FileUtils();
|
FileUtils fileUtils= new FileUtils();
|
||||||
File file=fileUtils.createFile(syncType,page);
|
File file=fileUtils.createFile(syncType,page);
|
||||||
String fileName=fileUtils.getFileName(syncType,page,FileUtils.txtEnd);
|
String fileName=fileUtils.getFileName(syncType,page,FileUtils.txtEnd,fileEndFix);
|
||||||
String filePath=file.getAbsolutePath();
|
String filePath=file.getAbsolutePath();
|
||||||
fileUtils.writeFile(filePath,fileName,content);
|
fileUtils.writeFile(filePath,fileName,content);
|
||||||
String sign=CommonUtil.generateOpenSign(content,commentModel.getAppKey(),commentModel.getAppId());
|
String sign=CommonUtil.generateOpenSign(content,commentModel.getAppKey(),commentModel.getAppId());
|
||||||
commentModel.setSign(sign);
|
commentModel.setSign(sign);
|
||||||
return webClientService.uploudSxData(filePath+FileUtils.pathSeparator+fileName,commentModel,page.toString(),syncType);
|
return webClientService.uploudSxData(filePath+FileUtils.pathSeparator+fileName,commentModel,page+"_"+fileEndFix,syncType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -458,6 +491,17 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
|
|||||||
syncAllGoods(dataBaseInfo,commentModel);
|
syncAllGoods(dataBaseInfo,commentModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 增量同步 调价单变化
|
||||||
|
*/
|
||||||
|
private void syncFlowPrice(DataBaseInfo dataBaseInfo, CommentModel commentModel,String sycnTime){
|
||||||
|
dataBaseInfo.setPriceOperatime(sycnTime);
|
||||||
|
commentModel.setSyncTime("");
|
||||||
|
dataBaseInfo.setOperDate("");
|
||||||
|
dataBaseInfo.setStockOperateWhere("");
|
||||||
|
syncAllGoods(dataBaseInfo,commentModel);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*获取特价商品
|
*获取特价商品
|
||||||
* @param dataBaseInfo
|
* @param dataBaseInfo
|
||||||
@ -600,10 +644,10 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
|
|||||||
log.info("文件下载目录: {}", downloadDirectory);
|
log.info("文件下载目录: {}", downloadDirectory);
|
||||||
HttpHeaders headers = new HttpHeaders();
|
HttpHeaders headers = new HttpHeaders();
|
||||||
// headers.set("User-Agent", "Apifox/1.0.0 (https://apifox.com)");
|
// headers.set("User-Agent", "Apifox/1.0.0 (https://apifox.com)");
|
||||||
headers.setAccept(Collections.singletonList(MediaType.ALL)); // 对应 */*
|
//headers.setAccept(Collections.singletonList(MediaType.ALL)); // 对应 */*
|
||||||
headers.set("Accept-Encoding", "gzip, deflate, br");
|
// headers.set("Accept-Encoding", "gzip, deflate, br");
|
||||||
// 可以添加其他Apifox可能发送的头部
|
// 可以添加其他Apifox可能发送的头部
|
||||||
headers.set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
|
// headers.set("Accept-Language", "zh-CN,zh;q=0.9,en;q=0.8");
|
||||||
headers.set("Connection", "keep-alive");
|
headers.set("Connection", "keep-alive");
|
||||||
|
|
||||||
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(headers);
|
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(headers);
|
||||||
@ -666,7 +710,8 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
|
|||||||
log.error("文件下载失败:{}",e.getMessage());
|
log.error("文件下载失败:{}",e.getMessage());
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return downloadDirectory+originalFileName;
|
File file=new File(downloadDirectory+originalFileName);
|
||||||
|
return file.length()>0?downloadDirectory+originalFileName:null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -726,6 +771,10 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
|
|||||||
dataBaseInfo.setRefreshTime(storeDbConfig.getRefreshTime());
|
dataBaseInfo.setRefreshTime(storeDbConfig.getRefreshTime());
|
||||||
dataBaseInfo.setIsTowSync(storeDbConfig.getIsTowSync());
|
dataBaseInfo.setIsTowSync(storeDbConfig.getIsTowSync());
|
||||||
dataBaseInfo.setShopGapTime(storeDbConfig.getShopGapTime());
|
dataBaseInfo.setShopGapTime(storeDbConfig.getShopGapTime());
|
||||||
|
dataBaseInfo.setSaleAccount(storeDbConfig.getSaleAccount());
|
||||||
|
dataBaseInfo.setIsSyncActive(storeDbConfig.getIsSyncActive());
|
||||||
|
dataBaseInfo.setIsSyncMember(storeDbConfig.getIsSyncMember());
|
||||||
|
dataBaseInfo.setClientVersion(storeDbConfig.getClientVersion());
|
||||||
return dataBaseInfo;
|
return dataBaseInfo;
|
||||||
}
|
}
|
||||||
return new DataBaseInfo();
|
return new DataBaseInfo();
|
||||||
@ -737,31 +786,54 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
|
|||||||
log.info("不做双向同步数据库");
|
log.info("不做双向同步数据库");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
String saleAccount= sxDataDao.getTRmCashier(dataBaseInfo,dataBaseInfo.getSaleAccount());
|
||||||
|
if(StringUtils.isEmpty(saleAccount)){
|
||||||
|
log.error("需要配置商城收银人员");
|
||||||
|
return;
|
||||||
|
}
|
||||||
JSONObject jsonObject= restTemplate.getForObject(remoteIp+HttpUtils.URL_SYNC_GET_STOR_DATA_RELEASE
|
JSONObject jsonObject= restTemplate.getForObject(remoteIp+HttpUtils.URL_SYNC_GET_STOR_DATA_RELEASE
|
||||||
+"?appKey="+commentModel.getAppKey()
|
+"?appKey="+commentModel.getAppKey()
|
||||||
+"&sign="+commentModel.getAppId(),JSONObject.class);
|
+"&sign="+commentModel.getAppId(),JSONObject.class);
|
||||||
|
// String jsonStr="[{\"consumeId\":\"1986611923814223873\",\"orderId\":\"DD_20251107_1\",\"productNumber\":\"31011\",\"unitPrice\":10.90,\"quantity\":1.500,\"saleAmount\":16.35,\"status\":0,\"storeId\":78,\"createTime\":\"2025-11-07 01:49:26\",\"updateTime\":\"2025-11-07 01:49:26\"},{\"consumeId\":\"1986611923814223872\",\"orderId\":\"DD_20251107_2\",\"productNumber\":\"6909409023853\",\"unitPrice\":1.00,\"quantity\":1.000,\"saleAmount\":1.00,\"status\":0,\"storeId\":78,\"createTime\":\"2025-11-07 01:49:26\",\"updateTime\":\"2025-11-07 01:49:26\"},{\"consumeId\":\"1986611923814223874\",\"orderId\":\"DD_20251107_1\",\"productNumber\":\"6909409023853\",\"unitPrice\":1.00,\"quantity\":2.000,\"saleAmount\":2.00,\"status\":0,\"storeId\":78,\"createTime\":\"2025-11-07 01:49:26\",\"updateTime\":\"2025-11-07 01:49:26\"}]";
|
||||||
|
//String jsonObjectStr="{\"error_code\":0,\"error_msg\":\"success\",\"result\":[{\"consumeId\":\"1993254597656690689\",\"orderId\":\"DD_20251125_2\",\"productNumber\":\"10035\",\"unitPrice\":16.00,\"quantity\":-0.500,\"saleAmount\":8.00,\"status\":0,\"storeId\":78,\"createTime\":\"2025-11-25 09:45:03\",\"updateTime\":\"2025-11-25 09:45:03\",\"saleTime\":1764063651000},{\"consumeId\":\"1993254597656690688\",\"orderId\":\"DD_20251125_2\",\"productNumber\":\"6922896008127\",\"unitPrice\":8.80,\"quantity\":-1.000,\"saleAmount\":8.80,\"status\":0,\"storeId\":78,\"createTime\":\"2025-11-25 09:45:03\",\"updateTime\":\"2025-11-25 09:45:03\",\"saleTime\":1764063651000}]}";
|
||||||
|
// JSONObject jsonObject=JSONUtil.parseObj(jsonObjectStr);
|
||||||
if(null!=jsonObject.get("result")){
|
if(null!=jsonObject.get("result")){
|
||||||
// Map map=(Map)jsonObject.get("result");
|
// Map map=(Map)jsonObject.get("result");
|
||||||
|
String jsonStr= jsonObject.getStr("result");
|
||||||
List<ProductQuantityConsumptionDto> productQuantityConsumptionDtoList =
|
List<ProductQuantityConsumptionDto> productQuantityConsumptionDtoList =
|
||||||
JSONUtil.toList(jsonObject.getStr("result"),ProductQuantityConsumptionDto.class);
|
JSONUtil.toList(jsonStr,ProductQuantityConsumptionDto.class);
|
||||||
if(!productQuantityConsumptionDtoList.isEmpty()){
|
if(!productQuantityConsumptionDtoList.isEmpty()){
|
||||||
Map map = productQuantityConsumptionDtoList.stream()
|
Map map = productQuantityConsumptionDtoList.stream()
|
||||||
.collect(Collectors.groupingBy(
|
.collect(Collectors.groupingBy(
|
||||||
ProductQuantityConsumptionDto::getProductNumber,
|
dto -> dto.getOrderId()+"-"+dto.getProductNumber(),
|
||||||
Collectors.reducing(
|
Collectors.reducing(
|
||||||
BigDecimal.ZERO,
|
BigDecimal.ZERO,
|
||||||
ProductQuantityConsumptionDto::getQuantity,
|
ProductQuantityConsumptionDto::getQuantity,
|
||||||
BigDecimal::add
|
BigDecimal::add
|
||||||
)
|
)
|
||||||
));
|
));
|
||||||
sxDataDao.updateStoreData(dataBaseInfo,map,productQuantityConsumptionDtoList,commentModel);
|
Map<String,ProductQuantityConsumptionDto> productQuantityConsumptionDtoMap=new HashMap<>();
|
||||||
}
|
|
||||||
|
|
||||||
|
productQuantityConsumptionDtoList.forEach(productQuantityConsumptionDto -> {
|
||||||
|
String key=productQuantityConsumptionDto.getOrderId()+"-"+productQuantityConsumptionDto.getProductNumber();
|
||||||
|
if(null!=productQuantityConsumptionDtoMap.get(key)){
|
||||||
|
ProductQuantityConsumptionDto oldProductQuantityConsumptionDto=productQuantityConsumptionDtoMap.get(key);
|
||||||
|
if(productQuantityConsumptionDto.getSaleTime()<oldProductQuantityConsumptionDto.getSaleTime()){
|
||||||
|
productQuantityConsumptionDtoMap.put(key,productQuantityConsumptionDto);
|
||||||
|
}
|
||||||
|
}else {
|
||||||
|
productQuantityConsumptionDtoMap.put(key,productQuantityConsumptionDto);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
sxDataDao.updateStoreData(dataBaseInfo,map,productQuantityConsumptionDtoMap,productQuantityConsumptionDtoList,commentModel);
|
||||||
|
}else {
|
||||||
|
log.info("无线上流水同步");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void syncAtive(DataBaseInfo dataBaseInfo, CommentModel commentModel) {
|
public boolean syncAtive(DataBaseInfo dataBaseInfo, CommentModel commentModel) {
|
||||||
String where="where 1=1";
|
String where="where 1=1";
|
||||||
Integer total =0;
|
Integer total =0;
|
||||||
if(StringUtils.isNotEmpty(commentModel.getSyncTime())){
|
if(StringUtils.isNotEmpty(commentModel.getSyncTime())){
|
||||||
@ -772,7 +844,7 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
|
|||||||
total = sxDataDao.getNewActiveCount(dataBaseInfo);
|
total = sxDataDao.getNewActiveCount(dataBaseInfo);
|
||||||
if(total==0){
|
if(total==0){
|
||||||
log.info("暂无活动同步");
|
log.info("暂无活动同步");
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
// 总页数
|
// 总页数
|
||||||
int pages = CommonUtil.getPagesCount(total, SxDataDao.PAGESIZE);
|
int pages = CommonUtil.getPagesCount(total, SxDataDao.PAGESIZE);
|
||||||
@ -797,7 +869,7 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
|
|||||||
syncCount+=activeDtos.size();
|
syncCount+=activeDtos.size();
|
||||||
}
|
}
|
||||||
log.info("成功同步活动数据:"+syncCount);
|
log.info("成功同步活动数据:"+syncCount);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -805,13 +877,16 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
|
|||||||
String where="where 1=1";
|
String where="where 1=1";
|
||||||
Integer total =0;
|
Integer total =0;
|
||||||
if(StringUtils.isNotEmpty(commentModel.getSyncTime())){
|
if(StringUtils.isNotEmpty(commentModel.getSyncTime())){
|
||||||
where+=" and m.oper_date>'"+commentModel.getSyncTime()+"' ";
|
where+=" and t.oper_date>'"+commentModel.getSyncTime()+"' ";
|
||||||
// where+=" or m.oper_date>'"+commentModel.getSyncTime()+"') ";
|
// where+=" or m.oper_date>'"+commentModel.getSyncTime()+"') ";
|
||||||
}
|
}
|
||||||
|
|
||||||
dataBaseInfo.setWhere(where);
|
dataBaseInfo.setWhere(where);
|
||||||
total = sxDataDao.getTotalSpecShop(dataBaseInfo);
|
total = sxDataDao.getTotalSpecShop(dataBaseInfo);
|
||||||
if(total==0){
|
if(total==0){
|
||||||
|
// HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_REFRESH
|
||||||
|
// +"?appKey="+commentModel.getAppKey()
|
||||||
|
// +"&sign="+commentModel.getAppId(), new JSONArray());
|
||||||
log.info("暂无活动商品同步");
|
log.info("暂无活动商品同步");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -839,9 +914,9 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
|
|||||||
syncCount+=activeDtos.size();
|
syncCount+=activeDtos.size();
|
||||||
}
|
}
|
||||||
log.info("成功同步活动商品数据:"+syncCount);
|
log.info("成功同步活动商品数据:"+syncCount);
|
||||||
HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_REFRESH
|
// HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_REFRESH
|
||||||
+"?appKey="+commentModel.getAppKey()
|
// +"?appKey="+commentModel.getAppKey()
|
||||||
+"&sign="+commentModel.getAppId(), new JSONArray());
|
// +"&sign="+commentModel.getAppId(), new JSONArray());
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -855,35 +930,12 @@ public class SxDataServiceImp extends SxDataAbstService implements SxDataService
|
|||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 创建跳过SSL验证的RestTemplate
|
|
||||||
* 仅用于测试环境,生产环境不安全
|
|
||||||
*/
|
|
||||||
private RestTemplate createUnsafeRestTemplate() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
|
|
||||||
// 配置请求参数
|
|
||||||
RequestConfig config = RequestConfig.custom()
|
|
||||||
.setConnectTimeout(30000)
|
|
||||||
.setSocketTimeout(30000)
|
|
||||||
.setRedirectsEnabled(true)
|
|
||||||
.setMaxRedirects(10)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// 创建SSL上下文(信任所有证书 - 仅测试环境使用)
|
@Override
|
||||||
SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
|
public void refreshTime(CommentModel commentModel) {
|
||||||
.loadTrustMaterial((chain, authType) -> true)
|
HttpUtils.postData(restTemplate,remoteIp+HttpUtils.URL_SYNC_REFRESH
|
||||||
.build();
|
+"?appKey="+commentModel.getAppKey()
|
||||||
|
+"&sign="+commentModel.getAppId(), new JSONArray());
|
||||||
// 创建HttpClient,配置SSL和重定向
|
|
||||||
CloseableHttpClient httpClient = HttpClients.custom()
|
|
||||||
.setDefaultRequestConfig(config)
|
|
||||||
.setSSLContext(sslContext)
|
|
||||||
.setRedirectStrategy(new LaxRedirectStrategy())
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// 创建请求工厂
|
|
||||||
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);
|
|
||||||
|
|
||||||
return new RestTemplate(factory);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,7 @@ import com.suisung.mall.common.utils.I18nUtil;
|
|||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
@ -32,6 +33,7 @@ public class AccountBaseUserLevelController {
|
|||||||
@Autowired
|
@Autowired
|
||||||
private AccountBaseUserLevelService accountBaseUserLevelService;
|
private AccountBaseUserLevelService accountBaseUserLevelService;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
@Autowired
|
@Autowired
|
||||||
private AccountService accountService;
|
private AccountService accountService;
|
||||||
|
|
||||||
|
|||||||
@ -467,6 +467,11 @@ public class AccountController {
|
|||||||
return accountUserBaseService.saveBatchAccountInfo(accountUserInfoList);
|
return accountUserBaseService.saveBatchAccountInfo(accountUserInfoList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequestMapping(value = "/saveBatchAccountUserLogin", method = RequestMethod.POST)
|
||||||
|
public ThirdApiRes saveBatchAccountUserLogin(@RequestBody List<AccountUserLogin> accountUserLoginList) {
|
||||||
|
return accountUserBaseService.saveBatchAccountUserLogin(accountUserLoginList);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@RequestMapping(value = "/findOneAccountUserBase", method = RequestMethod.POST)
|
@RequestMapping(value = "/findOneAccountUserBase", method = RequestMethod.POST)
|
||||||
public AccountUserBase findOneAccountUserBase(@RequestBody AccountUserBase accountUserBase) {
|
public AccountUserBase findOneAccountUserBase(@RequestBody AccountUserBase accountUserBase) {
|
||||||
|
|||||||
@ -31,6 +31,7 @@ import io.swagger.annotations.ApiOperation;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.data.util.Pair;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
import org.springframework.web.bind.annotation.RequestParam;
|
||||||
@ -231,5 +232,17 @@ public class AccountUserBaseController extends BaseControllerImpl {
|
|||||||
return CommonResult.success(accountUserBaseService.logout());
|
return CommonResult.success(accountUserBaseService.logout());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation("注册商家账号,项目之间远程调用")
|
||||||
|
@RequestMapping(value = "/merchant/inner-register", method = RequestMethod.POST)
|
||||||
|
public Pair<Boolean, String> merchantInnerRegister(@RequestParam("mobile") String mobile, @RequestParam("regPwd") String regPwd) {
|
||||||
|
return accountUserBaseService.merchantInnerRegister(mobile, regPwd);
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation("更改分店商家的手机号(仅限于分店,尽量不要更高总店的手机号),项目之间远程调用")
|
||||||
|
@RequestMapping(value = "/change/merchant/login-mobile", method = RequestMethod.POST)
|
||||||
|
public Pair<Boolean, String> changeMerchantLoginMobile(@RequestParam("oldMobile") String oldMobile, @RequestParam("newMobile") String newMobile, @RequestParam("password") String password) {
|
||||||
|
return accountUserBaseService.changeMerchantLoginMobile(oldMobile, newMobile, password);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -85,5 +85,17 @@ public class AccountUserBindConnectController {
|
|||||||
return accountUserBindConnectService.getUserBindConnectUserIdByCondition(bind_id, bind_type, user_type);
|
return accountUserBindConnectService.getUserBindConnectUserIdByCondition(bind_id, bind_type, user_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查手机注册的商家是否存在(仅供内部调用)
|
||||||
|
*
|
||||||
|
* @param mobile
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation(value = "检查手机注册的商家是否存在(仅供内部调用)", notes = "检查手机注册的商家是否存在(仅供内部调用)")
|
||||||
|
@RequestMapping(value = "/is-merchant-exists", method = RequestMethod.POST)
|
||||||
|
public Integer isMerchantExists(@RequestParam(name = "mobile") String mobile) {
|
||||||
|
return accountUserBindConnectService.isMerchantExists(mobile);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import com.suisung.mall.common.api.CommonResult;
|
|||||||
import com.suisung.mall.common.domain.UserDto;
|
import com.suisung.mall.common.domain.UserDto;
|
||||||
import com.suisung.mall.common.modules.account.AccountUserBase;
|
import com.suisung.mall.common.modules.account.AccountUserBase;
|
||||||
import com.suisung.mall.common.modules.account.AccountUserInfo;
|
import com.suisung.mall.common.modules.account.AccountUserInfo;
|
||||||
|
import com.suisung.mall.common.modules.account.AccountUserLogin;
|
||||||
import com.suisung.mall.common.pojo.req.WxUserInfoReq;
|
import com.suisung.mall.common.pojo.req.WxUserInfoReq;
|
||||||
import com.suisung.mall.common.pojo.res.ThirdApiRes;
|
import com.suisung.mall.common.pojo.res.ThirdApiRes;
|
||||||
import com.suisung.mall.core.web.service.IBaseService;
|
import com.suisung.mall.core.web.service.IBaseService;
|
||||||
@ -296,4 +297,30 @@ public interface AccountUserBaseService extends IBaseService<AccountUserBase> {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
ThirdApiRes saveBatchAccountInfo(List<AccountUserInfo> accountUserInfoList);
|
ThirdApiRes saveBatchAccountInfo(List<AccountUserInfo> accountUserInfoList);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商家内部注册(服务之间调用)
|
||||||
|
*
|
||||||
|
* @param mobile 商家手机号
|
||||||
|
* @param regPwd 注册密码
|
||||||
|
* @return Pair<Boolean, String> 第一个元素表示是否成功,第二个元素表示提示信息
|
||||||
|
*/
|
||||||
|
Pair<Boolean, String> merchantInnerRegister(String mobile, String regPwd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更改分店商家的手机号(仅限于分店,尽量不要更改总店的手机号),项目之间远程调用
|
||||||
|
*
|
||||||
|
* @param oldMobile 老的手机号
|
||||||
|
* @param newMobile 新的手机号
|
||||||
|
* @param password 新的登录密码(可选)
|
||||||
|
* @return Pair<Boolean, String> 操作结果和提示信息,true表示成功,false表示失败
|
||||||
|
*/
|
||||||
|
Pair<Boolean, String> changeMerchantLoginMobile(String oldMobile, String newMobile, String password);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量保存accountInfo
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
ThirdApiRes saveBatchAccountUserLogin(List<AccountUserLogin> accountUserLoginList);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import cn.hutool.json.JSONObject;
|
|||||||
import com.suisung.mall.common.modules.account.AccountUserBindConnect;
|
import com.suisung.mall.common.modules.account.AccountUserBindConnect;
|
||||||
import com.suisung.mall.common.pojo.req.WxUserInfoReq;
|
import com.suisung.mall.common.pojo.req.WxUserInfoReq;
|
||||||
import com.suisung.mall.core.web.service.IBaseService;
|
import com.suisung.mall.core.web.service.IBaseService;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
@ -89,9 +90,17 @@ public interface AccountUserBindConnectService extends IBaseService<AccountUserB
|
|||||||
*/
|
*/
|
||||||
AccountUserBindConnect initAccountUserBindConnect(String bindId, Integer bindType, Integer userId, Integer userType);
|
AccountUserBindConnect initAccountUserBindConnect(String bindId, Integer bindType, Integer userId, Integer userType);
|
||||||
|
|
||||||
List<AccountUserBindConnect> getAllBindPage(String bindTmpl,Integer pageNum, Integer pageSize);
|
List<AccountUserBindConnect> getAllBindPage(String bindTmpl, Integer pageNum, Integer pageSize);
|
||||||
|
|
||||||
long getAllBindCount(String bindTmpl);
|
long getAllBindCount(String bindTmpl);
|
||||||
|
|
||||||
void bindTmplId(JSONObject jsonObject);
|
void bindTmplId(JSONObject jsonObject);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查手机注册的商家是否存在
|
||||||
|
*
|
||||||
|
* @param mobile
|
||||||
|
* @return 1-存在;2-不存在;3-参数有误
|
||||||
|
*/
|
||||||
|
Integer isMerchantExists(String mobile);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -97,7 +97,7 @@ import static com.suisung.mall.common.utils.I18nUtil._;
|
|||||||
@Service
|
@Service
|
||||||
public class AccountUserBaseServiceImpl extends BaseServiceImpl<AccountUserBaseMapper, AccountUserBase> implements AccountUserBaseService {
|
public class AccountUserBaseServiceImpl extends BaseServiceImpl<AccountUserBaseMapper, AccountUserBase> implements AccountUserBaseService {
|
||||||
|
|
||||||
private final String VERIFY_CODE_KEY = "register:verifyCode:";
|
private final String VERIFY_CODE_KEY = RedisConstant.VERIFY_CODE_KEY; //"register:verifyCode:";
|
||||||
private final Logger logger = LoggerFactory.getLogger(AccountUserBaseServiceImpl.class);
|
private final Logger logger = LoggerFactory.getLogger(AccountUserBaseServiceImpl.class);
|
||||||
@Autowired
|
@Autowired
|
||||||
private AuthService authService;
|
private AuthService authService;
|
||||||
@ -1715,8 +1715,6 @@ public class AccountUserBaseServiceImpl extends BaseServiceImpl<AccountUserBaseM
|
|||||||
|
|
||||||
|
|
||||||
// 有手机参数,优先手机注册
|
// 有手机参数,优先手机注册
|
||||||
// if ((StrUtil.isNotBlank(user_mobile))
|
|
||||||
// || (CheckUtil.isMobile(user_account, user_intl) && StrUtil.equals(user_account, user_mobile))) {
|
|
||||||
if (StrUtil.isNotBlank(user_mobile) && CheckUtil.isMobile(user_mobile, user_intl)) {
|
if (StrUtil.isNotBlank(user_mobile) && CheckUtil.isMobile(user_mobile, user_intl)) {
|
||||||
|
|
||||||
if (StrUtil.isNotBlank(rand_key) && StrUtil.isNotBlank(verifyCode)) {
|
if (StrUtil.isNotBlank(rand_key) && StrUtil.isNotBlank(verifyCode)) {
|
||||||
@ -1801,7 +1799,7 @@ public class AccountUserBaseServiceImpl extends BaseServiceImpl<AccountUserBaseM
|
|||||||
throw new ApiException(_("绑定账号失败"));
|
throw new ApiException(_("绑定账号失败"));
|
||||||
}
|
}
|
||||||
return user_base_row;
|
return user_base_row;
|
||||||
// throw new ApiException(_("用户已经存在,请更换用户名"));
|
|
||||||
} else {
|
} else {
|
||||||
// 检测到用户尚未注册,立即新增用户基本信息和用户附加信息
|
// 检测到用户尚未注册,立即新增用户基本信息和用户附加信息
|
||||||
String user_nickname = Convert.toStr(userInfo.get("user_nickname"), user_account);
|
String user_nickname = Convert.toStr(userInfo.get("user_nickname"), user_account);
|
||||||
@ -1816,7 +1814,6 @@ public class AccountUserBaseServiceImpl extends BaseServiceImpl<AccountUserBaseM
|
|||||||
// 密码加盐加密
|
// 密码加盐加密
|
||||||
user_password = SecureUtil.md5(user_salt + SecureUtil.md5(user_password));
|
user_password = SecureUtil.md5(user_salt + SecureUtil.md5(user_password));
|
||||||
user_base_reg_row.setUser_password(user_password);
|
user_base_reg_row.setUser_password(user_password);
|
||||||
// user_base_reg_row.setUser_password((BCrypt.hashpw(user_password)));
|
|
||||||
user_base_reg_row.setUser_nickname(user_nickname);
|
user_base_reg_row.setUser_nickname(user_nickname);
|
||||||
user_base_reg_row.setUser_state(user_state);
|
user_base_reg_row.setUser_state(user_state);
|
||||||
user_base_reg_row.setUser_key(user_key);
|
user_base_reg_row.setUser_key(user_key);
|
||||||
@ -1900,16 +1897,17 @@ public class AccountUserBaseServiceImpl extends BaseServiceImpl<AccountUserBaseM
|
|||||||
throw new ApiException(ResultCode.FAILED);
|
throw new ApiException(ResultCode.FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 远程调用异常忽略掉
|
// 远程调用异常忽略掉
|
||||||
try {
|
try {
|
||||||
// User_Resource初始化 用户积分
|
// User_Resource初始化 用户积分
|
||||||
if (!payService.initUserPoints(user_id)) {
|
if (!payService.initUserPoints(user_id)) {
|
||||||
|
log.error("初始化用户积分失败");
|
||||||
// throw new ApiException(ResultCode.FAILED);
|
// throw new ApiException(ResultCode.FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化用户经验表
|
// 初始化用户经验表
|
||||||
if (!accountUserInfoService.initUserExperience(user_id)) {
|
if (!accountUserInfoService.initUserExperience(user_id)) {
|
||||||
|
log.error("初始化用户经验表失败");
|
||||||
// throw new ApiException(ResultCode.FAILED);
|
// throw new ApiException(ResultCode.FAILED);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1925,7 +1923,6 @@ public class AccountUserBaseServiceImpl extends BaseServiceImpl<AccountUserBaseM
|
|||||||
addSourceUserId(user_id, user_parent_id);
|
addSourceUserId(user_id, user_parent_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
//todo 分享券
|
|
||||||
// 分享券活动id
|
// 分享券活动id
|
||||||
Integer activityId = Convert.toInt(getParameter("activity_id"));
|
Integer activityId = Convert.toInt(getParameter("activity_id"));
|
||||||
if (CheckUtil.isEmpty(activityId) && CheckUtil.isNotEmpty(activity_id)) {
|
if (CheckUtil.isEmpty(activityId) && CheckUtil.isNotEmpty(activity_id)) {
|
||||||
@ -1953,7 +1950,7 @@ public class AccountUserBaseServiceImpl extends BaseServiceImpl<AccountUserBaseM
|
|||||||
// 如果是商家首次注册 恭喜您,成功注册了商家账号!您的初始密码为:${password},密码可登录商家版 APP,登录后请尽快修改密码。
|
// 如果是商家首次注册 恭喜您,成功注册了商家账号!您的初始密码为:${password},密码可登录商家版 APP,登录后请尽快修改密码。
|
||||||
Map<String, Object> smsArgs = new HashMap<>();
|
Map<String, Object> smsArgs = new HashMap<>();
|
||||||
smsArgs.put("password", user_password_src);
|
smsArgs.put("password", user_password_src);
|
||||||
sendSmsMessage(PhoneNumberUtils.cleanPhoneNumber(user_mobile), "SMS_496910525", smsArgs); // SMS_496910525
|
sendSmsMessage(PhoneNumberUtils.cleanPhoneNumber(user_mobile), "SMS_496910525", smsArgs);
|
||||||
}
|
}
|
||||||
|
|
||||||
//初次注册发送消息
|
//初次注册发送消息
|
||||||
@ -3221,6 +3218,20 @@ public class AccountUserBaseServiceImpl extends BaseServiceImpl<AccountUserBaseM
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public CommonResult doMobileBindLogin(String user_mobile, Integer userType, String cid, String osType) {
|
public CommonResult doMobileBindLogin(String user_mobile, Integer userType, String cid, String osType) {
|
||||||
|
return doMobileBindLogin(user_mobile, "", userType, cid, osType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* (商家版app)入驻商家手机号绑定登录
|
||||||
|
*
|
||||||
|
* @param user_mobile
|
||||||
|
* @param regPwd 注册用途
|
||||||
|
* @param userType
|
||||||
|
* @param cid
|
||||||
|
* @param osType
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public CommonResult doMobileBindLogin(String user_mobile, String regPwd, Integer userType, String cid, String osType) {
|
||||||
if (StrUtil.isBlank(user_mobile) || userType == null) {
|
if (StrUtil.isBlank(user_mobile) || userType == null) {
|
||||||
return CommonResult.failed("缺少必要参数!");
|
return CommonResult.failed("缺少必要参数!");
|
||||||
}
|
}
|
||||||
@ -3247,8 +3258,11 @@ public class AccountUserBaseServiceImpl extends BaseServiceImpl<AccountUserBaseM
|
|||||||
userInfo.put("user_account", user_mobile);
|
userInfo.put("user_account", user_mobile);
|
||||||
userInfo.put("user_mobile", user_mobile);
|
userInfo.put("user_mobile", user_mobile);
|
||||||
userInfo.put("user_is_admin", userType); // 商家入驻注册
|
userInfo.put("user_is_admin", userType); // 商家入驻注册
|
||||||
// 随机数明文密码
|
String user_password = regPwd;
|
||||||
String user_password = com.suisung.mall.common.utils.StringUtils.random(6, com.suisung.mall.common.utils.StringUtils.RandomType.STRING);
|
if (StrUtil.isBlank(user_password)) {
|
||||||
|
// 随机数明文密码
|
||||||
|
user_password = com.suisung.mall.common.utils.StringUtils.random(6, com.suisung.mall.common.utils.StringUtils.RandomType.STRING);
|
||||||
|
}
|
||||||
userInfo.put("user_password", user_password);
|
userInfo.put("user_password", user_password);
|
||||||
userInfo.put("is_admin", userType); // 商家入驻注册
|
userInfo.put("is_admin", userType); // 商家入驻注册
|
||||||
|
|
||||||
@ -3267,17 +3281,162 @@ public class AccountUserBaseServiceImpl extends BaseServiceImpl<AccountUserBaseM
|
|||||||
params.put("username", accountUserBase.getUser_account());
|
params.put("username", accountUserBase.getUser_account());
|
||||||
params.put("password", "");
|
params.put("password", "");
|
||||||
params.put("user_mobile", user_mobile);
|
params.put("user_mobile", user_mobile);
|
||||||
params.put("is_merch", "1"); // 是否为商家入驻 1-是;其他-否
|
params.put("is_merch", CommonConstant.Enable.toString()); // 是否为商家入驻 1-是;其他-否
|
||||||
|
|
||||||
if (StrUtil.isNotBlank(cid)) {
|
if (StrUtil.isNotBlank(cid)) {
|
||||||
params.put("cid", cid); // 个推客户端Id
|
params.put("cid", cid); // 个推客户端Id
|
||||||
params.put("os_type", osType);// 个推客系统类别 1-Android;2-iOS;3-微信小程序;
|
params.put("os_type", osType);// 个推客系统类别 1-Android;2-iOS;3-微信小程序;
|
||||||
// logger.info("推送参数2 cid:{}, osType:{}", cid, osType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return login(params);
|
return login(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 商家内部注册(服务之间调用)
|
||||||
|
*
|
||||||
|
* @param mobile 商家手机号
|
||||||
|
* @param regPwd 注册明文密码
|
||||||
|
* @return Pair<Boolean, String> 第一个元素表示是否成功,第二个元素表示提示信息
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Pair<Boolean, String> merchantInnerRegister(String mobile, String regPwd) {
|
||||||
|
log.info("商家内部注册开始,手机号: {}", mobile);
|
||||||
|
|
||||||
|
// 1. 参数校验
|
||||||
|
if (StrUtil.isBlank(mobile)) {
|
||||||
|
log.warn("商家内部注册失败:手机号为空");
|
||||||
|
return Pair.of(false, "手机号不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 2. 转换手机号格式(添加国家区号)
|
||||||
|
String convertedMobile = PhoneNumberUtils.convZhPhoneNumber(mobile);
|
||||||
|
|
||||||
|
// 3. 检查手机号是否已注册为商家
|
||||||
|
Integer isExists = accountUserBindConnectService.isMerchantExists(convertedMobile);
|
||||||
|
if (CommonConstant.Enable.equals(isExists)) {
|
||||||
|
log.warn("商家内部注册失败:手机号已注册,手机号: {}", convertedMobile);
|
||||||
|
return Pair.of(false, "该手机号已注册!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 执行手机号绑定登录(注册商家账号)
|
||||||
|
CommonResult result = doMobileBindLogin(convertedMobile, regPwd, CommonConstant.USER_TYPE_MCH, null, null);
|
||||||
|
|
||||||
|
// 5. 检查注册结果
|
||||||
|
if (result == null || result.getCode() != ResultCode.SUCCESS.getCode()) {
|
||||||
|
String errorMsg = result != null ? result.getMsg() : "注册结果为空";
|
||||||
|
log.error("商家账号注册失败:{},手机号: {}", errorMsg, convertedMobile);
|
||||||
|
return Pair.of(false, "商家账号注册失败:" + errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("商家内部注册成功,手机号: {}", convertedMobile);
|
||||||
|
return Pair.of(true, "商家注册成功");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("商家内部注册异常,手机号: {}", mobile, e);
|
||||||
|
return Pair.of(false, "商家注册失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更改分店商家的手机号(仅限于分店,尽量不要更改总店的手机号),项目之间远程调用
|
||||||
|
*
|
||||||
|
* @param oldMobile 老的手机号
|
||||||
|
* @param newMobile 新的手机号
|
||||||
|
* @param password 新的登录密码(可选)
|
||||||
|
* @return Pair<Boolean, String> 操作结果和提示信息,true表示成功,false表示失败
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Pair<Boolean, String> changeMerchantLoginMobile(String oldMobile, String newMobile, String password) {
|
||||||
|
log.debug("开始更改商家登录手机号,老手机号:{},新手机号:{},是否需要设置密码:{}", oldMobile, newMobile, StrUtil.isNotBlank(password));
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 验证输入参数
|
||||||
|
if (StrUtil.isBlank(oldMobile) || StrUtil.isBlank(newMobile)) {
|
||||||
|
log.warn("手机号参数为空,老手机号:{},新手机号:{}", oldMobile, newMobile);
|
||||||
|
return Pair.of(false, "手机号不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化手机号,添加国际区号
|
||||||
|
String formattedOldMobile = PhoneNumberUtils.convZhPhoneNumber(oldMobile);
|
||||||
|
String formattedNewMobile = PhoneNumberUtils.convZhPhoneNumber(newMobile);
|
||||||
|
log.debug("格式化后的手机号:老:{} 新:{}", formattedOldMobile, formattedNewMobile);
|
||||||
|
|
||||||
|
// 查询老手机号是否绑定商家账号
|
||||||
|
AccountUserBindConnect bindConnect = bindConnectService.getBindByBindId(formattedOldMobile, BindCode.MOBILE, CommonConstant.USER_TYPE_MCH);
|
||||||
|
if (bindConnect == null) {
|
||||||
|
log.warn("该老手机号未绑定商家账号,手机号:{}", formattedOldMobile);
|
||||||
|
return Pair.of(false, "该老手机号未绑定商家账号,无法进行更换");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
|
Integer userId = bindConnect.getUser_id();
|
||||||
|
log.debug("绑定记录中的用户ID:{}", userId);
|
||||||
|
|
||||||
|
AccountUserBase userBase = get(userId);
|
||||||
|
if (userBase == null) {
|
||||||
|
log.warn("用户信息不存在,用户ID:{}", userId);
|
||||||
|
return Pair.of(false, "用户信息不存在,无法进行更换");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证用户确实是商家类型
|
||||||
|
if (!userBase.getUser_is_admin().equals(CommonConstant.USER_TYPE_MCH)) {
|
||||||
|
log.warn("该账号不是商家账号,用户ID:{},用户类型:{}", userId, userBase.getUser_is_admin());
|
||||||
|
return Pair.of(false, "该账号不是商家账号,无法进行更换");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查目标手机号是否已被其他商家账号使用
|
||||||
|
AccountUserBase existingNewUser = getByAccountAndType(formattedNewMobile, CommonConstant.USER_TYPE_MCH);
|
||||||
|
if (existingNewUser != null && !existingNewUser.getUser_id().equals(userId)) {
|
||||||
|
log.warn("目标手机号已被其他商家账号使用,手机号:{},用户ID:{}", formattedNewMobile, existingNewUser.getUser_id());
|
||||||
|
return Pair.of(false, "该手机号已被其他商家账号使用");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查新手机号是否已被其他商家绑定
|
||||||
|
AccountUserBindConnect newBindConnect = bindConnectService.getBindByBindId(formattedNewMobile, BindCode.MOBILE, CommonConstant.USER_TYPE_MCH);
|
||||||
|
if (newBindConnect != null && !newBindConnect.getUser_id().equals(userId)) {
|
||||||
|
log.warn("该新手机号已被其他商家账号绑定,手机号:{},用户ID:{}", formattedNewMobile, newBindConnect.getUser_id());
|
||||||
|
return Pair.of(false, "该手机号已被其他商家账号绑定");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新用户账号为新手机号
|
||||||
|
log.debug("开始更新用户账号信息,用户ID:{},新手机号:{}", userId, formattedNewMobile);
|
||||||
|
userBase.setUser_account(formattedNewMobile);
|
||||||
|
|
||||||
|
// 如果提供了密码,则更新密码
|
||||||
|
if (StrUtil.isNotBlank(password)) {
|
||||||
|
log.debug("需要更新用户密码");
|
||||||
|
String user_salt = IdUtil.simpleUUID();
|
||||||
|
String encryptedPassword = SecureUtil.md5(user_salt + SecureUtil.md5(password));
|
||||||
|
userBase.setUser_password(encryptedPassword);
|
||||||
|
userBase.setUser_salt(user_salt);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新用户基础信息
|
||||||
|
boolean updateUserResult = saveOrUpdate(userBase);
|
||||||
|
if (!updateUserResult) {
|
||||||
|
log.error("更新用户账号信息失败,用户ID:{}", userId);
|
||||||
|
return Pair.of(false, "更新用户账号信息失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新绑定表中的bind_id为新手机号
|
||||||
|
log.debug("开始更新绑定信息,原绑定ID:{},新绑定ID:{}", bindConnect.getBind_id(), formattedNewMobile);
|
||||||
|
bindConnect.setBind_id(formattedNewMobile);
|
||||||
|
boolean updateBindResult = accountUserBindConnectService.saveOrUpdate(bindConnect);
|
||||||
|
if (!updateBindResult) {
|
||||||
|
log.error("更新绑定信息失败,绑定ID:{}", formattedNewMobile);
|
||||||
|
return Pair.of(false, "更新绑定信息失败");
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("更换商家登录手机号成功,用户ID:{},新手机号:{}", userId, formattedNewMobile);
|
||||||
|
return Pair.of(true, "更换商家登录手机号成功");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("更改商家登录手机号过程中发生异常,老手机号:{},新手机号:{}", oldMobile, newMobile, e);
|
||||||
|
return Pair.of(false, "系统异常:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> doAppConnectLogin(String bind_name, String code) {
|
public Map<String, Object> doAppConnectLogin(String bind_name, String code) {
|
||||||
String id_prefix = "";
|
String id_prefix = "";
|
||||||
@ -3803,6 +3962,23 @@ public class AccountUserBaseServiceImpl extends BaseServiceImpl<AccountUserBaseM
|
|||||||
return new ThirdApiRes().fail(250, "保存异常");
|
return new ThirdApiRes().fail(250, "保存异常");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ThirdApiRes saveBatchAccountUserLogin(List<AccountUserLogin> accountUserLoginList) {
|
||||||
|
boolean result = false;
|
||||||
|
if (!accountUserLoginList.isEmpty()) {
|
||||||
|
try {
|
||||||
|
result = accountUserLoginService.saveBatch(accountUserLoginList, accountUserLoginList.size());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("保存AccountUserLogin报错:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (result) {
|
||||||
|
return new ThirdApiRes().success("成功");
|
||||||
|
}
|
||||||
|
return new ThirdApiRes().fail(250, "保存异常");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查并修复商户店铺信息
|
* 检查并修复商户店铺信息
|
||||||
* 重要(补偿机制),检查修复商户入驻店铺信息(商家账号关联入驻店铺Id,给商家账户创立公司员工账号和权限)
|
* 重要(补偿机制),检查修复商户入驻店铺信息(商家账号关联入驻店铺Id,给商家账户创立公司员工账号和权限)
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import cn.hutool.core.util.ObjectUtil;
|
|||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.json.JSONArray;
|
import cn.hutool.json.JSONArray;
|
||||||
import cn.hutool.json.JSONObject;
|
import cn.hutool.json.JSONObject;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||||
import com.suisung.mall.account.mapper.AccountUserBindConnectMapper;
|
import com.suisung.mall.account.mapper.AccountUserBindConnectMapper;
|
||||||
@ -19,6 +20,7 @@ import com.suisung.mall.common.modules.account.AccountUserBase;
|
|||||||
import com.suisung.mall.common.modules.account.AccountUserBindConnect;
|
import com.suisung.mall.common.modules.account.AccountUserBindConnect;
|
||||||
import com.suisung.mall.common.modules.account.AccountUserInfo;
|
import com.suisung.mall.common.modules.account.AccountUserInfo;
|
||||||
import com.suisung.mall.common.pojo.req.WxUserInfoReq;
|
import com.suisung.mall.common.pojo.req.WxUserInfoReq;
|
||||||
|
import com.suisung.mall.common.utils.CheckUtil;
|
||||||
import com.suisung.mall.common.utils.I18nUtil;
|
import com.suisung.mall.common.utils.I18nUtil;
|
||||||
import com.suisung.mall.common.utils.StringUtils;
|
import com.suisung.mall.common.utils.StringUtils;
|
||||||
import com.suisung.mall.common.utils.phone.PhoneNumberUtils;
|
import com.suisung.mall.common.utils.phone.PhoneNumberUtils;
|
||||||
@ -367,6 +369,65 @@ public class AccountUserBindConnectServiceImpl extends BaseServiceImpl<AccountUs
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查手机注册的商家是否存在
|
||||||
|
*
|
||||||
|
* @param mobile
|
||||||
|
* @return 1-已存在 2-不存在 3-格式错误
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public Integer isMerchantExists(String mobile) {
|
||||||
|
log.debug("检查商家是否存在,手机号: {}", mobile);
|
||||||
|
|
||||||
|
if (StrUtil.isBlank(mobile)) {
|
||||||
|
log.warn("手机号为空,无法检查商家是否存在");
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
String convertedMobile = PhoneNumberUtils.convZhPhoneNumber(mobile);
|
||||||
|
if (CheckUtil.isEmpty(convertedMobile)) {
|
||||||
|
log.warn("手机号格式无效: {}", mobile);
|
||||||
|
return 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 使用正确的绑定类型检查手机号
|
||||||
|
LambdaQueryWrapper<AccountUserBindConnect> queryWrapper = new LambdaQueryWrapper<>();
|
||||||
|
queryWrapper.eq(AccountUserBindConnect::getBind_id, convertedMobile)
|
||||||
|
.eq(AccountUserBindConnect::getBind_type, BindCode.MOBILE) // 1-手机号
|
||||||
|
.eq(AccountUserBindConnect::getUser_type, CommonConstant.USER_TYPE_MCH) // 2-入驻商家
|
||||||
|
.eq(AccountUserBindConnect::getBind_active, CommonConstant.Enable)
|
||||||
|
.orderByAsc(AccountUserBindConnect::getBind_time)
|
||||||
|
.select(AccountUserBindConnect::getUser_id);
|
||||||
|
|
||||||
|
log.debug("执行查询,转换后的手机号: {}, 查询条件: {}", convertedMobile, queryWrapper.getSqlSegment());
|
||||||
|
|
||||||
|
AccountUserBindConnect accountUserBindConnect = getOne(queryWrapper);
|
||||||
|
if (accountUserBindConnect == null || CheckUtil.isEmpty(accountUserBindConnect.getUser_id())) {
|
||||||
|
log.info("未找到手机号绑定的商家记录,手机号: {}", convertedMobile);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
Integer userId = accountUserBindConnect.getUser_id();
|
||||||
|
log.debug("找到用户ID: {}", userId);
|
||||||
|
|
||||||
|
AccountUserBase accountUserBase = accountUserBaseService.get(userId);
|
||||||
|
if (accountUserBase == null) {
|
||||||
|
log.warn("用户基础信息不存在,用户ID: {}", userId);
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("商家存在,用户ID: {}", userId);
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("查询商家绑定信息时发生异常,手机号: {}", mobile, e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户绑定手机号和openid
|
* 用户绑定手机号和openid
|
||||||
*
|
*
|
||||||
@ -505,24 +566,24 @@ public class AccountUserBindConnectServiceImpl extends BaseServiceImpl<AccountUs
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<AccountUserBindConnect> getAllBindPage(String bindTmpl,Integer pageNum, Integer pageSize) {
|
public List<AccountUserBindConnect> getAllBindPage(String bindTmpl, Integer pageNum, Integer pageSize) {
|
||||||
QueryWrapper<AccountUserBindConnect> queryWrapper = new QueryWrapper<>();
|
QueryWrapper<AccountUserBindConnect> queryWrapper = new QueryWrapper<>();
|
||||||
queryWrapper.select("bind_id,bind_type,user_id,user_type");
|
queryWrapper.select("bind_id,bind_type,user_id,user_type");
|
||||||
queryWrapper.in("bind_type", Arrays.asList(BindCode.MOBILE,BindCode.WEIXIN_XCX))
|
queryWrapper.in("bind_type", Arrays.asList(BindCode.MOBILE, BindCode.WEIXIN_XCX))
|
||||||
.eq("user_type", CommonConstant.USER_TYPE_NORMAL)
|
.eq("user_type", CommonConstant.USER_TYPE_NORMAL)
|
||||||
.eq("bind_active", CommonConstant.Enable)
|
.eq("bind_active", CommonConstant.Enable)
|
||||||
.eq("bind_tmpl",bindTmpl)
|
.eq("bind_tmpl", bindTmpl)
|
||||||
.orderByAsc("bind_time");
|
.orderByAsc("bind_time");
|
||||||
return this.lists(queryWrapper,pageNum,pageSize).getRecords();
|
return this.lists(queryWrapper, pageNum, pageSize).getRecords();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public long getAllBindCount(String bindTmpl) {
|
public long getAllBindCount(String bindTmpl) {
|
||||||
QueryWrapper<AccountUserBindConnect> queryWrapper = new QueryWrapper<>();
|
QueryWrapper<AccountUserBindConnect> queryWrapper = new QueryWrapper<>();
|
||||||
queryWrapper.in("bind_type", Arrays.asList(BindCode.MOBILE,BindCode.WEIXIN_XCX))
|
queryWrapper.in("bind_type", Arrays.asList(BindCode.MOBILE, BindCode.WEIXIN_XCX))
|
||||||
.eq("user_type", CommonConstant.USER_TYPE_NORMAL)
|
.eq("user_type", CommonConstant.USER_TYPE_NORMAL)
|
||||||
.eq("bind_active", CommonConstant.Enable)
|
.eq("bind_active", CommonConstant.Enable)
|
||||||
.eq("bind_tmpl",bindTmpl)
|
.eq("bind_tmpl", bindTmpl)
|
||||||
.orderByAsc("bind_time");
|
.orderByAsc("bind_time");
|
||||||
return this.count(queryWrapper);
|
return this.count(queryWrapper);
|
||||||
}
|
}
|
||||||
@ -537,42 +598,42 @@ public class AccountUserBindConnectServiceImpl extends BaseServiceImpl<AccountUs
|
|||||||
// log.info("错误信息--{}",e.getMessage());
|
// log.info("错误信息--{}",e.getMessage());
|
||||||
// }
|
// }
|
||||||
log.info("jsonObject:{}", jsonObject);
|
log.info("jsonObject:{}", jsonObject);
|
||||||
String openId=jsonObject.getStr("FromUserName");//用户openid
|
String openId = jsonObject.getStr("FromUserName");//用户openid
|
||||||
QueryWrapper<AccountUserBindConnect> queryWrapper = new QueryWrapper<>();
|
QueryWrapper<AccountUserBindConnect> queryWrapper = new QueryWrapper<>();
|
||||||
queryWrapper.eq("bind_openid",openId)
|
queryWrapper.eq("bind_openid", openId)
|
||||||
.eq("user_type", CommonConstant.USER_TYPE_NORMAL)
|
.eq("user_type", CommonConstant.USER_TYPE_NORMAL)
|
||||||
.in("bind_type",Arrays.asList(1,15))
|
.in("bind_type", Arrays.asList(1, 15))
|
||||||
.eq("bind_active", CommonConstant.Enable);
|
.eq("bind_active", CommonConstant.Enable);
|
||||||
List<AccountUserBindConnect> accountUserBindConnectList= list(queryWrapper);
|
List<AccountUserBindConnect> accountUserBindConnectList = list(queryWrapper);
|
||||||
if (!accountUserBindConnectList.isEmpty()) {
|
if (!accountUserBindConnectList.isEmpty()) {
|
||||||
AccountUserBindConnect accountUserBindConnect=accountUserBindConnectList.get(0);
|
AccountUserBindConnect accountUserBindConnect = accountUserBindConnectList.get(0);
|
||||||
JSONArray jsonArray=null;
|
JSONArray jsonArray = null;
|
||||||
JSONObject object= null;
|
JSONObject object = null;
|
||||||
try {
|
try {
|
||||||
jsonArray= jsonObject.getJSONArray("List");
|
jsonArray = jsonObject.getJSONArray("List");
|
||||||
}catch (RuntimeException runtimeException){
|
} catch (RuntimeException runtimeException) {
|
||||||
log.info("单个模板");
|
log.info("单个模板");
|
||||||
object=jsonObject.getJSONObject("List");
|
object = jsonObject.getJSONObject("List");
|
||||||
}
|
}
|
||||||
UpdateWrapper<AccountUserBindConnect> updateWrapper = new UpdateWrapper<>();
|
UpdateWrapper<AccountUserBindConnect> updateWrapper = new UpdateWrapper<>();
|
||||||
if (jsonArray != null) {
|
if (jsonArray != null) {
|
||||||
object= (JSONObject) jsonArray.get(0);
|
object = (JSONObject) jsonArray.get(0);
|
||||||
String templateId= object.getStr("TemplateId");//模板id
|
String templateId = object.getStr("TemplateId");//模板id
|
||||||
String SubscribeStatusString= object.getStr("SubscribeStatusString");//订阅结果(accept接收;reject拒收)参考地址https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/subscribe-message.html#%E8%AE%A2%E9%98%85%E6%B6%88%E6%81%AF%E8%AF%AD%E9%9F%B3%E6%8F%90%E9%86%92
|
String SubscribeStatusString = object.getStr("SubscribeStatusString");//订阅结果(accept接收;reject拒收)参考地址https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/subscribe-message.html#%E8%AE%A2%E9%98%85%E6%B6%88%E6%81%AF%E8%AF%AD%E9%9F%B3%E6%8F%90%E9%86%92
|
||||||
if(SubscribeStatusString.equals("accept")){
|
if (SubscribeStatusString.equals("accept")) {
|
||||||
accountUserBindConnect.setBind_tmpl(templateId);
|
accountUserBindConnect.setBind_tmpl(templateId);
|
||||||
getUpdateWrapper(updateWrapper, accountUserBindConnect);
|
getUpdateWrapper(updateWrapper, accountUserBindConnect);
|
||||||
}
|
}
|
||||||
}else {
|
} else {
|
||||||
assert object != null;
|
assert object != null;
|
||||||
String templateId= object.getStr("TemplateId");//模板id
|
String templateId = object.getStr("TemplateId");//模板id
|
||||||
String SubscribeStatusString= object.getStr("SubscribeStatusString");
|
String SubscribeStatusString = object.getStr("SubscribeStatusString");
|
||||||
if(StringUtils.isNotEmpty(SubscribeStatusString)&&SubscribeStatusString.equals("accept")){
|
if (StringUtils.isNotEmpty(SubscribeStatusString) && SubscribeStatusString.equals("accept")) {
|
||||||
accountUserBindConnect.setBind_tmpl(templateId);
|
accountUserBindConnect.setBind_tmpl(templateId);
|
||||||
getUpdateWrapper(updateWrapper, accountUserBindConnect);
|
getUpdateWrapper(updateWrapper, accountUserBindConnect);
|
||||||
}else {
|
} else {
|
||||||
String ErrorStatus=object.getStr("ErrorStatus");
|
String ErrorStatus = object.getStr("ErrorStatus");
|
||||||
if(ErrorStatus.equals("success")){
|
if (ErrorStatus.equals("success")) {
|
||||||
log.info("消息推送成功给用户,不需要操作");
|
log.info("消息推送成功给用户,不需要操作");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -584,16 +645,17 @@ public class AccountUserBindConnectServiceImpl extends BaseServiceImpl<AccountUs
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 封装相同参数的调用方法
|
* 封装相同参数的调用方法
|
||||||
|
*
|
||||||
* @param updateWrapper
|
* @param updateWrapper
|
||||||
* @param accountUserBindConnect
|
* @param accountUserBindConnect
|
||||||
*/
|
*/
|
||||||
private void getUpdateWrapper(UpdateWrapper<AccountUserBindConnect> updateWrapper,
|
private void getUpdateWrapper(UpdateWrapper<AccountUserBindConnect> updateWrapper,
|
||||||
AccountUserBindConnect accountUserBindConnect) {
|
AccountUserBindConnect accountUserBindConnect) {
|
||||||
updateWrapper.set("bind_tmpl",accountUserBindConnect.getBind_tmpl());
|
updateWrapper.set("bind_tmpl", accountUserBindConnect.getBind_tmpl());
|
||||||
updateWrapper.eq("bind_id",accountUserBindConnect.getBind_id());
|
updateWrapper.eq("bind_id", accountUserBindConnect.getBind_id());
|
||||||
updateWrapper.eq("bind_type",accountUserBindConnect.getBind_type());
|
updateWrapper.eq("bind_type", accountUserBindConnect.getBind_type());
|
||||||
updateWrapper.eq("user_id",accountUserBindConnect.getUser_id());
|
updateWrapper.eq("user_id", accountUserBindConnect.getUser_id());
|
||||||
updateWrapper.eq("user_type",accountUserBindConnect.getUser_type());
|
updateWrapper.eq("user_type", accountUserBindConnect.getUser_type());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -99,7 +99,7 @@ logging:
|
|||||||
suisung:
|
suisung:
|
||||||
mall:
|
mall:
|
||||||
account:
|
account:
|
||||||
mapper: debug
|
mapper: info
|
||||||
sun:
|
sun:
|
||||||
mail: error
|
mail: error
|
||||||
baomidou: error
|
baomidou: error
|
||||||
|
|||||||
@ -1,13 +1,19 @@
|
|||||||
package com.suisung.mall.admin.controller.admin;
|
package com.suisung.mall.admin.controller.admin;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.suisung.mall.admin.service.AdminBaseMenuService;
|
import com.suisung.mall.admin.service.AdminBaseMenuService;
|
||||||
import com.suisung.mall.common.api.CommonResult;
|
import com.suisung.mall.common.api.CommonResult;
|
||||||
|
import com.suisung.mall.common.domain.UserDto;
|
||||||
|
import com.suisung.mall.common.exception.ApiException;
|
||||||
import com.suisung.mall.common.modules.admin.AdminBaseMenu;
|
import com.suisung.mall.common.modules.admin.AdminBaseMenu;
|
||||||
import com.suisung.mall.common.modules.admin.Router;
|
import com.suisung.mall.common.modules.admin.Router;
|
||||||
|
import com.suisung.mall.common.utils.ContextUtil;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
|
import io.swagger.annotations.ApiOperation;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,6 +44,18 @@ public class AdminBaseMenuController {
|
|||||||
return CommonResult.success(routers);
|
return CommonResult.success(routers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户权限目录信息
|
||||||
|
*
|
||||||
|
* @param
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@RequestMapping(value = "/treePage", method = RequestMethod.GET)
|
||||||
|
public IPage<AdminBaseMenu> getRouterList(AdminBaseMenu adminBaseMenu, @RequestParam(name = "pageNum", defaultValue = "1") Integer pageNum,
|
||||||
|
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
|
||||||
|
return adminBaseMenuService.treePage(adminBaseMenu,pageNum,pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取所有菜单配置
|
* 获取所有菜单配置
|
||||||
*
|
*
|
||||||
@ -83,6 +101,22 @@ public class AdminBaseMenuController {
|
|||||||
return adminBaseMenuService.updateAdminBaseMenu(adminBaseMenu);
|
return adminBaseMenuService.updateAdminBaseMenu(adminBaseMenu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量删除
|
||||||
|
*
|
||||||
|
* @param menu_ids
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation(value = "权限表 -批量删除", notes = "权限表 -批量删除")
|
||||||
|
@RequestMapping(value = "/deleteBatch", method = RequestMethod.DELETE)
|
||||||
|
public CommonResult deleteBatch(String menu_ids) {
|
||||||
|
UserDto userDto= ContextUtil.getCurrentUser();
|
||||||
|
if(userDto.getRole_id()!=9){
|
||||||
|
throw new ApiException("权限不足");
|
||||||
|
}
|
||||||
|
boolean flag = adminBaseMenuService.remove(Arrays.asList(menu_ids.split(",")));
|
||||||
|
return CommonResult.success(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,17 +1,16 @@
|
|||||||
package com.suisung.mall.admin.controller.admin;
|
package com.suisung.mall.admin.controller.admin;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
|
||||||
import com.suisung.mall.admin.service.AdminBaseProtocolService;
|
import com.suisung.mall.admin.service.AdminBaseProtocolService;
|
||||||
import com.suisung.mall.common.api.CommonResult;
|
import com.suisung.mall.common.api.CommonResult;
|
||||||
|
import com.suisung.mall.common.domain.UserDto;
|
||||||
|
import com.suisung.mall.common.exception.ApiException;
|
||||||
import com.suisung.mall.common.modules.admin.AdminBaseProtocol;
|
import com.suisung.mall.common.modules.admin.AdminBaseProtocol;
|
||||||
|
import com.suisung.mall.common.utils.ContextUtil;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
|
||||||
import org.springframework.web.bind.annotation.RequestParam;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@ -43,15 +42,11 @@ public class AdminBaseProtocolController {
|
|||||||
*/
|
*/
|
||||||
@ApiOperation(value = "基础通信协议表-分页列表查询", notes = "基础通信协议表-分页列表查询")
|
@ApiOperation(value = "基础通信协议表-分页列表查询", notes = "基础通信协议表-分页列表查询")
|
||||||
@RequestMapping(value = "/list", method = RequestMethod.GET)
|
@RequestMapping(value = "/list", method = RequestMethod.GET)
|
||||||
public CommonResult list(@RequestParam(name = "queryWrapper", required = false) AdminBaseProtocol adminBaseProtocol,
|
public CommonResult list(AdminBaseProtocol adminBaseProtocol,
|
||||||
@RequestParam(name = "pageNum", defaultValue = "1") Integer pageNum,
|
@RequestParam(name = "pageNum", defaultValue = "1") Integer pageNum,
|
||||||
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
|
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
|
||||||
|
|
||||||
QueryWrapper<AdminBaseProtocol> queryWrapper = new QueryWrapper<>();
|
return CommonResult.success(adminBaseProtocolService.getPageAdminBaseProtocol(adminBaseProtocol, pageNum, pageSize));
|
||||||
queryWrapper.orderByAsc("cmd_id");
|
|
||||||
|
|
||||||
IPage<AdminBaseProtocol> pageList = adminBaseProtocolService.lists(queryWrapper, pageNum, pageSize);
|
|
||||||
return CommonResult.success(pageList);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,9 +56,13 @@ public class AdminBaseProtocolController {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ApiOperation(value = "基础通信协议表-编辑", notes = "基础通信协议表-编辑")
|
@ApiOperation(value = "基础通信协议表-编辑", notes = "基础通信协议表-编辑")
|
||||||
@RequestMapping(value = "/edit", method = RequestMethod.POST)
|
@RequestMapping(value = "/edit", method = RequestMethod.PUT)
|
||||||
public CommonResult edit(AdminBaseProtocol adminBaseProtocol) {
|
public CommonResult edit(@RequestBody AdminBaseProtocol adminBaseProtocol) {
|
||||||
boolean flag = adminBaseProtocolService.edit(adminBaseProtocol);
|
UserDto userDto= ContextUtil.getCurrentUser();
|
||||||
|
if(userDto.getRole_id()!=9){
|
||||||
|
throw new ApiException("权限不足");
|
||||||
|
}
|
||||||
|
boolean flag = adminBaseProtocolService.editAdminBaseProtocol(adminBaseProtocol);
|
||||||
return CommonResult.success(flag);
|
return CommonResult.success(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,9 +86,13 @@ public class AdminBaseProtocolController {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ApiOperation(value = "基础通信协议表-批量删除", notes = "基础通信协议表-批量删除")
|
@ApiOperation(value = "基础通信协议表-批量删除", notes = "基础通信协议表-批量删除")
|
||||||
@RequestMapping(value = "/deleteBatch", method = RequestMethod.POST)
|
@RequestMapping(value = "/deleteBatch", method = RequestMethod.DELETE)
|
||||||
public CommonResult deleteBatch(@RequestParam(name = "cmd_ids") String cmd_ids) {
|
public CommonResult deleteBatch(@RequestParam(name = "cmd_ids") String cmd_ids) {
|
||||||
boolean flag = adminBaseProtocolService.removeByIds(Arrays.asList(cmd_ids.split(",")));
|
UserDto userDto= ContextUtil.getCurrentUser();
|
||||||
|
if(userDto.getRole_id()!=9){
|
||||||
|
throw new ApiException("权限不足");
|
||||||
|
}
|
||||||
|
boolean flag = adminBaseProtocolService.deleteBatchByIds(Arrays.asList(cmd_ids.split(",")));
|
||||||
return CommonResult.success(flag);
|
return CommonResult.success(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,10 +114,25 @@ public class AdminBaseProtocolController {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ApiOperation(value = "基础通信协议表-新增", notes = "基础通信协议表-新增")
|
@ApiOperation(value = "基础通信协议表-新增", notes = "基础通信协议表-新增")
|
||||||
@RequestMapping(value = "/add", method = RequestMethod.PUT)
|
@RequestMapping(value = "/add", method = RequestMethod.POST)
|
||||||
public CommonResult add(AdminBaseProtocol adminBaseProtocol) {
|
public CommonResult add(@RequestBody AdminBaseProtocol adminBaseProtocol) {
|
||||||
boolean flag = adminBaseProtocolService.save(adminBaseProtocol);
|
UserDto userDto= ContextUtil.getCurrentUser();
|
||||||
|
if(userDto.getRole_id()!=9){
|
||||||
|
throw new ApiException("权限不足");
|
||||||
|
}
|
||||||
|
boolean flag = adminBaseProtocolService.addAdminBaseProtocol(adminBaseProtocol);
|
||||||
return CommonResult.success(flag);
|
return CommonResult.success(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据id查出路由,主要是显示平台权限名称和店铺权限名称
|
||||||
|
* @param cmd_id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation(value = "基础通信协议表-查找单个协议", notes = "基础通信协议表-查找单个协议")
|
||||||
|
@RequestMapping(value = "/getOneById", method = RequestMethod.GET)
|
||||||
|
public CommonResult getOneById(@RequestParam(name = "cmd_id") Integer cmd_id) {
|
||||||
|
return CommonResult.success(adminBaseProtocolService.getOneByCmId(cmd_id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,11 +1,15 @@
|
|||||||
package com.suisung.mall.admin.controller.admin;
|
package com.suisung.mall.admin.controller.admin;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.suisung.mall.admin.service.AdminRightsBaseService;
|
import com.suisung.mall.admin.service.AdminRightsBaseService;
|
||||||
import com.suisung.mall.common.api.CommonResult;
|
import com.suisung.mall.common.api.CommonResult;
|
||||||
|
import com.suisung.mall.common.domain.UserDto;
|
||||||
|
import com.suisung.mall.common.exception.ApiException;
|
||||||
import com.suisung.mall.common.modules.admin.AdminRightsBase;
|
import com.suisung.mall.common.modules.admin.AdminRightsBase;
|
||||||
import com.suisung.mall.common.modules.admin.ElTree;
|
import com.suisung.mall.common.modules.admin.ElTree;
|
||||||
|
import com.suisung.mall.common.utils.ContextUtil;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@ -40,7 +44,7 @@ public class AdminRightsBaseController {
|
|||||||
*/
|
*/
|
||||||
@ApiOperation(value = "权限表 -分页列表查询", notes = "权限表 -分页列表查询")
|
@ApiOperation(value = "权限表 -分页列表查询", notes = "权限表 -分页列表查询")
|
||||||
@RequestMapping(value = "/list", method = RequestMethod.GET)
|
@RequestMapping(value = "/list", method = RequestMethod.GET)
|
||||||
public CommonResult list(@RequestParam(name = "queryWrapper", required = false) AdminRightsBase adminRightsBase,
|
public CommonResult list(AdminRightsBase adminRightsBase,
|
||||||
@RequestParam(name = "pageNum", defaultValue = "1") Integer pageNum,
|
@RequestParam(name = "pageNum", defaultValue = "1") Integer pageNum,
|
||||||
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
|
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
|
||||||
|
|
||||||
@ -51,6 +55,21 @@ public class AdminRightsBaseController {
|
|||||||
return CommonResult.success(pageList);
|
return CommonResult.success(pageList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 树形分页列表查询
|
||||||
|
* @param adminRightsBase
|
||||||
|
* @param pageNum
|
||||||
|
* @param pageSize
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation(value = "权限表 -树形分页列表查询", notes = "权限表 -树形分页列表查询")
|
||||||
|
@RequestMapping(value = "/treeList", method = RequestMethod.GET)
|
||||||
|
public IPage<AdminRightsBase> treeList(AdminRightsBase adminRightsBase,
|
||||||
|
@RequestParam(name = "pageNum", defaultValue = "1") Integer pageNum,
|
||||||
|
@RequestParam(name = "pageSize", defaultValue = "10") Integer pageSize) {
|
||||||
|
return adminRightsBaseService.treePage(adminRightsBase, pageNum, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取tree数据
|
* 获取tree数据
|
||||||
@ -78,8 +97,12 @@ public class AdminRightsBaseController {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ApiOperation(value = "权限表 -编辑", notes = "权限表 -编辑")
|
@ApiOperation(value = "权限表 -编辑", notes = "权限表 -编辑")
|
||||||
@RequestMapping(value = "/edit", method = RequestMethod.POST)
|
@RequestMapping(value = "/edit", method = RequestMethod.PUT)
|
||||||
public CommonResult edit(AdminRightsBase adminRightsBase) {
|
public CommonResult edit(@RequestBody AdminRightsBase adminRightsBase) {
|
||||||
|
UserDto userDto= ContextUtil.getCurrentUser();
|
||||||
|
if(userDto.getRole_id()!=9){
|
||||||
|
throw new ApiException("权限不足");
|
||||||
|
}
|
||||||
boolean flag = adminRightsBaseService.edit(adminRightsBase);
|
boolean flag = adminRightsBaseService.edit(adminRightsBase);
|
||||||
return CommonResult.success(flag);
|
return CommonResult.success(flag);
|
||||||
}
|
}
|
||||||
@ -94,6 +117,10 @@ public class AdminRightsBaseController {
|
|||||||
@ApiOperation(value = "权限表 -通过rights_id删除", notes = "权限表 -通过rights_id删除")
|
@ApiOperation(value = "权限表 -通过rights_id删除", notes = "权限表 -通过rights_id删除")
|
||||||
@RequestMapping(value = "/delete", method = RequestMethod.POST)
|
@RequestMapping(value = "/delete", method = RequestMethod.POST)
|
||||||
public CommonResult delete(@RequestParam(name = "rights_id") String rights_id) {
|
public CommonResult delete(@RequestParam(name = "rights_id") String rights_id) {
|
||||||
|
UserDto userDto= ContextUtil.getCurrentUser();
|
||||||
|
if(userDto.getRole_id()!=9){
|
||||||
|
throw new ApiException("权限不足");
|
||||||
|
}
|
||||||
boolean flag = adminRightsBaseService.remove(rights_id);
|
boolean flag = adminRightsBaseService.remove(rights_id);
|
||||||
return CommonResult.success(flag);
|
return CommonResult.success(flag);
|
||||||
}
|
}
|
||||||
@ -105,8 +132,12 @@ public class AdminRightsBaseController {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@ApiOperation(value = "权限表 -批量删除", notes = "权限表 -批量删除")
|
@ApiOperation(value = "权限表 -批量删除", notes = "权限表 -批量删除")
|
||||||
@RequestMapping(value = "/deleteBatch", method = RequestMethod.POST)
|
@RequestMapping(value = "/deleteBatch", method = RequestMethod.DELETE)
|
||||||
public CommonResult deleteBatch(@RequestParam(name = "rights_ids") String rights_ids) {
|
public CommonResult deleteBatch(String rights_ids) {
|
||||||
|
UserDto userDto= ContextUtil.getCurrentUser();
|
||||||
|
if(userDto.getRole_id()!=9){
|
||||||
|
throw new ApiException("权限不足");
|
||||||
|
}
|
||||||
boolean flag = this.adminRightsBaseService.remove(Arrays.asList(rights_ids.split(",")));
|
boolean flag = this.adminRightsBaseService.remove(Arrays.asList(rights_ids.split(",")));
|
||||||
return CommonResult.success(flag);
|
return CommonResult.success(flag);
|
||||||
}
|
}
|
||||||
@ -120,6 +151,13 @@ public class AdminRightsBaseController {
|
|||||||
@ApiOperation(value = "权限表 -新增", notes = "权限表 -编辑")
|
@ApiOperation(value = "权限表 -新增", notes = "权限表 -编辑")
|
||||||
@RequestMapping(value = "/add", method = RequestMethod.POST)
|
@RequestMapping(value = "/add", method = RequestMethod.POST)
|
||||||
public CommonResult add(@RequestBody AdminRightsBase adminRightsBase) {
|
public CommonResult add(@RequestBody AdminRightsBase adminRightsBase) {
|
||||||
|
UserDto userDto= ContextUtil.getCurrentUser();
|
||||||
|
if(userDto.getRole_id()!=9){
|
||||||
|
throw new ApiException("权限不足");
|
||||||
|
}
|
||||||
|
if(ObjectUtil.isEmpty(adminRightsBase.getRights_parent_id())){
|
||||||
|
adminRightsBase.setRights_parent_id(0);
|
||||||
|
}
|
||||||
boolean flag = adminRightsBaseService.save(adminRightsBase);
|
boolean flag = adminRightsBaseService.save(adminRightsBase);
|
||||||
return CommonResult.success(flag);
|
return CommonResult.success(flag);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -30,6 +30,7 @@ import com.suisung.mall.common.pojo.dto.OssCallbackResultDTO;
|
|||||||
import com.suisung.mall.common.pojo.dto.OssPolicyResultDTO;
|
import com.suisung.mall.common.pojo.dto.OssPolicyResultDTO;
|
||||||
import com.suisung.mall.common.utils.I18nUtil;
|
import com.suisung.mall.common.utils.I18nUtil;
|
||||||
import com.suisung.mall.common.utils.LogUtil;
|
import com.suisung.mall.common.utils.LogUtil;
|
||||||
|
import com.suisung.mall.common.utils.UploadUtil;
|
||||||
import com.suisung.mall.common.utils.VideoUtil;
|
import com.suisung.mall.common.utils.VideoUtil;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -169,6 +170,9 @@ public class OssServiceImpl implements OssService {
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public Map upload(MultipartFile file, UserDto user, String dir, String uploadPath, String uploadName, String fileName) {
|
public Map upload(MultipartFile file, UserDto user, String dir, String uploadPath, String uploadName, String fileName) {
|
||||||
|
// 如果是图片,先压缩图片,再上传到 cos 对象存储
|
||||||
|
file = UploadUtil.compressImageIfNeeded(file, 2048);
|
||||||
|
|
||||||
// 创建临时文件
|
// 创建临时文件
|
||||||
creTempFile(file, dir, uploadName);
|
creTempFile(file, dir, uploadName);
|
||||||
String url = null;
|
String url = null;
|
||||||
@ -248,7 +252,7 @@ public class OssServiceImpl implements OssService {
|
|||||||
tempFile.delete();
|
tempFile.delete();
|
||||||
logger.info("临时文件已删除: {}", uploadPath);
|
logger.info("临时文件已删除: {}", uploadPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 同时删除可能创建的封面文件
|
// 同时删除可能创建的封面文件
|
||||||
String coverPath = uploadPath.replace("." + VideoUtil.getVideoFormat(uploadPath), ".jpg");
|
String coverPath = uploadPath.replace("." + VideoUtil.getVideoFormat(uploadPath), ".jpg");
|
||||||
File coverFile = new File(coverPath);
|
File coverFile = new File(coverPath);
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.suisung.mall.admin.service;
|
package com.suisung.mall.admin.service;
|
||||||
|
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.suisung.mall.common.api.CommonResult;
|
import com.suisung.mall.common.api.CommonResult;
|
||||||
import com.suisung.mall.common.modules.admin.AdminBaseMenu;
|
import com.suisung.mall.common.modules.admin.AdminBaseMenu;
|
||||||
import com.suisung.mall.common.modules.admin.Router;
|
import com.suisung.mall.common.modules.admin.Router;
|
||||||
@ -32,4 +33,6 @@ public interface AdminBaseMenuService extends IBaseService<AdminBaseMenu> {
|
|||||||
CommonResult saveAdminBaseMenu(AdminBaseMenu adminBaseMenu);
|
CommonResult saveAdminBaseMenu(AdminBaseMenu adminBaseMenu);
|
||||||
|
|
||||||
CommonResult updateAdminBaseMenu(AdminBaseMenu adminBaseMenu);
|
CommonResult updateAdminBaseMenu(AdminBaseMenu adminBaseMenu);
|
||||||
|
|
||||||
|
IPage<AdminBaseMenu> treePage(AdminBaseMenu adminBaseMenu, Integer pageNum, Integer pageSize);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.suisung.mall.admin.service;
|
package com.suisung.mall.admin.service;
|
||||||
|
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.suisung.mall.common.modules.admin.AdminBaseProtocol;
|
import com.suisung.mall.common.modules.admin.AdminBaseProtocol;
|
||||||
import com.suisung.mall.core.web.service.IBaseService;
|
import com.suisung.mall.core.web.service.IBaseService;
|
||||||
|
|
||||||
@ -21,4 +22,17 @@ public interface AdminBaseProtocolService extends IBaseService<AdminBaseProtocol
|
|||||||
* 初始化资源角色规则
|
* 初始化资源角色规则
|
||||||
*/
|
*/
|
||||||
Map<String, List<String>> initResourceRolesMap();
|
Map<String, List<String>> initResourceRolesMap();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询路由
|
||||||
|
*/
|
||||||
|
IPage<AdminBaseProtocol> getPageAdminBaseProtocol(AdminBaseProtocol adminBaseProtocol,Integer pageNum,Integer pageSize);
|
||||||
|
|
||||||
|
boolean addAdminBaseProtocol(AdminBaseProtocol adminBaseProtocol);
|
||||||
|
|
||||||
|
boolean editAdminBaseProtocol(AdminBaseProtocol adminBaseProtocol);
|
||||||
|
|
||||||
|
boolean deleteBatchByIds(List<String> adminBaseProtocolIds);
|
||||||
|
|
||||||
|
AdminBaseProtocol getOneByCmId(Integer cmId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.suisung.mall.admin.service;
|
package com.suisung.mall.admin.service;
|
||||||
|
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.suisung.mall.common.modules.admin.AdminRightsBase;
|
import com.suisung.mall.common.modules.admin.AdminRightsBase;
|
||||||
import com.suisung.mall.common.modules.admin.ElTree;
|
import com.suisung.mall.common.modules.admin.ElTree;
|
||||||
import com.suisung.mall.core.web.service.IBaseService;
|
import com.suisung.mall.core.web.service.IBaseService;
|
||||||
@ -21,4 +22,9 @@ public interface AdminRightsBaseService extends IBaseService<AdminRightsBase> {
|
|||||||
* 获取tree数据
|
* 获取tree数据
|
||||||
*/
|
*/
|
||||||
List<ElTree> tree();
|
List<ElTree> tree();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取tree数据
|
||||||
|
*/
|
||||||
|
IPage<AdminRightsBase> treePage(AdminRightsBase adminRightsBase, Integer pageNum, Integer pageSize);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import cn.hutool.core.convert.Convert;
|
|||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||||
import com.suisung.mall.admin.mapper.AdminBaseMenuMapper;
|
import com.suisung.mall.admin.mapper.AdminBaseMenuMapper;
|
||||||
import com.suisung.mall.admin.service.AccountBaseConfigService;
|
import com.suisung.mall.admin.service.AccountBaseConfigService;
|
||||||
@ -870,6 +871,16 @@ public class AdminBaseMenuServiceImpl extends BaseServiceImpl<AdminBaseMenuMappe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommonResult saveAdminBaseMenu(AdminBaseMenu adminBaseMenu) {
|
public CommonResult saveAdminBaseMenu(AdminBaseMenu adminBaseMenu) {
|
||||||
|
UserDto userDto= ContextUtil.getCurrentUser();
|
||||||
|
if(userDto.getRole_id()!=9){
|
||||||
|
throw new ApiException("没有权限");
|
||||||
|
}
|
||||||
|
if(ObjectUtil.isEmpty(adminBaseMenu.getMenu_parent_id())){
|
||||||
|
adminBaseMenu.setMenu_parent_id(0);
|
||||||
|
}
|
||||||
|
if(ObjectUtil.isEmpty(adminBaseMenu.getMenu_time())){
|
||||||
|
adminBaseMenu.setMenu_time(new Date());
|
||||||
|
}
|
||||||
if (!save(adminBaseMenu)) {
|
if (!save(adminBaseMenu)) {
|
||||||
return CommonResult.failed("新增失败");
|
return CommonResult.failed("新增失败");
|
||||||
}
|
}
|
||||||
@ -878,12 +889,61 @@ public class AdminBaseMenuServiceImpl extends BaseServiceImpl<AdminBaseMenuMappe
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CommonResult updateAdminBaseMenu(AdminBaseMenu adminBaseMenu) {
|
public CommonResult updateAdminBaseMenu(AdminBaseMenu adminBaseMenu) {
|
||||||
|
UserDto userDto= ContextUtil.getCurrentUser();
|
||||||
|
if(userDto.getRole_id()!=9){
|
||||||
|
throw new ApiException("没有权限");
|
||||||
|
}
|
||||||
|
if(ObjectUtil.isEmpty(adminBaseMenu.getMenu_time())){
|
||||||
|
adminBaseMenu.setMenu_time(new Date());
|
||||||
|
}
|
||||||
if (!edit(adminBaseMenu)) {
|
if (!edit(adminBaseMenu)) {
|
||||||
return CommonResult.failed("更新失败");
|
return CommonResult.failed("更新失败");
|
||||||
}
|
}
|
||||||
return CommonResult.success("更新成功");
|
return CommonResult.success("更新成功");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPage<AdminBaseMenu> treePage(AdminBaseMenu adminBaseMenu, Integer pageNum, Integer pageSize) {
|
||||||
|
UserDto userDto= ContextUtil.getCurrentUser();
|
||||||
|
if(userDto.getRole_id()!=9){
|
||||||
|
throw new ApiException("没有权限");
|
||||||
|
}
|
||||||
|
QueryWrapper<AdminBaseMenu> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.orderByDesc("menu_id");
|
||||||
|
if(adminBaseMenu!=null){
|
||||||
|
if(StringUtils.isNotEmpty(adminBaseMenu.getMenu_name())){
|
||||||
|
queryWrapper.like("menu_name", adminBaseMenu.getMenu_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
queryWrapper.eq("menu_parent_id",0);
|
||||||
|
IPage<AdminBaseMenu> pageList = this.lists(queryWrapper, pageNum, pageSize);
|
||||||
|
List<AdminBaseMenu> adminBaseMenus= pageList.getRecords();
|
||||||
|
adminBaseMenus=buildAdminBaseMenuTree(adminBaseMenus,0);
|
||||||
|
pageList.setRecords(adminBaseMenus);
|
||||||
|
return pageList;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 递归构建tree
|
||||||
|
*
|
||||||
|
* @param bases
|
||||||
|
* @param pid
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public List<AdminBaseMenu> buildAdminBaseMenuTree(List<AdminBaseMenu> bases, Integer pid) {
|
||||||
|
List<AdminBaseMenu> tree = new ArrayList<>();
|
||||||
|
bases.forEach(s -> {
|
||||||
|
if (s.getMenu_parent_id().intValue() == pid.intValue()) {
|
||||||
|
QueryWrapper<AdminBaseMenu> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq("menu_parent_id", s.getMenu_id());
|
||||||
|
long countAdminRightsBase=this.count(queryWrapper);
|
||||||
|
if(countAdminRightsBase>0){
|
||||||
|
s.setChildren(buildAdminBaseMenuTree(this.list(queryWrapper), s.getMenu_id()));
|
||||||
|
}
|
||||||
|
tree.add(s);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验是否为平台
|
* 校验是否为平台
|
||||||
|
|||||||
@ -3,18 +3,30 @@ package com.suisung.mall.admin.service.impl;
|
|||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
import cn.hutool.core.util.ObjectUtil;
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.suisung.mall.admin.mapper.AdminBaseProtocolMapper;
|
import com.suisung.mall.admin.mapper.AdminBaseProtocolMapper;
|
||||||
|
import com.suisung.mall.admin.service.AdminBaseMenuService;
|
||||||
import com.suisung.mall.admin.service.AdminBaseProtocolService;
|
import com.suisung.mall.admin.service.AdminBaseProtocolService;
|
||||||
|
import com.suisung.mall.admin.service.AdminRightsBaseService;
|
||||||
import com.suisung.mall.admin.service.AdminRightsGroupService;
|
import com.suisung.mall.admin.service.AdminRightsGroupService;
|
||||||
import com.suisung.mall.common.constant.AuthConstant;
|
import com.suisung.mall.common.constant.AuthConstant;
|
||||||
|
import com.suisung.mall.common.domain.UserDto;
|
||||||
|
import com.suisung.mall.common.exception.ApiException;
|
||||||
|
import com.suisung.mall.common.feignService.ShopService;
|
||||||
|
import com.suisung.mall.common.modules.admin.AdminBaseMenu;
|
||||||
import com.suisung.mall.common.modules.admin.AdminBaseProtocol;
|
import com.suisung.mall.common.modules.admin.AdminBaseProtocol;
|
||||||
|
import com.suisung.mall.common.modules.admin.AdminRightsBase;
|
||||||
import com.suisung.mall.common.modules.admin.AdminRightsGroup;
|
import com.suisung.mall.common.modules.admin.AdminRightsGroup;
|
||||||
|
import com.suisung.mall.common.modules.store.ShopStoreEmployeeRightsBase;
|
||||||
|
import com.suisung.mall.common.utils.ContextUtil;
|
||||||
import com.suisung.mall.core.web.service.RedisService;
|
import com.suisung.mall.core.web.service.RedisService;
|
||||||
import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
|
import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
|
||||||
import org.apache.commons.lang.ArrayUtils;
|
import org.apache.commons.lang.ArrayUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang.StringUtils;
|
||||||
|
import org.hibernate.validator.internal.util.stereotypes.Lazy;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
@ -38,6 +50,17 @@ public class AdminBaseProtocolServiceImpl extends BaseServiceImpl<AdminBaseProto
|
|||||||
@Autowired
|
@Autowired
|
||||||
private RedisService redisService;
|
private RedisService redisService;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Autowired
|
||||||
|
private AdminRightsBaseService adminRightsBaseService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ShopService shopService;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Autowired
|
||||||
|
private AdminBaseMenuService adminBaseMenuService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 插入数据
|
* 插入数据
|
||||||
*
|
*
|
||||||
@ -45,7 +68,15 @@ public class AdminBaseProtocolServiceImpl extends BaseServiceImpl<AdminBaseProto
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean add(AdminBaseProtocol adminBaseProtocol) {
|
public boolean addAdminBaseProtocol(AdminBaseProtocol adminBaseProtocol) {
|
||||||
|
QueryWrapper<AdminBaseProtocol> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq("ctl",adminBaseProtocol.getCtl());
|
||||||
|
queryWrapper.eq("met",adminBaseProtocol.getMet());
|
||||||
|
queryWrapper.eq("mdu",adminBaseProtocol.getMdu());
|
||||||
|
List<AdminBaseProtocol> adminBaseProtocolList=this.list(queryWrapper);
|
||||||
|
if(!adminBaseProtocolList.isEmpty()){
|
||||||
|
throw new ApiException("已存在相同的路由");
|
||||||
|
}
|
||||||
boolean flag = save(adminBaseProtocol);
|
boolean flag = save(adminBaseProtocol);
|
||||||
initResourceRolesMap();
|
initResourceRolesMap();
|
||||||
return flag;
|
return flag;
|
||||||
@ -58,25 +89,83 @@ public class AdminBaseProtocolServiceImpl extends BaseServiceImpl<AdminBaseProto
|
|||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean edit(AdminBaseProtocol adminBaseProtocol) {
|
public boolean editAdminBaseProtocol(AdminBaseProtocol adminBaseProtocol) {
|
||||||
|
QueryWrapper<AdminBaseProtocol> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq("ctl",adminBaseProtocol.getCtl());
|
||||||
|
queryWrapper.eq("met",adminBaseProtocol.getMet());
|
||||||
|
queryWrapper.eq("mdu",adminBaseProtocol.getMdu());
|
||||||
|
List<AdminBaseProtocol> adminBaseProtocolList=this.list(queryWrapper);
|
||||||
|
if(!adminBaseProtocolList.isEmpty()){
|
||||||
|
throw new ApiException("已存在相同的路由");
|
||||||
|
}
|
||||||
boolean flag = updateById(adminBaseProtocol);
|
boolean flag = updateById(adminBaseProtocol);
|
||||||
initResourceRolesMap();
|
initResourceRolesMap();
|
||||||
return flag;
|
return flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据cmd_id删除
|
* 根据cmd_id批量删除
|
||||||
*
|
* @param adminBaseProtocolIds
|
||||||
* @param cmd_id
|
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean remove(Serializable cmd_id) {
|
@Transactional(rollbackFor = Exception.class)
|
||||||
boolean flag = super.remove(cmd_id);
|
public boolean deleteBatchByIds(List<String> adminBaseProtocolIds) {
|
||||||
|
boolean flag = removeBatchByIds(adminBaseProtocolIds);
|
||||||
initResourceRolesMap();
|
initResourceRolesMap();
|
||||||
return flag;
|
return flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AdminBaseProtocol getOneByCmId(Integer cmId) {
|
||||||
|
UserDto userDto= ContextUtil.getCurrentUser();
|
||||||
|
if(userDto.getRole_id()!=9){
|
||||||
|
throw new ApiException("权限不足");
|
||||||
|
}
|
||||||
|
QueryWrapper<AdminBaseProtocol> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq("cmd_id",cmId);
|
||||||
|
List<AdminBaseProtocol> adminBaseProtocolList=this.list(queryWrapper);
|
||||||
|
if(adminBaseProtocolList.isEmpty()){
|
||||||
|
throw new ApiException("不存在通信协议");
|
||||||
|
}
|
||||||
|
AdminBaseProtocol adminBaseProtocol=adminBaseProtocolList.get(0);
|
||||||
|
String rigthsIds= adminBaseProtocol.getRights_id();
|
||||||
|
if(StringUtils.isNotBlank(rigthsIds)){
|
||||||
|
QueryWrapper<AdminBaseMenu> adminBaseMenuQuery = new QueryWrapper<>();
|
||||||
|
adminBaseMenuQuery.eq("menu_url_path",adminBaseProtocol.getPath());
|
||||||
|
List<AdminBaseMenu> adminBaseMenuList= adminBaseMenuService.list(adminBaseMenuQuery);
|
||||||
|
if(adminBaseMenuList.isEmpty()){
|
||||||
|
throw new ApiException("需要配置菜单");
|
||||||
|
}
|
||||||
|
List<String> rightsIdList= Arrays.asList(rigthsIds.split(","));
|
||||||
|
List<Integer> rightsIdsIntList=new ArrayList<>();
|
||||||
|
rightsIdList.forEach(s->rightsIdsIntList.add(Integer.parseInt(s)));
|
||||||
|
QueryWrapper<AdminRightsBase> adminRightsBaseQuery = new QueryWrapper<>();
|
||||||
|
adminRightsBaseQuery.in("rights_id",rightsIdsIntList);
|
||||||
|
List<AdminRightsBase> adminRightsBaseList= adminRightsBaseService.list(adminRightsBaseQuery);
|
||||||
|
AdminBaseMenu adminBaseMenu=adminBaseMenuList.get(0);
|
||||||
|
if(!adminRightsBaseList.isEmpty()){
|
||||||
|
adminRightsBaseList= adminRightsBaseList.stream().filter(s->s.getRights_name().equals(adminBaseMenu.getMenu_name())).collect(Collectors.toList());
|
||||||
|
Map<Integer,String> adminRightBaseMap=adminRightsBaseList.stream().collect(
|
||||||
|
Collectors.toMap(AdminRightsBase::getRights_id,
|
||||||
|
AdminRightsBase::getRights_name));
|
||||||
|
adminBaseProtocol.setAdminRightBaseMap(adminRightBaseMap);
|
||||||
|
}
|
||||||
|
QueryWrapper<ShopStoreEmployeeRightsBase> shopStoreEmployeeRightsBasesQuery = new QueryWrapper<>();
|
||||||
|
shopStoreEmployeeRightsBasesQuery.in("rights_id",rightsIdsIntList);
|
||||||
|
List<ShopStoreEmployeeRightsBase> shopStoreEmployeeRightsBases= shopService.queryByRightsIds(rigthsIds);
|
||||||
|
if(!shopStoreEmployeeRightsBases.isEmpty()){
|
||||||
|
shopStoreEmployeeRightsBases= shopStoreEmployeeRightsBases.stream().filter(s->s.getRights_name().equals(adminBaseMenu.getMenu_name())).collect(Collectors.toList());
|
||||||
|
Map<Integer,String> shopStoreEmployeeRightsBaseMap=shopStoreEmployeeRightsBases.stream().collect(
|
||||||
|
Collectors.toMap(ShopStoreEmployeeRightsBase::getRights_id,
|
||||||
|
ShopStoreEmployeeRightsBase::getRights_name));
|
||||||
|
adminBaseProtocol.setShopStoreEmployeeRightsBaseMap(shopStoreEmployeeRightsBaseMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return adminBaseProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<String>> initResourceRolesMap() {
|
public Map<String, List<String>> initResourceRolesMap() {
|
||||||
// 资源-分组信息
|
// 资源-分组信息
|
||||||
@ -108,6 +197,28 @@ public class AdminBaseProtocolServiceImpl extends BaseServiceImpl<AdminBaseProto
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPage<AdminBaseProtocol> getPageAdminBaseProtocol(AdminBaseProtocol adminBaseProtocol, Integer pageNum, Integer pageSize) {
|
||||||
|
UserDto userDto= ContextUtil.getCurrentUser();
|
||||||
|
if(userDto.getRole_id()!=9){
|
||||||
|
throw new ApiException("权限不足");
|
||||||
|
}
|
||||||
|
QueryWrapper<AdminBaseProtocol> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.orderByAsc("cmd_id");
|
||||||
|
if(adminBaseProtocol!=null){
|
||||||
|
if(StringUtils.isNotBlank(adminBaseProtocol.getComment())){
|
||||||
|
queryWrapper.like("comment", adminBaseProtocol.getComment());
|
||||||
|
}
|
||||||
|
if(StringUtils.isNotBlank(adminBaseProtocol.getCtl())){
|
||||||
|
queryWrapper.like("ctl", adminBaseProtocol.getCtl());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.lists(queryWrapper, pageNum, pageSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 对比两个数组是否有相同元素
|
* 对比两个数组是否有相同元素
|
||||||
*
|
*
|
||||||
|
|||||||
@ -2,11 +2,16 @@ package com.suisung.mall.admin.service.impl;
|
|||||||
|
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
|
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||||
import com.suisung.mall.admin.mapper.AdminRightsBaseMapper;
|
import com.suisung.mall.admin.mapper.AdminRightsBaseMapper;
|
||||||
import com.suisung.mall.admin.service.AccountBaseConfigService;
|
import com.suisung.mall.admin.service.AccountBaseConfigService;
|
||||||
import com.suisung.mall.admin.service.AdminRightsBaseService;
|
import com.suisung.mall.admin.service.AdminRightsBaseService;
|
||||||
|
import com.suisung.mall.common.domain.UserDto;
|
||||||
|
import com.suisung.mall.common.exception.ApiException;
|
||||||
import com.suisung.mall.common.modules.admin.AdminRightsBase;
|
import com.suisung.mall.common.modules.admin.AdminRightsBase;
|
||||||
import com.suisung.mall.common.modules.admin.ElTree;
|
import com.suisung.mall.common.modules.admin.ElTree;
|
||||||
|
import com.suisung.mall.common.utils.ContextUtil;
|
||||||
|
import com.suisung.mall.common.utils.StringUtils;
|
||||||
import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
|
import com.suisung.mall.core.web.service.impl.BaseServiceImpl;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -48,6 +53,59 @@ public class AdminRightsBaseServiceImpl extends BaseServiceImpl<AdminRightsBaseM
|
|||||||
return BuildTree(list, 0);
|
return BuildTree(list, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IPage<AdminRightsBase> treePage(AdminRightsBase adminRightsBase, Integer pageNum, Integer pageSize) {
|
||||||
|
UserDto userDto= ContextUtil.getCurrentUser();
|
||||||
|
if(userDto.getRole_id()!=9){
|
||||||
|
throw new ApiException("没有权限");
|
||||||
|
}
|
||||||
|
QueryWrapper<AdminRightsBase> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.orderByAsc("rights_id");
|
||||||
|
if(adminRightsBase!=null){
|
||||||
|
if(StringUtils.isNotEmpty(adminRightsBase.getRights_name())){
|
||||||
|
queryWrapper.like("rights_name", adminRightsBase.getRights_name());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
queryWrapper.eq("rights_parent_id",0);
|
||||||
|
IPage<AdminRightsBase> pageList = this.lists(queryWrapper, pageNum, pageSize);
|
||||||
|
List<AdminRightsBase> adminRightsBases= pageList.getRecords();
|
||||||
|
adminRightsBases=buildAdminRightBaseTree(adminRightsBases,0);
|
||||||
|
pageList.setRecords(adminRightsBases);
|
||||||
|
return pageList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 递归构建tree
|
||||||
|
*
|
||||||
|
* @param bases
|
||||||
|
* @param pid
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public List<AdminRightsBase> buildAdminRightBaseTree(List<AdminRightsBase> bases, Integer pid) {
|
||||||
|
List<AdminRightsBase> tree = new ArrayList<>();
|
||||||
|
bases.forEach(s -> {
|
||||||
|
if (s.getRights_parent_id().intValue() == pid.intValue()) {
|
||||||
|
AdminRightsBase adminRightsBase=new AdminRightsBase();
|
||||||
|
adminRightsBase.setRights_id(s.getRights_id());
|
||||||
|
adminRightsBase.setRights_name(s.getRights_name());
|
||||||
|
adminRightsBase.setRights_parent_id(s.getRights_parent_id());
|
||||||
|
adminRightsBase.setRights_order(s.getRights_order());
|
||||||
|
adminRightsBase.setRights_remark(s.getRights_remark());
|
||||||
|
adminRightsBase.setMenu_func(s.getMenu_func());
|
||||||
|
// adminRightsBase.setChildren(buildAdminRightBaseTree(bases, s.getRights_id()));
|
||||||
|
QueryWrapper<AdminRightsBase> queryWrapper = new QueryWrapper<>();
|
||||||
|
queryWrapper.eq("rights_parent_id", s.getRights_id());
|
||||||
|
long countAdminRightsBase=this.count(queryWrapper);
|
||||||
|
if(countAdminRightsBase>0){
|
||||||
|
adminRightsBase.setChildren(buildAdminRightBaseTree(this.list(queryWrapper), s.getRights_id()));
|
||||||
|
}
|
||||||
|
tree.add(adminRightsBase);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 递归构建tree
|
* 递归构建tree
|
||||||
*
|
*
|
||||||
|
|||||||
@ -0,0 +1,39 @@
|
|||||||
|
package com.suisung.mall.common.annotation;
|
||||||
|
|
||||||
|
import java.lang.annotation.*;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分布式锁注解
|
||||||
|
*/
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Documented
|
||||||
|
public @interface DistributedLock {
|
||||||
|
/**
|
||||||
|
* 锁的key,支持SpEL表达式,下面是示例
|
||||||
|
* key = "'ACCOUNT_LOCK:' + #batchSize", // 使用SpEL表达式,锁key包含参数
|
||||||
|
* public List<Integer> getBatchUserAccountBaseId(int batchSize)
|
||||||
|
*/
|
||||||
|
String key();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 等待时间(秒),默认0-不等待
|
||||||
|
*/
|
||||||
|
long waitTime() default 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 锁持有时间(秒),默认30秒,-1表示使用看门狗机制
|
||||||
|
*/
|
||||||
|
long leaseTime() default 30;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 时间单位,默认秒
|
||||||
|
*/
|
||||||
|
TimeUnit timeUnit() default TimeUnit.SECONDS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取锁失败时的错误消息
|
||||||
|
*/
|
||||||
|
String errorMsg() default "系统繁忙,请稍后再试";
|
||||||
|
}
|
||||||
@ -15,8 +15,8 @@ public class StateCode {
|
|||||||
public static final int DELIVERY_TYPE_AIR_FREIGHT = 4; //货运(空运、水运、铁路运输、公路运输)
|
public static final int DELIVERY_TYPE_AIR_FREIGHT = 4; //货运(空运、水运、铁路运输、公路运输)
|
||||||
public static final int DELIVERY_TYPE_SELF_PICK_UP = 5; // 自提(运费 0 元)
|
public static final int DELIVERY_TYPE_SELF_PICK_UP = 5; // 自提(运费 0 元)
|
||||||
public static final int DELIVERY_TYPE_EXP = 10; // 普通快递
|
public static final int DELIVERY_TYPE_EXP = 10; // 普通快递
|
||||||
public static final int DELIVERY_TYPE_IN_STORE_SERVICE = 15; // 店铺配送
|
public static final int DELIVERY_TYPE_IN_STORE_SERVICE = 15; // 店铺配送(运费 0 元)
|
||||||
public static final int DELIVERY_TYPE_SAME_CITY = 16;//顺丰同城配送
|
public static final int DELIVERY_TYPE_SAME_CITY = 16;//同城配送
|
||||||
|
|
||||||
public static final Map<Integer, String> DELIVERY_TYPE_MAP = new HashMap() {
|
public static final Map<Integer, String> DELIVERY_TYPE_MAP = new HashMap() {
|
||||||
{
|
{
|
||||||
@ -27,7 +27,7 @@ public class StateCode {
|
|||||||
put(DELIVERY_TYPE_SELF_PICK_UP, "到店自提");
|
put(DELIVERY_TYPE_SELF_PICK_UP, "到店自提");
|
||||||
put(DELIVERY_TYPE_EXP, "普通快递");
|
put(DELIVERY_TYPE_EXP, "普通快递");
|
||||||
put(DELIVERY_TYPE_IN_STORE_SERVICE, "店铺配送");
|
put(DELIVERY_TYPE_IN_STORE_SERVICE, "店铺配送");
|
||||||
put(DELIVERY_TYPE_SAME_CITY, "顺丰同城");
|
put(DELIVERY_TYPE_SAME_CITY, "顺丰同城配送");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -450,6 +450,11 @@ public class StateCode {
|
|||||||
public static final int SF_ORDER_STATUS_EXCEPTION = 91;
|
public static final int SF_ORDER_STATUS_EXCEPTION = 91;
|
||||||
public static final int SF_ORDER_STATUS_CANCELING = 31;
|
public static final int SF_ORDER_STATUS_CANCELING = 31;
|
||||||
|
|
||||||
|
// 退款状态:0-是无退款;1-是部分退款;2-是全部退款
|
||||||
|
public static final int ORDER_REFUND_STATUS_NO = 0;
|
||||||
|
public static final int ORDER_REFUND_STATUS_PART = 1;
|
||||||
|
public static final int ORDER_REFUND_STATUS_ALL = 2;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
DELIVERY_TIME_NOT_TIMER.put(1, I18nUtil._("不限时段"));
|
DELIVERY_TIME_NOT_TIMER.put(1, I18nUtil._("不限时段"));
|
||||||
DELIVERY_TIME_NOT_TIMER.put(15, I18nUtil._("上午"));
|
DELIVERY_TIME_NOT_TIMER.put(15, I18nUtil._("上午"));
|
||||||
|
|||||||
@ -126,10 +126,27 @@ public class CommonConstant {
|
|||||||
public static final Integer Order_Booking_State_YY = 2;
|
public static final Integer Order_Booking_State_YY = 2;
|
||||||
|
|
||||||
// 预约下单从当前时间延迟的最小分钟数(单位分钟),不能低于35分钟
|
// 预约下单从当前时间延迟的最小分钟数(单位分钟),不能低于35分钟
|
||||||
public final static Integer MIN_DELAY_MINUTES_FOR_BOOKING_ORDER = 46;
|
public final static Integer MIN_DELAY_MINUTES_FOR_BOOKING_ORDER = 50;
|
||||||
|
|
||||||
// 预约订单创建提前分钟数(用于提前创建顺丰订单)
|
|
||||||
// public final static Integer MIN_DELAY_MINUTES_FOR_SF_EXPRESS_ORDER = 35;
|
|
||||||
|
|
||||||
|
|
||||||
|
// 店铺营业状态:1-开业营业中(且在营业时间内);12-开业打烊中(但在营业时间外);2-停业中;3-开业(或活动)筹备中;
|
||||||
|
// 1-开业营业中(且在营业时间内)
|
||||||
|
public final static Integer Store_Biz_State_Opening = 1;
|
||||||
|
// 12-开业打烊中(但在营业时间外) ;
|
||||||
|
public final static Integer Store_Biz_State_Opening2 = 12;
|
||||||
|
//2-停业中;
|
||||||
|
public final static Integer Store_Biz_State_Closed = 2;
|
||||||
|
//3-开业(活动)筹备中;
|
||||||
|
public final static Integer Store_Biz_State_PreActivity = 3;
|
||||||
|
|
||||||
|
//用户砍价订单状态:1-砍价已完成下单;
|
||||||
|
public static final Integer CutPrice_Order_State_Finished = 1;
|
||||||
|
//2-砍价未下单已取消;
|
||||||
|
public static final Integer CutPrice_Order_State_Canceled = 2;
|
||||||
|
//3-砍价助力进行中;
|
||||||
|
public static final Integer CutPrice_Order_State_ING = 3;
|
||||||
|
//4-砍价过期失效;
|
||||||
|
public static final Integer CutPrice_Order_State_Expired = 4;
|
||||||
|
//6-砍价助力已完成待下单;
|
||||||
|
public static final Integer CutPrice_Order_State_CutFinished = 6;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,7 @@ public class MqConstant {
|
|||||||
public static final Integer FAILURE = 2; // 消息消费失败更改状态
|
public static final Integer FAILURE = 2; // 消息消费失败更改状态
|
||||||
public static final Integer DELIVERED = 3; // 消息重试消费成功更改状态
|
public static final Integer DELIVERED = 3; // 消息重试消费成功更改状态
|
||||||
public static final Integer MAX_COUNT = 3; // 消息重新投递最大重试次数
|
public static final Integer MAX_COUNT = 3; // 消息重新投递最大重试次数
|
||||||
|
|
||||||
public static final String SHOP_EXCHANGE = "shop-event-exchange"; // SHOP服务交换机
|
public static final String SHOP_EXCHANGE = "shop-event-exchange"; // SHOP服务交换机
|
||||||
public static final String ACCOUNT_EXCHANGE = "account-event-exchange"; // ACCOUNT服务交换机
|
public static final String ACCOUNT_EXCHANGE = "account-event-exchange"; // ACCOUNT服务交换机
|
||||||
public static final String PAY_EXCHANGE = "pay-event-exchange"; // PAY服务交换机
|
public static final String PAY_EXCHANGE = "pay-event-exchange"; // PAY服务交换机
|
||||||
|
|||||||
@ -44,4 +44,6 @@ public class RedisConstant {
|
|||||||
// 预约订单任务新增键
|
// 预约订单任务新增键
|
||||||
public static final String Order_Booking_Task_Key = ConstantRedis.Cache_NameSpace + "new:order:booking:task:";
|
public static final String Order_Booking_Task_Key = ConstantRedis.Cache_NameSpace + "new:order:booking:task:";
|
||||||
|
|
||||||
|
public static final String VERIFY_CODE_KEY = "register:verifyCode:";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import com.suisung.mall.common.pojo.output.TimelineOutput;
|
|||||||
import com.suisung.mall.common.pojo.res.ThirdApiRes;
|
import com.suisung.mall.common.pojo.res.ThirdApiRes;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import org.springframework.cloud.openfeign.FeignClient;
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
|
import org.springframework.data.util.Pair;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
@ -278,6 +279,15 @@ public interface AccountService {
|
|||||||
@PostMapping(value = "/admin/account/accountController/saveBatchAccountInfo")
|
@PostMapping(value = "/admin/account/accountController/saveBatchAccountInfo")
|
||||||
ThirdApiRes saveBatchAccountInfo(@RequestBody List<AccountUserInfo> accountUserInfoList);
|
ThirdApiRes saveBatchAccountInfo(@RequestBody List<AccountUserInfo> accountUserInfoList);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 批量保存accountUserLogin
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@PostMapping(value = "/admin/account/accountController/saveBatchAccountUserLogin")
|
||||||
|
ThirdApiRes saveBatchAccountUserLogin(@RequestBody List<AccountUserLogin> accountUserLoginList);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 批量保存accountInfo
|
* 批量保存accountInfo
|
||||||
*
|
*
|
||||||
@ -303,4 +313,28 @@ public interface AccountService {
|
|||||||
|
|
||||||
@GetMapping(value = "/admin/account/accountController/getAllBindCount")
|
@GetMapping(value = "/admin/account/accountController/getAllBindCount")
|
||||||
long getAllBindCount(@RequestParam(name = "bindTmpl") String bindTmpl);
|
long getAllBindCount(@RequestParam(name = "bindTmpl") String bindTmpl);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查手机注册的商家是否存在(仅供内部调用)
|
||||||
|
*
|
||||||
|
* @param mobile
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@RequestMapping(value = "/admin/account/account-user-bind-connect/is-merchant-exists", method = RequestMethod.POST)
|
||||||
|
Integer isMerchantExists(@RequestParam(name = "mobile") String mobile);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务间注册商家账号,项目之间远程调用
|
||||||
|
*/
|
||||||
|
@RequestMapping(value = "/admin/account/account-user-base/merchant/inner-register", method = RequestMethod.POST)
|
||||||
|
Pair<Boolean, String> merchantInnerRegister(@RequestParam("mobile") String mobile, @RequestParam("regPwd") String regPwd);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务间注册商家账号,项目之间远程调用
|
||||||
|
*/
|
||||||
|
@RequestMapping(value = "/admin/account/account-user-base/merchant/inner-register", method = RequestMethod.POST)
|
||||||
|
Pair<Boolean, String> changeMerchantLoginMobile(@RequestParam("oldMobile") String oldMobile, @RequestParam("newMobile") String newMobile, @RequestParam("password") String password);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,10 +18,7 @@ import com.suisung.mall.common.modules.pay.dto.ItemActivityInfoDTO;
|
|||||||
import com.suisung.mall.common.modules.plantform.ShopPlantformSubsiteUser;
|
import com.suisung.mall.common.modules.plantform.ShopPlantformSubsiteUser;
|
||||||
import com.suisung.mall.common.modules.product.ShopProductBase;
|
import com.suisung.mall.common.modules.product.ShopProductBase;
|
||||||
import com.suisung.mall.common.modules.product.ShopProductIndex;
|
import com.suisung.mall.common.modules.product.ShopProductIndex;
|
||||||
import com.suisung.mall.common.modules.store.ShopStoreBase;
|
import com.suisung.mall.common.modules.store.*;
|
||||||
import com.suisung.mall.common.modules.store.ShopStoreEmployee;
|
|
||||||
import com.suisung.mall.common.modules.store.ShopStoreEmployeeKefu;
|
|
||||||
import com.suisung.mall.common.modules.store.ShopStoreEmployeeRightsGroup;
|
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import org.springframework.cloud.openfeign.FeignClient;
|
import org.springframework.cloud.openfeign.FeignClient;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
@ -326,5 +323,18 @@ public interface ShopService {
|
|||||||
BigDecimal getOrderShippingFee(@RequestParam(name = "order_id") String order_id);
|
BigDecimal getOrderShippingFee(@RequestParam(name = "order_id") String order_id);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取店铺的内部运费 shopping_fee_inner (远程调用用途)
|
||||||
|
*
|
||||||
|
* @param order_id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@ApiOperation(value = "获取店铺的内部运费 shopping_fee_inner", notes = "获取店铺的内部运费 shopping_fee_inner (远程调用用途)")
|
||||||
|
@RequestMapping(value = "/admin/shop/shop-store-info/shopping-fee-inner", method = RequestMethod.POST)
|
||||||
|
Integer storeShoppingFeeInner(@RequestParam(name = "order_id") String order_id);
|
||||||
|
|
||||||
|
|
||||||
|
@GetMapping(value = "/admin/shop/shop-store-employee-rights-base/queryByRightsIds")
|
||||||
|
List<ShopStoreEmployeeRightsBase> queryByRightsIds(@RequestParam(name = "rights_ids") String rights_ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -56,8 +56,20 @@ public class ShopActivityCutprice implements Serializable {
|
|||||||
@ApiModelProperty(value = "砍价人数")
|
@ApiModelProperty(value = "砍价人数")
|
||||||
private Integer ac_num;
|
private Integer ac_num;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "砍价过期时间戳")
|
||||||
|
private Long expired_at;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "用户砍价订单状态:1-砍价已完成下单;2-砍价未下单已取消;3-砍价助力进行中;4-砍价过期失效;6-砍价助力已完成待下单;")
|
||||||
|
private Integer state;
|
||||||
|
|
||||||
@Version
|
@Version
|
||||||
@ApiModelProperty(value = "乐观锁")
|
@ApiModelProperty(value = "乐观锁")
|
||||||
private Integer version;
|
private Integer version;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "更新时间")
|
||||||
|
private Date updated_at;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "大转盘次数")
|
||||||
|
private Integer lottery_num;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,6 @@
|
|||||||
package com.suisung.mall.common.modules.activity;
|
package com.suisung.mall.common.modules.activity;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@ -52,5 +50,14 @@ public class ShopActivityCutpriceHistory implements Serializable {
|
|||||||
@ApiModelProperty(value = "砍价编号")
|
@ApiModelProperty(value = "砍价编号")
|
||||||
private Integer ac_id;
|
private Integer ac_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "摇骰子次数")
|
||||||
|
private Integer lottery_num;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "骰子点数")
|
||||||
|
@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
|
private Integer alh_point;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "转盘前砍掉的价格")
|
||||||
|
@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
|
private BigDecimal ach_price_pre;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.suisung.mall.common.modules.admin;
|
package com.suisung.mall.common.modules.admin;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
@ -11,6 +12,7 @@ import lombok.experimental.Accessors;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@ -105,5 +107,7 @@ public class AdminBaseMenu implements Serializable {
|
|||||||
@ApiModelProperty(value = "允许关闭(BOOL):0-禁止;1-允许")
|
@ApiModelProperty(value = "允许关闭(BOOL):0-禁止;1-允许")
|
||||||
private Integer menu_close;
|
private Integer menu_close;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
@ApiModelProperty(value = "节点子集")
|
||||||
|
private List<AdminBaseMenu> children;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.suisung.mall.common.modules.admin;
|
package com.suisung.mall.common.modules.admin;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
@ -10,6 +11,7 @@ import lombok.EqualsAndHashCode;
|
|||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@ -61,5 +63,11 @@ public class AdminBaseProtocol implements Serializable {
|
|||||||
@ApiModelProperty(value = "请求地址")
|
@ApiModelProperty(value = "请求地址")
|
||||||
private String path;
|
private String path;
|
||||||
|
|
||||||
|
//平台权限id和名称
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Map<Integer,String> adminRightBaseMap;
|
||||||
|
|
||||||
|
//店铺权限id和名称
|
||||||
|
@TableField(exist = false)
|
||||||
|
private Map<Integer,String> shopStoreEmployeeRightsBaseMap;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.suisung.mall.common.modules.admin;
|
package com.suisung.mall.common.modules.admin;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
@ -10,6 +11,7 @@ import lombok.EqualsAndHashCode;
|
|||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@ -47,5 +49,7 @@ public class AdminRightsBase implements Serializable {
|
|||||||
@ApiModelProperty(value = "功能开启:跟设置config_key")
|
@ApiModelProperty(value = "功能开启:跟设置config_key")
|
||||||
private String menu_func;
|
private String menu_func;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "节点子集")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private List<AdminRightsBase> children;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,42 @@
|
|||||||
|
package com.suisung.mall.common.modules.lakala;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 拉卡拉确认收货通知数据日志
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@TableName("lkl_receive_notify_log")
|
||||||
|
@ApiModel(value = "拉卡拉确认收货通知数据日志", description = "拉卡拉确认收货通知数据日志")
|
||||||
|
public class LklReceiveNotifyLog {
|
||||||
|
|
||||||
|
@TableId(value = "id", type = IdType.AUTO)
|
||||||
|
@ApiModelProperty(value = "自增 Id", example = "1")
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单Id")
|
||||||
|
private String orderId = "";
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "通知 JSON 响应数据")
|
||||||
|
private String respJson;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "使用状态:1-被使用过;2-未被使用;")
|
||||||
|
private Integer status = 2;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "创建时间")
|
||||||
|
private Date createdAt;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "更新时间")
|
||||||
|
private Date updatedAt;
|
||||||
|
}
|
||||||
@ -11,6 +11,7 @@ import lombok.NoArgsConstructor;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
@Builder
|
@Builder
|
||||||
@ -28,7 +29,11 @@ public class LibraryProduct implements Serializable {
|
|||||||
@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
|
@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "商品简称,作为匹配关键字", example = "小米12 Pro")
|
||||||
|
private String sname;
|
||||||
|
|
||||||
@ApiModelProperty(value = "商品标题", example = "小米12 Pro")
|
@ApiModelProperty(value = "商品标题", example = "小米12 Pro")
|
||||||
|
@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
private String title;
|
private String title;
|
||||||
|
|
||||||
@ApiModelProperty(value = "条形码/Barcode", example = "6923450657713")
|
@ApiModelProperty(value = "条形码/Barcode", example = "6923450657713")
|
||||||
@ -36,9 +41,11 @@ public class LibraryProduct implements Serializable {
|
|||||||
private String barcode;
|
private String barcode;
|
||||||
|
|
||||||
@ApiModelProperty(value = "第一级分类", example = "生鲜")
|
@ApiModelProperty(value = "第一级分类", example = "生鲜")
|
||||||
|
@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
private String category_1st;
|
private String category_1st;
|
||||||
|
|
||||||
@ApiModelProperty(value = "第二级分类", example = "牛肉")
|
@ApiModelProperty(value = "第二级分类", example = "牛肉")
|
||||||
|
@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
private String category_2nd;
|
private String category_2nd;
|
||||||
|
|
||||||
@ApiModelProperty(value = "商品分类", example = "牛肉")
|
@ApiModelProperty(value = "商品分类", example = "牛肉")
|
||||||
@ -62,7 +69,6 @@ public class LibraryProduct implements Serializable {
|
|||||||
@ApiModelProperty(value = "商品卖点", example = "绿色有机")
|
@ApiModelProperty(value = "商品卖点", example = "绿色有机")
|
||||||
private String selling_point;
|
private String selling_point;
|
||||||
|
|
||||||
|
|
||||||
@ApiModelProperty(value = "商品介绍", example = "商品介绍")
|
@ApiModelProperty(value = "商品介绍", example = "商品介绍")
|
||||||
private String intro;
|
private String intro;
|
||||||
|
|
||||||
@ -92,4 +98,11 @@ public class LibraryProduct implements Serializable {
|
|||||||
@ApiModelProperty(value = "更新时间", example = "2023-01-02 15:30:00")
|
@ApiModelProperty(value = "更新时间", example = "2023-01-02 15:30:00")
|
||||||
@TableField(value = "updated_at")
|
@TableField(value = "updated_at")
|
||||||
private Date updatedAt;
|
private Date updatedAt;
|
||||||
|
|
||||||
|
// @ApiModelProperty(value = "来源ID", example = "vendor_001")
|
||||||
|
// @TableField(value = "product_short_name", updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
|
// private String productShortName;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private List<LibraryProductImage> product_image_list;
|
||||||
}
|
}
|
||||||
@ -9,6 +9,7 @@
|
|||||||
package com.suisung.mall.common.modules.library;
|
package com.suisung.mall.common.modules.library;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
@ -34,13 +35,16 @@ public class LibraryProductImage implements Serializable {
|
|||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
@ApiModelProperty(value = "商品ID", position = 2, example = "1001")
|
@ApiModelProperty(value = "商品ID", position = 2, example = "1001")
|
||||||
|
@TableField(value = "product_id")
|
||||||
private Long productId;
|
private Long productId;
|
||||||
|
|
||||||
@ApiModelProperty(value = "图片地址", position = 3,
|
@ApiModelProperty(value = "图片地址", position = 3,
|
||||||
example = "/media/images/product/1001.jpg")
|
example = "/media/images/product/1001.jpg")
|
||||||
|
@TableField(value = "image_url")
|
||||||
private String imageUrl;
|
private String imageUrl;
|
||||||
|
|
||||||
@ApiModelProperty(value = "是否主图 1-主图 0-副图", position = 4, example = "1")
|
@ApiModelProperty(value = "是否主图 1-主图 0-副图", position = 4, example = "1")
|
||||||
|
@TableField(value = "is_main")
|
||||||
private Boolean isMain;
|
private Boolean isMain;
|
||||||
|
|
||||||
@ApiModelProperty(value = "排序值,越小越前面", position = 5, example = "10")
|
@ApiModelProperty(value = "排序值,越小越前面", position = 5, example = "10")
|
||||||
@ -50,8 +54,10 @@ public class LibraryProductImage implements Serializable {
|
|||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
||||||
@ApiModelProperty(value = "创建时间", position = 7, example = "2023-01-01 12:30:00")
|
@ApiModelProperty(value = "创建时间", position = 7, example = "2023-01-01 12:30:00")
|
||||||
|
@TableField(value = "created_at")
|
||||||
private Date createdAt;
|
private Date createdAt;
|
||||||
|
|
||||||
@ApiModelProperty(value = "更新时间", position = 8, example = "2023-01-01 13:15:00")
|
@ApiModelProperty(value = "更新时间", position = 8, example = "2023-01-01 13:15:00")
|
||||||
|
@TableField(value = "updated_at")
|
||||||
private Date updatedAt;
|
private Date updatedAt;
|
||||||
}
|
}
|
||||||
@ -42,6 +42,9 @@ public class MchOrderInfoDTO implements Serializable {
|
|||||||
// 物流轨迹信息(物流,已发货的才有数据)
|
// 物流轨迹信息(物流,已发货的才有数据)
|
||||||
@ApiModelProperty(value = "物流轨迹信息")
|
@ApiModelProperty(value = "物流轨迹信息")
|
||||||
Map<String, Object> logistics_traces;
|
Map<String, Object> logistics_traces;
|
||||||
|
|
||||||
|
// 退款订单详情
|
||||||
|
MchReturnOrderDetailDTO return_order_detail;
|
||||||
|
|
||||||
// 订单信息
|
// 订单信息
|
||||||
@ApiModelProperty(value = "订单编号")
|
@ApiModelProperty(value = "订单编号")
|
||||||
@ -95,16 +98,12 @@ public class MchOrderInfoDTO implements Serializable {
|
|||||||
private Integer is_deny_return;
|
private Integer is_deny_return;
|
||||||
@ApiModelProperty(value = "异常订单操作流程,0-可操作自行发货,1-可操作订单完成,2-订单完成不可操作")
|
@ApiModelProperty(value = "异常订单操作流程,0-可操作自行发货,1-可操作订单完成,2-订单完成不可操作")
|
||||||
private String operate_flag;
|
private String operate_flag;
|
||||||
|
|
||||||
@ApiModelProperty(value = "订单配送预约状态:1-立即配送;2-预约配送")
|
@ApiModelProperty(value = "订单配送预约状态:1-立即配送;2-预约配送")
|
||||||
private Integer booking_state;
|
private Integer booking_state;
|
||||||
|
|
||||||
@ApiModelProperty(value = "预约送达起始时间,格式如:yyyy-MM-dd HH:mm:ss")
|
@ApiModelProperty(value = "预约送达起始时间,格式如:yyyy-MM-dd HH:mm:ss")
|
||||||
private Date booking_begin_time;
|
private Date booking_begin_time;
|
||||||
|
|
||||||
@ApiModelProperty(value = "预约送达截止时间,格式如:yyyy-MM-dd HH:mm:ss")
|
@ApiModelProperty(value = "预约送达截止时间,格式如:yyyy-MM-dd HH:mm:ss")
|
||||||
private Date booking_end_time;
|
private Date booking_end_time;
|
||||||
|
|
||||||
@ApiModelProperty(value = "预订单到达时间戳(秒)")
|
@ApiModelProperty(value = "预订单到达时间戳(秒)")
|
||||||
private Long booking_at;
|
private Long booking_at;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,193 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||||
|
* Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
|
||||||
|
* Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
|
||||||
|
* Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
|
||||||
|
* Vestibulum commodo. Ut rhoncus gravida arcu.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.suisung.mall.common.modules.order.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@ApiModel(value = "退款单详情主表实体类 对象", description = "商家版退款单详情主表实体类")
|
||||||
|
public class MchReturnOrderDetailDTO implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货单编号")
|
||||||
|
private String return_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单编号")
|
||||||
|
private String order_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "买家用户ID")
|
||||||
|
private Integer buyer_user_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "买家店铺ID")
|
||||||
|
private Integer buyer_store_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货原因ID")
|
||||||
|
private Integer return_reason_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货原因名称")
|
||||||
|
private String return_reason_name;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "买家退货留言")
|
||||||
|
private String return_buyer_message;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "店铺ID")
|
||||||
|
private Integer store_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货退款金额")
|
||||||
|
private BigDecimal return_refund_amount;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货佣金费用")
|
||||||
|
private BigDecimal return_commision_fee;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货状态ID")
|
||||||
|
private Integer return_state_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货状态名称")
|
||||||
|
private String return_state_name;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货联系电话")
|
||||||
|
private String return_tel;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货年份")
|
||||||
|
private Integer return_year;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货月份")
|
||||||
|
private Integer return_month;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货日期")
|
||||||
|
private Integer return_day;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货地址")
|
||||||
|
private String return_addr;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货联系手机")
|
||||||
|
private Long return_mobile;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货联系电话")
|
||||||
|
private String return_telephone;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货联系人姓名")
|
||||||
|
private String return_contact_name;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "分站ID")
|
||||||
|
private Integer subsite_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货申请时间")
|
||||||
|
private Date return_add_time;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货完成时间")
|
||||||
|
private Date return_finish_time;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否已退款(ENUM): 0-未退款; 1-已退款")
|
||||||
|
private Integer return_is_paid;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退款渠道代码")
|
||||||
|
private String return_channel_code;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退款渠道交易ID")
|
||||||
|
private String return_channel_trans_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退款渠道处理时间")
|
||||||
|
private Date return_channel_time;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退款渠道状态(ENUM): 0-待退; 1-已退; 2-异常")
|
||||||
|
private Integer return_channel_flag;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "支付交易号")
|
||||||
|
private String deposit_trade_no;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "支付渠道ID")
|
||||||
|
private Integer payment_channel_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "商家处理留言")
|
||||||
|
private String return_store_message;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "平台退货状态ID")
|
||||||
|
private Integer plantform_return_state_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "平台退货状态名称")
|
||||||
|
private String plantform_return_state_name;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货类型(ENUM): 0-不用退货; 1-需要退货")
|
||||||
|
private Integer return_flag;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "申请类型(ENUM): 1-退款申请; 2-退货申请; 3-虚拟退款")
|
||||||
|
private Integer return_type;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "提交的退货退款金额")
|
||||||
|
private BigDecimal submit_return_refund_amount;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单商品ID列表")
|
||||||
|
private List<Long> order_item_ids;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "买家用户名")
|
||||||
|
private String buyer_user_name;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否有合同类型")
|
||||||
|
private Boolean contract_type_ids;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "尝试退款次数")
|
||||||
|
private Integer try_return_count;
|
||||||
|
|
||||||
|
// 收货地址相关信息
|
||||||
|
@ApiModelProperty(value = "收货省份")
|
||||||
|
private String da_province;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "收货城市")
|
||||||
|
private String da_city;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "收货区县")
|
||||||
|
private String da_county;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "收货详细地址")
|
||||||
|
private String da_address;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "收货人姓名")
|
||||||
|
private String da_name;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "收货人手机号")
|
||||||
|
private String da_mobile;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "收货人电话")
|
||||||
|
private String da_telephone;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "收货省份ID")
|
||||||
|
private Integer da_province_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "收货城市ID")
|
||||||
|
private Integer da_city_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "收货区县ID")
|
||||||
|
private Integer da_county_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "纬度")
|
||||||
|
private String da_latitude;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "经度")
|
||||||
|
private String da_longitude;
|
||||||
|
|
||||||
|
// 退款商品列表
|
||||||
|
@ApiModelProperty(value = "退货商品详情列表")
|
||||||
|
private List<MchReturnOrderItemDetailDTO> items;
|
||||||
|
}
|
||||||
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||||
|
* Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
|
||||||
|
* Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
|
||||||
|
* Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
|
||||||
|
* Vestibulum commodo. Ut rhoncus gravida arcu.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.suisung.mall.common.modules.order.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@ApiModel(value = "退款单商品详情实体对象", description = "退款单商品详情实体类")
|
||||||
|
public class MchReturnOrderItemDetailDTO implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货商品项ID")
|
||||||
|
private Long order_return_item_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货单编号")
|
||||||
|
private String return_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单商品项ID")
|
||||||
|
private Long order_item_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单编号")
|
||||||
|
private String order_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货商品数量")
|
||||||
|
private Integer return_item_num;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货商品小计金额")
|
||||||
|
private BigDecimal return_item_subtotal;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货原因ID")
|
||||||
|
private Integer return_reason_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货商品备注")
|
||||||
|
private String return_item_note;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货商品图片")
|
||||||
|
private String return_item_image;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货商品状态ID")
|
||||||
|
private Integer return_state_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货商品佣金费用")
|
||||||
|
private BigDecimal return_item_commision_fee;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货商品商家备注")
|
||||||
|
private String return_item_store_remark;
|
||||||
|
|
||||||
|
// 关联的订单商品信息
|
||||||
|
@ApiModelProperty(value = "商品名称")
|
||||||
|
private String item_name;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "产品名称")
|
||||||
|
private String product_name;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "货品ID")
|
||||||
|
private Long item_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单商品图片")
|
||||||
|
private String order_item_image;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "单位ID")
|
||||||
|
private Integer unit_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "单位名称")
|
||||||
|
private String unit_name;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单商品单价")
|
||||||
|
private BigDecimal order_item_unit_price;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单商品数量")
|
||||||
|
private Integer order_item_quantity;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单商品总金额")
|
||||||
|
private BigDecimal order_item_amount;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单商品总重量")
|
||||||
|
private BigDecimal order_item_weight_total;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单商品备注")
|
||||||
|
private String order_item_note;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "商品编号")
|
||||||
|
private String item_number;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "产品编号")
|
||||||
|
private String product_number;
|
||||||
|
}
|
||||||
@ -1,8 +1,6 @@
|
|||||||
package com.suisung.mall.common.modules.page;
|
package com.suisung.mall.common.modules.page;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
@ -67,5 +65,22 @@ public class ShopPageApp implements Serializable {
|
|||||||
@ApiModelProperty(value = "是否使用(BOOL):0-否;1-是")
|
@ApiModelProperty(value = "是否使用(BOOL):0-否;1-是")
|
||||||
private Integer app_is_use;
|
private Integer app_is_use;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "模板编号,用于模板市场")
|
||||||
|
private Integer app_template_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否收费0否,1是")
|
||||||
|
private String is_pay;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否发布0否,1是")
|
||||||
|
private String is_pulish;
|
||||||
|
|
||||||
|
@TableField(exist = false)
|
||||||
|
private String tpl_label;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "市场展示图片,[{'imageUrl:'','description':''}")
|
||||||
|
private String app_market_images;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "行业类别:1超市,2数码家电, 3水果生鲜, 4烘培饮品, 5社区团购, 6时尚美妆, 7婴儿服饰, 8家居, 9汽车, 10酒店旅游, 11鲜花绿植, 12医药健康, 13工业五金, 14节日模板,0其他行业")
|
||||||
|
@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
|
private String app_industry;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import lombok.EqualsAndHashCode;
|
|||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@ -135,5 +136,9 @@ public class ShopPageBase implements Serializable {
|
|||||||
@ApiModelProperty(value = "信息发布(BOOL):0-否;1-是")
|
@ApiModelProperty(value = "信息发布(BOOL):0-否;1-是")
|
||||||
private Integer page_message;
|
private Integer page_message;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "创建时间")
|
||||||
|
private Date created_at;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "最后更新时间")
|
||||||
|
private Date updated_at;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,7 +42,7 @@ public class ShopProductImage implements Serializable {
|
|||||||
@ApiModelProperty(value = "规格值")
|
@ApiModelProperty(value = "规格值")
|
||||||
private String color_name;
|
private String color_name;
|
||||||
|
|
||||||
@ApiModelProperty(value = "商品主图")
|
@ApiModelProperty(value = "商品主图,0是编辑空图片,1是同步空图片")
|
||||||
@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
|
@TableField(updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
private String item_image_default;
|
private String item_image_default;
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,53 @@
|
|||||||
|
package com.suisung.mall.common.modules.store;
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 砍价抽奖历史记录DTO
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@TableName("shop_activity_cut_lottery_history")
|
||||||
|
@ApiModel(value = "ShopActivityCutLotteryHistory", description = "砍价大转盘抽奖历史记录")
|
||||||
|
public class ShopActivityCutLotteryHistory implements Serializable{
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@TableId(value = "alh_id", type = IdType.AUTO)
|
||||||
|
@ApiModelProperty(value = "用户中奖编号", example = "1")
|
||||||
|
private Long alhId;
|
||||||
|
|
||||||
|
@TableField(value ="user_id",updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
|
@ApiModelProperty(value = "用户编号", example = "10001")
|
||||||
|
private Integer userId;
|
||||||
|
|
||||||
|
@TableField(value ="activity_id",updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
|
@ApiModelProperty(value = "活动编号", example = "20001")
|
||||||
|
private Integer activityId;
|
||||||
|
|
||||||
|
@TableField(value ="alh_item_id",updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
|
@ApiModelProperty(value = "抽奖物品编号", example = "30001")
|
||||||
|
private Integer alhItemId;
|
||||||
|
|
||||||
|
@TableField(value ="alh_item_name",updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
|
@ApiModelProperty(value = "抽奖物品名称", example = "iPhone 15 Pro")
|
||||||
|
private String alhItemName;
|
||||||
|
|
||||||
|
@TableField(value = "alh_award_flag",updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
|
@ApiModelProperty(value = "是否中奖:0-未中奖;1-中奖", example = "1")
|
||||||
|
private Boolean alhAwardFlag;
|
||||||
|
|
||||||
|
@TableField(value ="create_time")
|
||||||
|
@ApiModelProperty(value = "创建时间", example = "2023-12-01 10:00:00")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
@TableField("update_time")
|
||||||
|
@ApiModelProperty(value = "更新时间", example = "2023-12-01 10:00:00")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date updateTime;
|
||||||
|
}
|
||||||
@ -96,6 +96,9 @@ public class ShopMchEntry implements Serializable {
|
|||||||
@ApiModelProperty(value = "入驻商家店铺门面环境图片的存储路径", required = true)
|
@ApiModelProperty(value = "入驻商家店铺门面环境图片的存储路径", required = true)
|
||||||
private String environment_image;
|
private String environment_image;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "收银台照片", required = true)
|
||||||
|
private String checkstand_image;
|
||||||
|
|
||||||
@ApiModelProperty(value = "入驻主体类型,企业或个人:1-企业;2-个人;", required = true)
|
@ApiModelProperty(value = "入驻主体类型,企业或个人:1-企业;2-个人;", required = true)
|
||||||
private Integer entity_type;
|
private Integer entity_type;
|
||||||
|
|
||||||
@ -183,6 +186,9 @@ public class ShopMchEntry implements Serializable {
|
|||||||
@ApiModelProperty(value = "结算账户⾏号", required = true)
|
@ApiModelProperty(value = "结算账户⾏号", required = true)
|
||||||
private String openning_bank_code;
|
private String openning_bank_code;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "开户许可证(银行对公开户证明)")
|
||||||
|
private String opening_permit_image;
|
||||||
|
|
||||||
@ApiModelProperty(value = "结算账户清算⾏号")
|
@ApiModelProperty(value = "结算账户清算⾏号")
|
||||||
private String clearing_bank_code;
|
private String clearing_bank_code;
|
||||||
|
|
||||||
@ -258,6 +264,9 @@ public class ShopMchEntry implements Serializable {
|
|||||||
@ApiModelProperty(value = "是否绑定分账接收方:1-是;2-否;")
|
@ApiModelProperty(value = "是否绑定分账接收方:1-是;2-否;")
|
||||||
private Integer has_bind_receiver;
|
private Integer has_bind_receiver;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "父入驻id(总店入驻id)")
|
||||||
|
private Long parent_id;
|
||||||
|
|
||||||
@ApiModelProperty(value = "该商家入驻记录是否有效,0:无效,1:有效")
|
@ApiModelProperty(value = "该商家入驻记录是否有效,0:无效,1:有效")
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2025. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
|
||||||
|
* Morbi non lorem porttitor neque feugiat blandit. Ut vitae ipsum eget quam lacinia accumsan.
|
||||||
|
* Etiam sed turpis ac ipsum condimentum fringilla. Maecenas magna.
|
||||||
|
* Proin dapibus sapien vel ante. Aliquam erat volutpat. Pellentesque sagittis ligula eget metus.
|
||||||
|
* Vestibulum commodo. Ut rhoncus gravida arcu.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.suisung.mall.common.modules.store;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@TableName("shop_mch_entry_branch")
|
||||||
|
@ApiModel(value = "shop_mch_entry_branch 实体", description = "商家分店拉卡拉商户申请数据")
|
||||||
|
public class ShopMchEntryBranch implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "自增ID")
|
||||||
|
@TableId(value = "id", type = IdType.INPUT)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "拉卡拉外部(银联)商户号", required = true)
|
||||||
|
private String lkl_mer_cup_no;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "拉卡拉终端号")
|
||||||
|
private String lkl_term_no;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "审核关联号", required = true)
|
||||||
|
private String review_related_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "请求拉卡拉参数")
|
||||||
|
private String lkl_req;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "拉卡拉响应数据")
|
||||||
|
private String lkl_reps;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "拉卡拉通知响应数据")
|
||||||
|
private String lkl_notify_reps;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "该商家入驻记录是否有效,0:无效,1:有效")
|
||||||
|
private Integer status;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "商家入驻记录的创建时间")
|
||||||
|
private Date created_at;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "商家入驻记录的更新时间")
|
||||||
|
private Date updated_at;
|
||||||
|
}
|
||||||
@ -1,6 +1,9 @@
|
|||||||
package com.suisung.mall.common.modules.store;
|
package com.suisung.mall.common.modules.store;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.*;
|
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 com.fasterxml.jackson.annotation.JsonFormat;
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
import io.swagger.annotations.ApiModelProperty;
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
@ -119,7 +122,6 @@ public class ShopStoreActivityBase implements Serializable {
|
|||||||
@TableField(updateStrategy = NOT_EMPTY)
|
@TableField(updateStrategy = NOT_EMPTY)
|
||||||
private Integer activity_share_num;
|
private Integer activity_share_num;
|
||||||
|
|
||||||
@Version
|
|
||||||
@ApiModelProperty(value = "乐观锁")
|
@ApiModelProperty(value = "乐观锁")
|
||||||
private Integer version;
|
private Integer version;
|
||||||
|
|
||||||
@ -127,4 +129,14 @@ public class ShopStoreActivityBase implements Serializable {
|
|||||||
@TableField(updateStrategy = NOT_EMPTY)
|
@TableField(updateStrategy = NOT_EMPTY)
|
||||||
private String flow_no;
|
private String flow_no;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "在活动时间范围内,用户砍价有效期(小时)")
|
||||||
|
private Integer cut_hour;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "参与活动商品的总数量(个)")
|
||||||
|
private Integer product_count;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "规则配置")
|
||||||
|
@TableField(updateStrategy = NOT_EMPTY)
|
||||||
|
private String lucky_turn;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -80,9 +80,12 @@ public class ShopStoreBase implements Serializable {
|
|||||||
@ApiModelProperty(value = "店铃声开关:1-开启;2-关闭;")
|
@ApiModelProperty(value = "店铃声开关:1-开启;2-关闭;")
|
||||||
private Integer ringtone_is_enable;
|
private Integer ringtone_is_enable;
|
||||||
|
|
||||||
@ApiModelProperty(value = "店铺营业状态:1-营业中;2-已打烊;")
|
@ApiModelProperty(value = "店铺营业状态:1-开业营业中(且在营业时间内);12-开业打烊中(但在营业时间外);2-停业中;3-开业(或活动)筹备中;")
|
||||||
private Integer store_biz_state;
|
private Integer store_biz_state;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "开业(活动)筹备日期 yyyy-MM-dd")
|
||||||
|
private Date store_biz_opening_date;
|
||||||
|
|
||||||
@ApiModelProperty(value = "上级店铺编号:创建店铺决定,所属分销商-不可更改! 佣金公平性考虑")
|
@ApiModelProperty(value = "上级店铺编号:创建店铺决定,所属分销商-不可更改! 佣金公平性考虑")
|
||||||
private Integer shop_parent_id;
|
private Integer shop_parent_id;
|
||||||
|
|
||||||
@ -138,6 +141,9 @@ public class ShopStoreBase implements Serializable {
|
|||||||
@ApiModelProperty(value = "店铺统一的打包费, 0-10元范围")
|
@ApiModelProperty(value = "店铺统一的打包费, 0-10元范围")
|
||||||
private BigDecimal packing_fee;
|
private BigDecimal packing_fee;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "分店所属总店 Id,一般分店有值,总店为0")
|
||||||
|
private Integer parent_id;
|
||||||
|
|
||||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||||
@ApiModelProperty(value = "新增时间")
|
@ApiModelProperty(value = "新增时间")
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.suisung.mall.common.modules.store;
|
package com.suisung.mall.common.modules.store;
|
||||||
|
|
||||||
import com.baomidou.mybatisplus.annotation.IdType;
|
import com.baomidou.mybatisplus.annotation.IdType;
|
||||||
|
import com.baomidou.mybatisplus.annotation.TableField;
|
||||||
import com.baomidou.mybatisplus.annotation.TableId;
|
import com.baomidou.mybatisplus.annotation.TableId;
|
||||||
import com.baomidou.mybatisplus.annotation.TableName;
|
import com.baomidou.mybatisplus.annotation.TableName;
|
||||||
import io.swagger.annotations.ApiModel;
|
import io.swagger.annotations.ApiModel;
|
||||||
@ -10,6 +11,7 @@ import lombok.EqualsAndHashCode;
|
|||||||
import lombok.experimental.Accessors;
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@ -47,5 +49,7 @@ public class ShopStoreEmployeeRightsBase implements Serializable {
|
|||||||
@ApiModelProperty(value = "功能开启:跟设置config_key")
|
@ApiModelProperty(value = "功能开启:跟设置config_key")
|
||||||
private String menu_func;
|
private String menu_func;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "节点子集")
|
||||||
|
@TableField(exist = false)
|
||||||
|
private List<ShopStoreEmployeeRightsBase> children;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -126,6 +126,9 @@ public class ShopStoreInfo implements Serializable {
|
|||||||
@ApiModelProperty(value = "店铺内部运费,单位(分)0-使用平台的内部运费;>0 使用店铺的内部运费")
|
@ApiModelProperty(value = "店铺内部运费,单位(分)0-使用平台的内部运费;>0 使用店铺的内部运费")
|
||||||
private Integer shopping_fee_inner;
|
private Integer shopping_fee_inner;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "是否自配送:1-是;2-否;")
|
||||||
|
private Integer is_delivery_self;
|
||||||
|
|
||||||
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
|
||||||
@ApiModelProperty(value = "新增时间")
|
@ApiModelProperty(value = "新增时间")
|
||||||
|
|||||||
@ -43,12 +43,18 @@ public class ShopStoreSameCityTransportBase implements Serializable {
|
|||||||
@TableId(value = "transport_base_id", type = IdType.AUTO)
|
@TableId(value = "transport_base_id", type = IdType.AUTO)
|
||||||
private Long transport_base_id;
|
private Long transport_base_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "平台配送费设置?1-是;2-否")
|
||||||
|
private Integer is_platform;
|
||||||
|
|
||||||
@ApiModelProperty(value = "店铺ID")
|
@ApiModelProperty(value = "店铺ID")
|
||||||
private Long store_id;
|
private Long store_id;
|
||||||
|
|
||||||
@ApiModelProperty(value = "顺丰同城(第三方)店铺ID")
|
@ApiModelProperty(value = "顺丰同城(第三方)店铺ID")
|
||||||
private String shop_id;
|
private String shop_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "顺丰平台商家Id (用于县级代理商创建店铺的)")
|
||||||
|
private String supplier_id;
|
||||||
|
|
||||||
@ApiModelProperty(value = "店铺主营商品分类ID")
|
@ApiModelProperty(value = "店铺主营商品分类ID")
|
||||||
private Integer business_type;
|
private Integer business_type;
|
||||||
|
|
||||||
|
|||||||
@ -28,10 +28,18 @@ public class ProductQuantityConsumption {
|
|||||||
@ApiModelProperty("商品编号")
|
@ApiModelProperty("商品编号")
|
||||||
private String productNumber;
|
private String productNumber;
|
||||||
|
|
||||||
|
@TableField(value = "unit_price", updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
|
@ApiModelProperty("商品单价")
|
||||||
|
private BigDecimal unitPrice;
|
||||||
|
|
||||||
@ApiModelProperty("数量(正数表示入库/增加,负数表示出库/减少)")
|
@ApiModelProperty("数量(正数表示入库/增加,负数表示出库/减少)")
|
||||||
@TableField(value = "quantity",updateStrategy = FieldStrategy.NOT_EMPTY)
|
@TableField(value = "quantity",updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
private BigDecimal quantity;
|
private BigDecimal quantity;
|
||||||
|
|
||||||
|
@ApiModelProperty("订单总额")
|
||||||
|
@TableField(value = "sale_amount",updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
|
private BigDecimal saleAmount;
|
||||||
|
|
||||||
@ApiModelProperty("消费状态:0-未消费,1-已消费")
|
@ApiModelProperty("消费状态:0-未消费,1-已消费")
|
||||||
@TableField(value = "status",updateStrategy = FieldStrategy.NOT_EMPTY)
|
@TableField(value = "status",updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
private Integer status;
|
private Integer status;
|
||||||
@ -40,7 +48,7 @@ public class ProductQuantityConsumption {
|
|||||||
@ApiModelProperty("店铺ID")
|
@ApiModelProperty("店铺ID")
|
||||||
private Integer storeId;
|
private Integer storeId;
|
||||||
|
|
||||||
@TableField(value = "update_time",updateStrategy = FieldStrategy.NOT_EMPTY)
|
@TableField(value = "create_time",updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
@ApiModelProperty("创建时间")
|
@ApiModelProperty("创建时间")
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private Date createTime;
|
private Date createTime;
|
||||||
@ -49,4 +57,8 @@ public class ProductQuantityConsumption {
|
|||||||
@ApiModelProperty(value = "更新时间")
|
@ApiModelProperty(value = "更新时间")
|
||||||
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
private Date updateTime;
|
private Date updateTime;
|
||||||
|
|
||||||
|
@TableField(value = "sale_time",updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
|
@ApiModelProperty(value = "下单时间")
|
||||||
|
private Long saleTime;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -126,4 +126,24 @@ public class StoreDbConfig implements Serializable {
|
|||||||
@TableField(value = "shop_gap_time",updateStrategy = FieldStrategy.NOT_EMPTY)
|
@TableField(value = "shop_gap_time",updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
@NotBlank(message = "商品同步间隔时间不能为空")
|
@NotBlank(message = "商品同步间隔时间不能为空")
|
||||||
private String shopGapTime;
|
private String shopGapTime;
|
||||||
|
|
||||||
|
@TableField(value = "sale_account",updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
|
@ApiModelProperty(value = "营业员账号")
|
||||||
|
private String saleAccount;
|
||||||
|
|
||||||
|
@TableField(value = "is_sync_active",updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
|
@ApiModelProperty(value = "是否同步活动(0:否,1:是)")
|
||||||
|
private String isSyncActive;
|
||||||
|
|
||||||
|
@TableField(value = "is_sync_member",updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
|
@ApiModelProperty(value = "是否同步会员(0:否,1:是)")
|
||||||
|
private String isSyncMember;
|
||||||
|
|
||||||
|
@TableField(value = "automatic",updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
|
@ApiModelProperty(value = "默认次日补全库存(思迅同步时配置)")
|
||||||
|
private Integer automatic;
|
||||||
|
|
||||||
|
@TableField(value = "client_version",updateStrategy = FieldStrategy.NOT_EMPTY)
|
||||||
|
@ApiModelProperty(value = "客户端版本:1商云10,2商瑞9.7,3商祺")
|
||||||
|
private String clientVersion;
|
||||||
}
|
}
|
||||||
@ -0,0 +1,53 @@
|
|||||||
|
package com.suisung.mall.common.modules.sync;
|
||||||
|
|
||||||
|
import com.baomidou.mybatisplus.annotation.*;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonFormat;
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Date;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@TableName("sycn_store_data")
|
||||||
|
@ApiModel("商品同步数据")
|
||||||
|
public class SyncStoreData implements Serializable {
|
||||||
|
@TableId(value = "sync_store_data_id", type = IdType.INPUT)
|
||||||
|
@ApiModelProperty("主键ID")
|
||||||
|
private String syncStoreDataId;
|
||||||
|
|
||||||
|
@TableField("store_id")
|
||||||
|
@ApiModelProperty("店铺ID")
|
||||||
|
private String storeId;
|
||||||
|
|
||||||
|
@TableField("content")
|
||||||
|
@ApiModelProperty("同步文本内容")
|
||||||
|
private String content;
|
||||||
|
|
||||||
|
@TableField("file_name")
|
||||||
|
@ApiModelProperty("文件名称")
|
||||||
|
private String fileName;
|
||||||
|
|
||||||
|
@TableField("folder")
|
||||||
|
@ApiModelProperty("文件路径")
|
||||||
|
private String folder;
|
||||||
|
|
||||||
|
@TableField("file_path")
|
||||||
|
@ApiModelProperty("文件全路径")
|
||||||
|
private String filePath;
|
||||||
|
|
||||||
|
@TableField("status")
|
||||||
|
@ApiModelProperty("处理状态0:未处理,1:已处理")
|
||||||
|
private String status;
|
||||||
|
|
||||||
|
@TableField("create_time")
|
||||||
|
@ApiModelProperty(value = "创建时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date createTime;
|
||||||
|
|
||||||
|
@TableField("update_time")
|
||||||
|
@ApiModelProperty(value = "更新时间")
|
||||||
|
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
|
||||||
|
private Date updateTime;
|
||||||
|
}
|
||||||
@ -42,7 +42,7 @@ public class LibraryProductDTO implements Serializable {
|
|||||||
if (this.product_image_list != null) {
|
if (this.product_image_list != null) {
|
||||||
for (ProductImage image : this.product_image_list) {
|
for (ProductImage image : this.product_image_list) {
|
||||||
if (image != null) {
|
if (image != null) {
|
||||||
image.image_url = addDomainPrefix(imageDomain, image.getImage_url());
|
image.imageUrl = addDomainPrefix(imageDomain, image.getImageUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -66,9 +66,10 @@ public class LibraryProductDTO implements Serializable {
|
|||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
@ApiModel(value = "商品库的商品图片", description = "商品库的商品图片")
|
@ApiModel(value = "商品库的商品图片", description = "商品库的商品图片")
|
||||||
public static class ProductImage implements Serializable {
|
public static class ProductImage implements Serializable {
|
||||||
private Long product_id;
|
private Long id;
|
||||||
private String image_url;
|
private Long productId;
|
||||||
private Integer is_main;
|
private String imageUrl;
|
||||||
|
private Integer isMain;
|
||||||
private Integer seq;
|
private Integer seq;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ package com.suisung.mall.common.pojo.dto;
|
|||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.data.util.Pair;
|
import org.springframework.data.util.Pair;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
@ -29,10 +30,13 @@ import java.math.RoundingMode;
|
|||||||
*/
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Slf4j
|
||||||
public class LklSeparateWithTotalAmountDTO {
|
public class LklSeparateWithTotalAmountDTO {
|
||||||
|
|
||||||
// 常量定义
|
// 商家最低分账比例阈值 20%
|
||||||
private static final BigDecimal MCH_RATIO_THRESHOLD = new BigDecimal("0.2");
|
private static final BigDecimal MIN_MERCHANT_RATIO_THRESHOLD = BigDecimal.valueOf(0.2);
|
||||||
|
// 默认平台比例 1%
|
||||||
|
private static final BigDecimal DEFAULT_PLATFORM_RATIO = BigDecimal.valueOf(0.01);
|
||||||
|
|
||||||
// 基础金额属性
|
// 基础金额属性
|
||||||
private Integer totalSeparateAmount; // 分账总金额(分)
|
private Integer totalSeparateAmount; // 分账总金额(分)
|
||||||
@ -41,7 +45,7 @@ public class LklSeparateWithTotalAmountDTO {
|
|||||||
private Integer shippingFee; // 配送费(分)
|
private Integer shippingFee; // 配送费(分)
|
||||||
|
|
||||||
// 分账比例属性
|
// 分账比例属性
|
||||||
private BigDecimal lklRatio; // 拉卡拉分账比例(如 0.0025=0.25%)
|
private BigDecimal lklRatio; // 拉卡拉分账比例(如 0.0025=0.025%)
|
||||||
private BigDecimal mchRatio; // 商户分账比例(如 0.96=96%)
|
private BigDecimal mchRatio; // 商户分账比例(如 0.96=96%)
|
||||||
private BigDecimal platRatio; // 平台分账比例(如 0.01=1%)
|
private BigDecimal platRatio; // 平台分账比例(如 0.01=1%)
|
||||||
private BigDecimal agent1stRatio; // 一级代理商分账比例(如 0.01=1%)
|
private BigDecimal agent1stRatio; // 一级代理商分账比例(如 0.01=1%)
|
||||||
@ -53,6 +57,7 @@ public class LklSeparateWithTotalAmountDTO {
|
|||||||
private Integer platAmount; // 平台分账金额(分)
|
private Integer platAmount; // 平台分账金额(分)
|
||||||
private Integer agent1stAmount; // 一级代理商分账金额(分)
|
private Integer agent1stAmount; // 一级代理商分账金额(分)
|
||||||
private Integer agent2ndAmount; // 二级代理商分账金额(分)
|
private Integer agent2ndAmount; // 二级代理商分账金额(分)
|
||||||
|
private String errMsg; // 错误信息字段
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 测试方法
|
* 测试方法
|
||||||
@ -62,26 +67,75 @@ public class LklSeparateWithTotalAmountDTO {
|
|||||||
// 测试用例1: 所有参与方都参与分账(符合比例要求)
|
// 测试用例1: 所有参与方都参与分账(符合比例要求)
|
||||||
System.out.println("=== 测试用例1: 所有参与方都参与分账 ===");
|
System.out.println("=== 测试用例1: 所有参与方都参与分账 ===");
|
||||||
LklSeparateWithTotalAmountDTO dto1 = new LklSeparateWithTotalAmountDTO();
|
LklSeparateWithTotalAmountDTO dto1 = new LklSeparateWithTotalAmountDTO();
|
||||||
dto1.setTotalSeparateAmount(1696); // 总金额100元(10000分)
|
dto1.setTotalSeparateAmount(800); // 总金额16.96元(1696分)
|
||||||
dto1.setShippingFee(500);
|
dto1.setShippingFee(500);
|
||||||
// dto1.setRefCanSeparateAmount(1496);
|
// dto1.setRefCanSeparateAmount(1496);
|
||||||
dto1.setLklRatio(new BigDecimal("0.0025")); // 拉卡拉分账比例0.25%
|
dto1.setLklRatio(BigDecimal.valueOf(0.0025)); // 拉卡拉分账比例0.25%
|
||||||
dto1.setMchRatio(new BigDecimal("0.96")); // 商家分账比例94.75%
|
dto1.setMchRatio(BigDecimal.valueOf(0.96)); // 商家分账比例94.75%
|
||||||
dto1.setPlatRatio(new BigDecimal("0.04")); // 平台分账比例1%
|
dto1.setPlatRatio(BigDecimal.valueOf(0.04)); // 平台分账比例1%
|
||||||
// dto1.setAgent2ndRatio(new BigDecimal("0.04")); // 二级代理商分账比例4%
|
// dto1.setAgent2ndRatio(BigDecimal.valueOf(0.04)); // 二级代理商分账比例4%
|
||||||
// dto1.setAgent1stRatio(new BigDecimal("0.01")); // 一级代理商分账比例1%
|
// dto1.setAgent1stRatio(BigDecimal.valueOf(0.01)); // 一级代理商分账比例1%
|
||||||
|
|
||||||
Pair<Boolean, LklSeparateWithTotalAmountDTO> result = dto1.calculateSeparateAmount();
|
SeparateResult result = dto1.calculateSeparateAmount();
|
||||||
if (result.getFirst()) {
|
if (result.getIsSuccess()) {
|
||||||
System.out.println("分账计算成功:");
|
System.out.println("分账计算成功:");
|
||||||
System.out.println(result.getSecond());
|
System.out.println(result.getData());
|
||||||
System.out.println("JSON格式输出:");
|
System.out.println("JSON格式输出:");
|
||||||
System.out.println(result.getSecond().toJSON());
|
System.out.println(result.getData().toJSON());
|
||||||
} else {
|
} else {
|
||||||
System.out.println("分账计算失败");
|
System.out.println("分账计算失败");
|
||||||
if (result.getSecond() != null) {
|
if (result.getErrMsg() != null) {
|
||||||
System.out.println("部分结果:");
|
System.out.println("部分结果:");
|
||||||
System.out.println(result.getSecond());
|
System.out.println(result.getErrMsg());
|
||||||
|
// 输出错误信息
|
||||||
|
if (result.getErrMsg() != null && !result.getErrMsg().isEmpty()) {
|
||||||
|
System.out.println("错误信息: " + result.getErrMsg());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试用例2: 商家分账比例过低的情况
|
||||||
|
System.out.println("\n=== 测试用例2: 商家分账比例过低 ===");
|
||||||
|
LklSeparateWithTotalAmountDTO dto2 = new LklSeparateWithTotalAmountDTO();
|
||||||
|
dto2.setTotalSeparateAmount(10000); // 总金额100元(10000分)
|
||||||
|
dto2.setLklRatio(BigDecimal.valueOf(0.0025)); // 拉卡拉分账比例0.25%
|
||||||
|
dto2.setMchRatio(BigDecimal.valueOf(0.1)); // 商家分账比例过低(10%)
|
||||||
|
dto2.setPlatRatio(BigDecimal.valueOf(0.04)); // 平台分账比例4%
|
||||||
|
|
||||||
|
SeparateResult result2 = dto2.calculateSeparateAmount();
|
||||||
|
if (result2.getIsSuccess()) {
|
||||||
|
System.out.println("分账计算成功:");
|
||||||
|
System.out.println(result2.getData());
|
||||||
|
} else {
|
||||||
|
System.out.println("分账计算失败");
|
||||||
|
if (result2.getData() != null) {
|
||||||
|
System.out.println("部分结果:");
|
||||||
|
System.out.println(result2.getData());
|
||||||
|
// 输出错误信息
|
||||||
|
if (result2.getErrMsg() != null && !result2.getErrMsg().isEmpty()) {
|
||||||
|
System.out.println("错误信息: " + result2.getErrMsg());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试用例3: 使用calculateSeparateAmountWithResult方法获取SeparateResult结果
|
||||||
|
System.out.println("\n=== 测试用例3: 使用calculateSeparateAmountWithResult方法获取SeparateResult结果 ===");
|
||||||
|
LklSeparateWithTotalAmountDTO dto3 = new LklSeparateWithTotalAmountDTO();
|
||||||
|
dto3.setTotalSeparateAmount(10000); // 总金额100元(10000分)
|
||||||
|
dto3.setLklRatio(BigDecimal.valueOf(0.0025)); // 拉卡拉分账比例0.25%
|
||||||
|
dto3.setMchRatio(BigDecimal.valueOf(0.8)); // 商家分账比例80%
|
||||||
|
dto3.setPlatRatio(BigDecimal.valueOf(0.04)); // 平台分账比例4%
|
||||||
|
|
||||||
|
LklSeparateWithTotalAmountDTO.SeparateResult separateResult = dto3.calculateSeparateAmountWithResult();
|
||||||
|
|
||||||
|
if (separateResult.getIsSuccess()) {
|
||||||
|
System.out.println("分账计算成功:");
|
||||||
|
System.out.println(separateResult.getData());
|
||||||
|
} else {
|
||||||
|
System.out.println("分账计算失败: " + separateResult.getErrMsg());
|
||||||
|
if (separateResult.getData() != null) {
|
||||||
|
System.out.println("部分结果:");
|
||||||
|
System.out.println(separateResult.getData());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,63 +143,129 @@ public class LklSeparateWithTotalAmountDTO {
|
|||||||
/**
|
/**
|
||||||
* 执行分账计算逻辑
|
* 执行分账计算逻辑
|
||||||
*
|
*
|
||||||
* @return Pair<Boolean, LklSeparateWithTotalAmountDTO> Boolean表示是否成功,LklSeparateWithTotalAmountDTO为分账结果
|
* @return SeparateResult 包含成功状态、分账结果和错误信息的包装类
|
||||||
*/
|
*/
|
||||||
public Pair<Boolean, LklSeparateWithTotalAmountDTO> calculateSeparateAmount() {
|
public SeparateResult calculateSeparateAmount() {
|
||||||
try {
|
try {
|
||||||
|
// 清空之前的错误信息
|
||||||
|
this.errMsg = null;
|
||||||
|
|
||||||
// 参数校验
|
// 参数校验
|
||||||
validateInputs();
|
Pair<Boolean, String> validateResult = validateInputs();
|
||||||
|
if (!validateResult.getFirst()) {
|
||||||
|
String errorMsg = "分账计算参数异常: " + validateResult.getSecond();
|
||||||
|
log.error(errorMsg);
|
||||||
|
this.errMsg = errorMsg;
|
||||||
|
return SeparateResult.failure(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
// 1. 计算拉卡拉分账金额和可分账金额
|
// 1. 计算拉卡拉分账金额和可分账金额
|
||||||
calculateLklAmountAndCanSeparateAmount();
|
Pair<Boolean, String> lklResult = calculateLklAmountAndCanSeparateAmount();
|
||||||
|
if (!lklResult.getFirst()) {
|
||||||
|
String errorMsg = "分账计算参数异常: " + lklResult.getSecond();
|
||||||
|
log.error(errorMsg);
|
||||||
|
this.errMsg = errorMsg;
|
||||||
|
return SeparateResult.failure(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
// 2. 确定实际可分账金额
|
// 2. 确定实际可分账金额
|
||||||
int actualCanSeparateAmount = determineActualCanSeparateAmount();
|
Pair<Boolean, String> canSeparateResult = determineActualCanSeparateAmount();
|
||||||
|
if (!canSeparateResult.getFirst()) {
|
||||||
|
String errorMsg = "分账计算参数异常: " + canSeparateResult.getSecond();
|
||||||
|
log.error(errorMsg);
|
||||||
|
this.errMsg = errorMsg;
|
||||||
|
return SeparateResult.failure(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
int actualCanSeparateAmount = Integer.parseInt(canSeparateResult.getSecond());
|
||||||
|
|
||||||
// 3. 计算各参与方分账比例
|
// 3. 计算各参与方分账比例
|
||||||
calculateDefaultRatios();
|
Pair<Boolean, String> ratioResult = calculateDefaultRatios();
|
||||||
|
if (!ratioResult.getFirst()) {
|
||||||
|
String errorMsg = "分账计算参数异常: " + ratioResult.getSecond();
|
||||||
|
log.error(errorMsg);
|
||||||
|
this.errMsg = errorMsg;
|
||||||
|
return SeparateResult.failure(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
// 4. 根据优先级顺序计算各参与方分账金额
|
// 4. 根据优先级顺序计算各参与方分账金额
|
||||||
calculateAmountsInPriorityOrder(actualCanSeparateAmount);
|
Pair<Boolean, String> amountResult = calculateAmountsInPriorityOrder(actualCanSeparateAmount);
|
||||||
|
if (!amountResult.getFirst()) {
|
||||||
|
String errorMsg = "分账计算参数异常: " + amountResult.getSecond();
|
||||||
|
log.error(errorMsg);
|
||||||
|
this.errMsg = errorMsg;
|
||||||
|
return SeparateResult.failure(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
// 5. 校验分账金额总和不能超过总金额
|
// 5. 校验分账金额总和不能超过总金额
|
||||||
validateSeparateAmountTotal();
|
Pair<Boolean, String> totalResult = validateSeparateAmountTotal();
|
||||||
|
if (!totalResult.getFirst()) {
|
||||||
|
String errorMsg = "分账计算参数异常: " + totalResult.getSecond();
|
||||||
|
log.error(errorMsg);
|
||||||
|
this.errMsg = errorMsg;
|
||||||
|
return SeparateResult.failure(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
// 6. 计算商家实际分账比例
|
// 6. 根据指定规则调整各参与方分账金额
|
||||||
calculateActualMchRatio();
|
Pair<Boolean, String> adjustResult = adjustAmountsWithLklAmount();
|
||||||
|
if (!adjustResult.getFirst()) {
|
||||||
|
String errorMsg = "分账计算参数异常: " + adjustResult.getSecond();
|
||||||
|
log.error(errorMsg);
|
||||||
|
this.errMsg = errorMsg;
|
||||||
|
return SeparateResult.failure(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
return Pair.of(true, this);
|
// 7. 计算商家实际分账比例
|
||||||
|
Pair<Boolean, String> mchResult = calculateActualMchRatio();
|
||||||
|
if (!mchResult.getFirst()) {
|
||||||
|
String errorMsg = "分账计算参数异常: " + mchResult.getSecond();
|
||||||
|
log.error(errorMsg);
|
||||||
|
this.errMsg = errorMsg;
|
||||||
|
return SeparateResult.failure(errorMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return SeparateResult.success(this);
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
// 参数校验异常,返回false和当前对象
|
// 参数校验异常,返回false和当前对象
|
||||||
System.err.println("分账计算参数异常: " + e.getMessage());
|
String errorMsg = "分账计算参数异常: " + e.getMessage();
|
||||||
return Pair.of(false, this);
|
log.error(errorMsg, e);
|
||||||
|
this.errMsg = errorMsg;
|
||||||
|
return SeparateResult.failure(errorMsg);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// 其他异常,返回false和null
|
// 其他异常,返回false和null
|
||||||
System.err.println("分账计算异常: " + e.getMessage());
|
String errorMsg = "分账计算异常: " + e.getMessage();
|
||||||
return Pair.of(false, null);
|
log.error(errorMsg, e);
|
||||||
|
this.errMsg = errorMsg;
|
||||||
|
return SeparateResult.failure(errorMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验必要参数
|
* 校验必要参数
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException 当参数不合法时抛出异常
|
* @return Pair<Boolean, String> Boolean表示是否成功,String为错误信息
|
||||||
*/
|
*/
|
||||||
private void validateInputs() {
|
private Pair<Boolean, String> validateInputs() {
|
||||||
// 校验totalSeparateAmount必须为有效值且大于0
|
// 校验totalSeparateAmount必须为有效值且大于0
|
||||||
if (totalSeparateAmount == null || totalSeparateAmount <= 0) {
|
if (totalSeparateAmount == null || totalSeparateAmount <= 0) {
|
||||||
throw new IllegalArgumentException("分账计算缺少必要参数或参数不合法");
|
return Pair.of(false, "分账计算:总分账金额必须大于0");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验必要参数
|
// 校验必要参数
|
||||||
if (lklRatio == null || mchRatio == null) {
|
if (lklRatio == null || lklRatio.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
throw new IllegalArgumentException("分账计算缺少必要参数");
|
return Pair.of(false, "分账计算:拉卡拉分账比例必须大于0");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mchRatio == null || mchRatio.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
|
return Pair.of(false, "分账计算:商户分账比例必须大于0");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 校验shippingFee不能大于等于totalSeparateAmount
|
// 校验shippingFee不能大于等于totalSeparateAmount
|
||||||
if (shippingFee != null && shippingFee >= totalSeparateAmount) {
|
if (shippingFee != null && shippingFee > 0 && shippingFee >= totalSeparateAmount) {
|
||||||
throw new IllegalArgumentException("配送费不能大于等于总金额");
|
return Pair.of(false, "分账计算:配送费用不能大于等于总金额");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Pair.of(true, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -153,19 +273,17 @@ public class LklSeparateWithTotalAmountDTO {
|
|||||||
* 拉卡拉分账金额 = 总金额 * 拉卡拉分账比例(四舍五入)
|
* 拉卡拉分账金额 = 总金额 * 拉卡拉分账比例(四舍五入)
|
||||||
* 可分账金额 = 总金额 - 拉卡拉分账金额
|
* 可分账金额 = 总金额 - 拉卡拉分账金额
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException 当计算出的分账金额为负数时抛出异常
|
* @return Pair<Boolean, String> Boolean表示是否成功,String为错误信息
|
||||||
*/
|
*/
|
||||||
private void calculateLklAmountAndCanSeparateAmount() {
|
private Pair<Boolean, String> calculateLklAmountAndCanSeparateAmount() {
|
||||||
// 拉卡拉分账金额 = 总金额 * 拉卡拉分账比例(四舍五入)
|
// 拉卡拉分账金额 = 总金额 * 拉卡拉分账比例(四舍五入)
|
||||||
lklAmount = lklRatio.multiply(new BigDecimal(totalSeparateAmount))
|
lklAmount = lklRatio.multiply(BigDecimal.valueOf(totalSeparateAmount))
|
||||||
.setScale(0, RoundingMode.HALF_UP)
|
.setScale(0, RoundingMode.HALF_UP)
|
||||||
.intValue();
|
.intValue();
|
||||||
|
|
||||||
// 校验拉卡拉分账金额不能为负数
|
// 校验拉卡拉分账金额不能为负数
|
||||||
if (lklAmount < 0) {
|
if (lklAmount < 0) {
|
||||||
// 记录详细日志
|
return Pair.of(false, "拉卡拉分账金额计算异常");
|
||||||
System.err.println("拉卡拉分账金额计算结果为负数: " + lklAmount);
|
|
||||||
throw new IllegalArgumentException("拉卡拉分账金额计算异常");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 可分账金额 = 总金额 - 拉卡拉分账金额
|
// 可分账金额 = 总金额 - 拉卡拉分账金额
|
||||||
@ -173,10 +291,10 @@ public class LklSeparateWithTotalAmountDTO {
|
|||||||
|
|
||||||
// 校验可分账金额不能为负数
|
// 校验可分账金额不能为负数
|
||||||
if (canSeparateAmount < 0) {
|
if (canSeparateAmount < 0) {
|
||||||
// 记录详细日志
|
return Pair.of(false, "可分账金额计算异常");
|
||||||
System.err.println("可分账金额计算结果为负数: " + canSeparateAmount);
|
|
||||||
throw new IllegalArgumentException("可分账金额计算异常");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Pair.of(true, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -184,10 +302,9 @@ public class LklSeparateWithTotalAmountDTO {
|
|||||||
* 实际可分账金额A: 如果refCanSeparateAmount有效则使用,否则使用canSeparateAmount
|
* 实际可分账金额A: 如果refCanSeparateAmount有效则使用,否则使用canSeparateAmount
|
||||||
* 实际可分账金额B = 实际可分账金额A - 配送费(如果配送费有效)
|
* 实际可分账金额B = 实际可分账金额A - 配送费(如果配送费有效)
|
||||||
*
|
*
|
||||||
* @return 实际可分账金额
|
* @return Pair<Boolean, String> Boolean表示是否成功,String为错误信息
|
||||||
* @throws IllegalArgumentException 当实际可分账金额为负数时抛出异常
|
|
||||||
*/
|
*/
|
||||||
private int determineActualCanSeparateAmount() {
|
private Pair<Boolean, String> determineActualCanSeparateAmount() {
|
||||||
// 实际可分账金额A: 如果refCanSeparateAmount有效则使用,否则使用canSeparateAmount
|
// 实际可分账金额A: 如果refCanSeparateAmount有效则使用,否则使用canSeparateAmount
|
||||||
int actualCanSeparateAmountA = (refCanSeparateAmount != null && refCanSeparateAmount > 0)
|
int actualCanSeparateAmountA = (refCanSeparateAmount != null && refCanSeparateAmount > 0)
|
||||||
? refCanSeparateAmount : canSeparateAmount;
|
? refCanSeparateAmount : canSeparateAmount;
|
||||||
@ -200,23 +317,24 @@ public class LklSeparateWithTotalAmountDTO {
|
|||||||
|
|
||||||
// 校验实际可分账金额不能为负数
|
// 校验实际可分账金额不能为负数
|
||||||
if (actualCanSeparateAmountB < 0) {
|
if (actualCanSeparateAmountB < 0) {
|
||||||
// 记录详细日志
|
return Pair.of(false, "实际可分账金额计算异常");
|
||||||
System.err.println("实际可分账金额计算结果为负数: " + actualCanSeparateAmountB);
|
|
||||||
throw new IllegalArgumentException("实际可分账金额计算异常");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return actualCanSeparateAmountB;
|
return Pair.of(true, String.valueOf(actualCanSeparateAmountB));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算各参与方分账比例
|
* 计算各参与方分账比例
|
||||||
* 如果平台比例无效,设置默认值0.01
|
*
|
||||||
|
* @return Pair<Boolean, String> Boolean表示是否成功,String为错误信息
|
||||||
*/
|
*/
|
||||||
private void calculateDefaultRatios() {
|
private Pair<Boolean, String> calculateDefaultRatios() {
|
||||||
// 如果平台比例无效,设置默认值0.01
|
// 如果平台比例无效,设置默认值0.01
|
||||||
if (platRatio == null || platRatio.compareTo(BigDecimal.ZERO) <= 0) {
|
if (platRatio == null || platRatio.compareTo(BigDecimal.ZERO) <= 0) {
|
||||||
platRatio = new BigDecimal("0.01");
|
platRatio = DEFAULT_PLATFORM_RATIO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Pair.of(true, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -225,41 +343,31 @@ public class LklSeparateWithTotalAmountDTO {
|
|||||||
* 商家获得所有剩余金额
|
* 商家获得所有剩余金额
|
||||||
*
|
*
|
||||||
* @param actualCanSeparateAmount 实际可分账金额
|
* @param actualCanSeparateAmount 实际可分账金额
|
||||||
* @throws IllegalArgumentException 当计算出的分账金额为负数或分账金额超过可用金额时抛出异常
|
* @return Pair<Boolean, String> Boolean表示是否成功,String为错误信息
|
||||||
*/
|
*/
|
||||||
private void calculateAmountsInPriorityOrder(int actualCanSeparateAmount) {
|
private Pair<Boolean, String> calculateAmountsInPriorityOrder(int actualCanSeparateAmount) {
|
||||||
// 重置所有分账金额
|
// 重置所有分账金额
|
||||||
platAmount = 0;
|
platAmount = 0;
|
||||||
agent1stAmount = 0;
|
agent1stAmount = 0;
|
||||||
agent2ndAmount = 0;
|
agent2ndAmount = 0;
|
||||||
mchAmount = 0;
|
mchAmount = 0;
|
||||||
|
|
||||||
// 1. 计算拉卡拉分账金额(拉卡拉一定参与分账)
|
|
||||||
lklAmount = lklRatio.multiply(new BigDecimal(totalSeparateAmount))
|
|
||||||
.setScale(0, RoundingMode.HALF_UP)
|
|
||||||
.intValue();
|
|
||||||
|
|
||||||
// 校验拉卡拉分账金额不能为负数
|
|
||||||
if (lklAmount < 0) {
|
|
||||||
throw new IllegalArgumentException("拉卡拉分账金额计算异常");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. 校验实际可分账金额不能小于0
|
// 2. 校验实际可分账金额不能小于0
|
||||||
if (actualCanSeparateAmount < 0) {
|
if (actualCanSeparateAmount < 0) {
|
||||||
throw new IllegalArgumentException("实际可分账金额不能为负数");
|
return Pair.of(false, "实际可分账金额不能小于0");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 初始化剩余金额为实际可分账金额
|
// 3. 初始化剩余金额为实际可分账金额
|
||||||
int remainingAmount = actualCanSeparateAmount;
|
int remainingAmount = actualCanSeparateAmount;
|
||||||
|
|
||||||
// 4. 计算平台分账金额(平台一定参与分账)
|
// 4. 计算平台分账金额(平台一定参与分账)
|
||||||
platAmount = platRatio.multiply(new BigDecimal(totalSeparateAmount))
|
platAmount = platRatio.multiply(BigDecimal.valueOf(totalSeparateAmount))
|
||||||
.setScale(0, RoundingMode.HALF_UP)
|
.setScale(0, RoundingMode.HALF_UP)
|
||||||
.intValue();
|
.intValue();
|
||||||
|
|
||||||
// 校验平台分账金额不能为负数
|
// 校验平台分账金额不能为负数
|
||||||
if (platAmount < 0) {
|
if (platAmount < 0) {
|
||||||
throw new IllegalArgumentException("平台分账金额计算异常");
|
return Pair.of(false, "平台分账金额不能小于0");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保不超过剩余金额
|
// 确保不超过剩余金额
|
||||||
@ -278,13 +386,13 @@ public class LklSeparateWithTotalAmountDTO {
|
|||||||
// 优先级:平台 -> 二级代理商 -> 一级代理商
|
// 优先级:平台 -> 二级代理商 -> 一级代理商
|
||||||
if (isAgent2ndParticipate) {
|
if (isAgent2ndParticipate) {
|
||||||
// 计算二级代理商分账金额
|
// 计算二级代理商分账金额
|
||||||
agent2ndAmount = agent2ndRatio.multiply(new BigDecimal(totalSeparateAmount))
|
agent2ndAmount = agent2ndRatio.multiply(BigDecimal.valueOf(totalSeparateAmount))
|
||||||
.setScale(0, RoundingMode.HALF_UP)
|
.setScale(0, RoundingMode.HALF_UP)
|
||||||
.intValue();
|
.intValue();
|
||||||
|
|
||||||
// 校验二级代理商分账金额不能为负数
|
// 校验二级代理商分账金额不能为负数
|
||||||
if (agent2ndAmount < 0) {
|
if (agent2ndAmount < 0) {
|
||||||
throw new IllegalArgumentException("二级代理商分账金额计算异常");
|
return Pair.of(false, "二级代理商分账金额不能小于0");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保不超过剩余金额
|
// 确保不超过剩余金额
|
||||||
@ -296,13 +404,13 @@ public class LklSeparateWithTotalAmountDTO {
|
|||||||
|
|
||||||
if (isAgent1stParticipate) {
|
if (isAgent1stParticipate) {
|
||||||
// 计算一级代理商分账金额
|
// 计算一级代理商分账金额
|
||||||
agent1stAmount = agent1stRatio.multiply(new BigDecimal(totalSeparateAmount))
|
agent1stAmount = agent1stRatio.multiply(BigDecimal.valueOf(totalSeparateAmount))
|
||||||
.setScale(0, RoundingMode.HALF_UP)
|
.setScale(0, RoundingMode.HALF_UP)
|
||||||
.intValue();
|
.intValue();
|
||||||
|
|
||||||
// 校验一级代理商分账金额不能为负数
|
// 校验一级代理商分账金额不能为负数
|
||||||
if (agent1stAmount < 0) {
|
if (agent1stAmount < 0) {
|
||||||
throw new IllegalArgumentException("一级代理商分账金额计算异常");
|
return Pair.of(false, "一级代理商分账金额不能小于0");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 确保不超过剩余金额
|
// 确保不超过剩余金额
|
||||||
@ -317,48 +425,109 @@ public class LklSeparateWithTotalAmountDTO {
|
|||||||
|
|
||||||
// 校验商家分账金额不能为负数
|
// 校验商家分账金额不能为负数
|
||||||
if (mchAmount < 0) {
|
if (mchAmount < 0) {
|
||||||
throw new IllegalArgumentException("商家分账金额计算异常");
|
return Pair.of(false, "商家分账金额不能小于0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Pair.of(true, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据指定规则调整各参与方分账金额
|
||||||
|
* 如果拉卡拉分账金额lklAmount>0:
|
||||||
|
* 1. 如果agent2ndAmount>0 且大于lklAmount, 则agent2ndAmount=agent2ndAmount-lklAmount,mchAmount=mchAmount+lklAmount
|
||||||
|
* 2. 如果agent2ndAmount<=0 但 agent1stAmount>0 且大于lklAmount, 则agent1stAmount=agent1stAmount-lklAmount,mchAmount=mchAmount+lklAmount
|
||||||
|
* 3. 如果agent2ndAmount<=0 且 agent1stAmount<=0,但 platAmount>0 且大于等于lklAmount, 则 platAmount=platAmount-lklAmount,mchAmount=mchAmount+lklAmount
|
||||||
|
*
|
||||||
|
* @return Pair<Boolean, String> Boolean表示是否成功,String为错误信息
|
||||||
|
*/
|
||||||
|
private Pair<Boolean, String> adjustAmountsWithLklAmount() {
|
||||||
|
try {
|
||||||
|
// 只有当拉卡拉分账金额大于0时才执行调整逻辑
|
||||||
|
if (lklAmount != null && lklAmount > 0) {
|
||||||
|
int adjustment = lklAmount; // 需要调整的金额
|
||||||
|
|
||||||
|
// 情况1: 如果agent2ndAmount>0 且大于lklAmount
|
||||||
|
if (agent2ndAmount != null && agent2ndAmount > 0 && agent2ndAmount > adjustment) {
|
||||||
|
agent2ndAmount -= adjustment;
|
||||||
|
mchAmount += adjustment;
|
||||||
|
log.debug("调整二级代理商分账金额: 减少{},商家分账金额增加{}", adjustment, adjustment);
|
||||||
|
}
|
||||||
|
// 情况2: 如果agent2ndAmount<=0 但 agent1stAmount>0 且大于lklAmount
|
||||||
|
else if ((agent2ndAmount == null || agent2ndAmount <= 0) &&
|
||||||
|
agent1stAmount != null && agent1stAmount > 0 && agent1stAmount > adjustment) {
|
||||||
|
agent1stAmount -= adjustment;
|
||||||
|
mchAmount += adjustment;
|
||||||
|
log.debug("调整一级代理商分账金额: 减少{},商家分账金额增加{}", adjustment, adjustment);
|
||||||
|
}
|
||||||
|
// 情况3: 如果agent2ndAmount<=0 且 agent1stAmount<=0,但 platAmount>0 且大于等于lklAmount
|
||||||
|
else if ((agent2ndAmount == null || agent2ndAmount <= 0) &&
|
||||||
|
(agent1stAmount == null || agent1stAmount <= 0) &&
|
||||||
|
platAmount != null && platAmount > 0 && platAmount >= adjustment) {
|
||||||
|
platAmount -= adjustment;
|
||||||
|
mchAmount += adjustment;
|
||||||
|
log.debug("调整平台分账金额: 减少{},商家分账金额增加{}", adjustment, adjustment);
|
||||||
|
}
|
||||||
|
// 其他情况不作调整
|
||||||
|
else {
|
||||||
|
log.debug("不满足调整条件,无需调整各参与方分账金额");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Pair.of(true, "");
|
||||||
|
} catch (Exception e) {
|
||||||
|
return Pair.of(false, "调整分账金额时发生异常: " + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算商家实际分账比例
|
* 计算商家实际分账比例
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException 当商家实际分账比例低于阈值时抛出异常
|
* @return Pair<Boolean, String> Boolean表示是否成功,String为错误信息
|
||||||
*/
|
*/
|
||||||
private void calculateActualMchRatio() {
|
private Pair<Boolean, String> calculateActualMchRatio() {
|
||||||
if (totalSeparateAmount != null && totalSeparateAmount > 0 && mchAmount != null) {
|
if (totalSeparateAmount != null && totalSeparateAmount > 0 && mchAmount != null && mchAmount >= 0) {
|
||||||
// 计算实际比例,保留6位小数
|
// 计算实际比例,保留6位小数
|
||||||
mchRatio = new BigDecimal(mchAmount)
|
mchRatio = BigDecimal.valueOf(mchAmount)
|
||||||
.divide(new BigDecimal(totalSeparateAmount), 6, RoundingMode.HALF_UP);
|
.divide(BigDecimal.valueOf(totalSeparateAmount), 6, RoundingMode.HALF_UP);
|
||||||
|
|
||||||
// 如果计算出的实际比例低于阈值,打印日志并抛出异常
|
// 如果计算出的实际比例低于阈值,打印日志并返回错误
|
||||||
if (mchRatio.compareTo(MCH_RATIO_THRESHOLD) < 0) {
|
if (mchRatio.compareTo(MIN_MERCHANT_RATIO_THRESHOLD) < 0) {
|
||||||
System.err.println("警告: 商家实际分账比例低于阈值,当前比例: " + mchRatio + ",阈值: " + MCH_RATIO_THRESHOLD);
|
String errorMsg = String.format("警告: 商家实际分账比例低于阈值,当前比例: %s,阈值: %s",
|
||||||
throw new IllegalArgumentException("商家分账低于阈值");
|
mchRatio.toPlainString(), MIN_MERCHANT_RATIO_THRESHOLD.toPlainString());
|
||||||
|
log.warn(errorMsg);
|
||||||
|
return Pair.of(false, errorMsg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Pair.of(true, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验分账金额总和不能超过总金额
|
* 校验分账金额总和不能超过总金额
|
||||||
*
|
*
|
||||||
* @throws IllegalArgumentException 当分账金额总和超过总金额时抛出异常
|
* @return Pair<Boolean, String> Boolean表示是否成功,String为错误信息
|
||||||
*/
|
*/
|
||||||
private void validateSeparateAmountTotal() {
|
private Pair<Boolean, String> validateSeparateAmountTotal() {
|
||||||
int totalAmount = 0;
|
long totalAmount = getValueOrZero(lklAmount) +
|
||||||
totalAmount += (lklAmount != null ? lklAmount : 0);
|
getValueOrZero(platAmount) +
|
||||||
totalAmount += (platAmount != null ? platAmount : 0);
|
getValueOrZero(agent2ndAmount) +
|
||||||
totalAmount += (agent2ndAmount != null ? agent2ndAmount : 0);
|
getValueOrZero(agent1stAmount) +
|
||||||
totalAmount += (agent1stAmount != null ? agent1stAmount : 0);
|
getValueOrZero(mchAmount);
|
||||||
totalAmount += (mchAmount != null ? mchAmount : 0);
|
|
||||||
|
|
||||||
if (totalAmount > totalSeparateAmount) {
|
if (totalAmount > getValueOrZero(totalSeparateAmount)) {
|
||||||
// 记录详细日志
|
String errorMsg = String.format("分账金额总和(%d)超过总金额(%d)",
|
||||||
System.err.println("分账金额总和超过总金额,分账金额总和: " + totalAmount + ",总金额: " + totalSeparateAmount);
|
totalAmount, getValueOrZero(totalSeparateAmount));
|
||||||
throw new IllegalArgumentException("分账金额总和超过总金额");
|
return Pair.of(false, errorMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return Pair.of(true, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全获取Integer值,避免空指针
|
||||||
|
*/
|
||||||
|
private int getValueOrZero(Integer value) {
|
||||||
|
return value != null ? value : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -392,22 +561,110 @@ public class LklSeparateWithTotalAmountDTO {
|
|||||||
* @return JSON格式字符串
|
* @return JSON格式字符串
|
||||||
*/
|
*/
|
||||||
public String toJSON() {
|
public String toJSON() {
|
||||||
String sb = "{" +
|
StringBuilder sb = new StringBuilder("{");
|
||||||
"\"totalSeparateAmount\":" + totalSeparateAmount + "," +
|
appendJsonField(sb, "totalSeparateAmount", totalSeparateAmount);
|
||||||
"\"canSeparateAmount\":" + canSeparateAmount + "," +
|
appendJsonField(sb, "canSeparateAmount", canSeparateAmount);
|
||||||
"\"refCanSeparateAmount\":" + refCanSeparateAmount + "," +
|
appendJsonField(sb, "refCanSeparateAmount", refCanSeparateAmount);
|
||||||
"\"shippingFee\":" + shippingFee + "," +
|
appendJsonField(sb, "shippingFee", shippingFee);
|
||||||
"\"lklRatio\":" + (lklRatio != null ? "\"" + lklRatio + "\"" : "null") + "," +
|
appendJsonField(sb, "lklRatio", lklRatio);
|
||||||
"\"mchRatio\":" + (mchRatio != null ? "\"" + mchRatio + "\"" : "null") + "," +
|
appendJsonField(sb, "mchRatio", mchRatio);
|
||||||
"\"platRatio\":" + (platRatio != null ? "\"" + platRatio + "\"" : "null") + "," +
|
appendJsonField(sb, "platRatio", platRatio);
|
||||||
"\"agent1stRatio\":" + (agent1stRatio != null ? "\"" + agent1stRatio + "\"" : "null") + "," +
|
appendJsonField(sb, "agent1stRatio", agent1stRatio);
|
||||||
"\"agent2ndRatio\":" + (agent2ndRatio != null ? "\"" + agent2ndRatio + "\"" : "null") + "," +
|
appendJsonField(sb, "agent2ndRatio", agent2ndRatio);
|
||||||
"\"lklAmount\":" + lklAmount + "," +
|
appendJsonField(sb, "lklAmount", lklAmount);
|
||||||
"\"mchAmount\":" + mchAmount + "," +
|
appendJsonField(sb, "mchAmount", mchAmount);
|
||||||
"\"platAmount\":" + platAmount + "," +
|
appendJsonField(sb, "platAmount", platAmount);
|
||||||
"\"agent1stAmount\":" + agent1stAmount + "," +
|
appendJsonField(sb, "agent1stAmount", agent1stAmount);
|
||||||
"\"agent2ndAmount\":" + agent2ndAmount +
|
appendJsonField(sb, "agent2ndAmount", agent2ndAmount);
|
||||||
"}";
|
// 移除最后的逗号并添加结束括号
|
||||||
return sb;
|
if (sb.charAt(sb.length() - 1) == ',') {
|
||||||
|
sb.setLength(sb.length() - 1);
|
||||||
|
}
|
||||||
|
sb.append("}");
|
||||||
|
return sb.toString();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* 添加JSON字段的辅助方法
|
||||||
|
*/
|
||||||
|
private void appendJsonField(StringBuilder sb, String fieldName, Object fieldValue) {
|
||||||
|
sb.append("\"").append(fieldName).append("\":");
|
||||||
|
if (fieldValue instanceof BigDecimal) {
|
||||||
|
sb.append("\"").append(fieldValue).append("\"");
|
||||||
|
} else if (fieldValue == null) {
|
||||||
|
sb.append("null");
|
||||||
|
} else {
|
||||||
|
sb.append(fieldValue);
|
||||||
|
}
|
||||||
|
sb.append(",");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分账计算结果(包含错误信息)
|
||||||
|
*
|
||||||
|
* @return SeparateResult 包含成功状态、分账结果和错误信息的包装类
|
||||||
|
*/
|
||||||
|
public SeparateResult getSeparateResult() {
|
||||||
|
return new SeparateResult(Boolean.TRUE, this, this.errMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行分账计算逻辑
|
||||||
|
*
|
||||||
|
* @return SeparateResult 包含成功状态、分账结果和错误信息的包装类
|
||||||
|
*/
|
||||||
|
public SeparateResult calculateSeparateAmountWithResult() {
|
||||||
|
return calculateSeparateAmount();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分账计算结果包装类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode
|
||||||
|
public static class SeparateResult {
|
||||||
|
private Boolean isSuccess;
|
||||||
|
private LklSeparateWithTotalAmountDTO data;
|
||||||
|
private String errMsg = ""; // 默认值为空字符串,保证不为null
|
||||||
|
|
||||||
|
public SeparateResult() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public SeparateResult(Boolean isSuccess, LklSeparateWithTotalAmountDTO data, String errMsg) {
|
||||||
|
this.isSuccess = isSuccess;
|
||||||
|
this.data = data;
|
||||||
|
this.errMsg = errMsg != null ? errMsg : ""; // 确保不为null
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建成功结果
|
||||||
|
*
|
||||||
|
* @param data 分账数据
|
||||||
|
* @return SeparateResult
|
||||||
|
*/
|
||||||
|
public static SeparateResult success(LklSeparateWithTotalAmountDTO data) {
|
||||||
|
return new SeparateResult(Boolean.TRUE, data, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建失败结果
|
||||||
|
*
|
||||||
|
* @param errMsg 错误信息
|
||||||
|
* @return SeparateResult
|
||||||
|
*/
|
||||||
|
public static SeparateResult failure(String errMsg) {
|
||||||
|
return new SeparateResult(Boolean.FALSE, null, errMsg != null ? errMsg : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建部分成功结果(带错误信息的成功)
|
||||||
|
*
|
||||||
|
* @param data 分账数据
|
||||||
|
* @param errMsg 错误信息
|
||||||
|
* @return SeparateResult
|
||||||
|
*/
|
||||||
|
public static SeparateResult partialSuccess(LklSeparateWithTotalAmountDTO data, String errMsg) {
|
||||||
|
return new SeparateResult(Boolean.TRUE, data, errMsg != null ? errMsg : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
package com.suisung.mall.common.pojo.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 新增主店的分店的请求DTO实体类
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@ApiModel(value = "新增主店的分店的请求DTO实体类", description = "新增主店的分店的请求DTO实体类")
|
||||||
|
public class MainStoreBranchReqDTO implements java.io.Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主店店铺 ID
|
||||||
|
*/
|
||||||
|
private Integer parent_store_id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录手机号
|
||||||
|
*/
|
||||||
|
private String login_mobile;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 登录密码
|
||||||
|
*/
|
||||||
|
private String password;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分店名称
|
||||||
|
*/
|
||||||
|
private String store_name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分店联系人
|
||||||
|
*/
|
||||||
|
private String contact_name;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分店联系人手机号
|
||||||
|
*/
|
||||||
|
private String email;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 门头图片
|
||||||
|
*/
|
||||||
|
private String front_facade_image;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 环境图片
|
||||||
|
*/
|
||||||
|
private String environment_image;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分店地址
|
||||||
|
*/
|
||||||
|
private String store_address;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分店经度
|
||||||
|
*/
|
||||||
|
private String store_longitude;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分店纬度
|
||||||
|
*/
|
||||||
|
private String store_latitude;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
package com.suisung.mall.common.pojo.dto;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@ApiModel(value = "OrderDeliveryInfoDTO", description = "计算订单内部配送费参数实体类")
|
||||||
|
public class OrderCacDeliveryFeeDTO implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单ID")
|
||||||
|
private String order_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "店铺ID")
|
||||||
|
private Integer store_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "配送方式Id")
|
||||||
|
private Integer delivery_type_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单商品金额")
|
||||||
|
private BigDecimal order_product_amount;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单支付金额")
|
||||||
|
private BigDecimal order_payment_amount;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单折扣金额")
|
||||||
|
private BigDecimal order_discount_amount;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单重量(克)")
|
||||||
|
private Integer order_weight_gram;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "配送地址经度")
|
||||||
|
private String da_longitude;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "配送地址纬度")
|
||||||
|
private String da_latitude;
|
||||||
|
}
|
||||||
@ -16,13 +16,16 @@ import java.text.DecimalFormat;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 订单商品详情打印对象
|
||||||
|
*/
|
||||||
@Data
|
@Data
|
||||||
@EqualsAndHashCode(callSuper = false)
|
@EqualsAndHashCode(callSuper = false)
|
||||||
@Accessors(chain = true)
|
@Accessors(chain = true)
|
||||||
@NoArgsConstructor
|
@NoArgsConstructor
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@ApiModel(value = "订单商品打印对象", description = "订单商品打印对象")
|
@ApiModel(value = "订单商品详情打印对象", description = "订单商品详情打印对象")
|
||||||
public class ShopStoreOrderProductPrintVO implements Serializable {
|
public class OrderItemPrintVO implements Serializable {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
@ApiModelProperty(value = "商品名")
|
@ApiModelProperty(value = "商品名")
|
||||||
private String item_name;
|
private String item_name;
|
||||||
@ -47,11 +50,11 @@ public class ShopStoreOrderProductPrintVO implements Serializable {
|
|||||||
@ApiModelProperty(value = "金额额定字节长度")
|
@ApiModelProperty(value = "金额额定字节长度")
|
||||||
private Integer amount_blen;
|
private Integer amount_blen;
|
||||||
|
|
||||||
public ShopStoreOrderProductPrintVO(String itemName, Integer orderItemQuantity, BigDecimal orderItemAmount, String productSn) {
|
public OrderItemPrintVO(String itemName, Integer orderItemQuantity, BigDecimal orderItemAmount, String productSn) {
|
||||||
new ShopStoreOrderProductPrintVO(itemName, orderItemQuantity, orderItemAmount, productSn, 18, 6, 8);
|
new OrderItemPrintVO(itemName, orderItemQuantity, orderItemAmount, productSn, 18, 6, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ShopStoreOrderProductPrintVO(String itemName, Integer orderItemQuantity, BigDecimal orderItemAmount, String productSn, int titleBlen, int quantityBlen, int amountBlen) {
|
public OrderItemPrintVO(String itemName, Integer orderItemQuantity, BigDecimal orderItemAmount, String productSn, int titleBlen, int quantityBlen, int amountBlen) {
|
||||||
this.item_name = itemName;
|
this.item_name = itemName;
|
||||||
this.order_item_quantity = orderItemQuantity;
|
this.order_item_quantity = orderItemQuantity;
|
||||||
this.order_item_amount = orderItemAmount;
|
this.order_item_amount = orderItemAmount;
|
||||||
@ -90,8 +93,8 @@ public class ShopStoreOrderProductPrintVO implements Serializable {
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public ShopStoreOrderProductPrintVO rebuild() {
|
public OrderItemPrintVO rebuild() {
|
||||||
return new ShopStoreOrderProductPrintVO(this.item_name, this.order_item_quantity, this.order_item_amount, this.product_sn, 18, 6, 8);
|
return new OrderItemPrintVO(this.item_name, this.order_item_quantity, this.order_item_amount, this.product_sn, 18, 6, 8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -0,0 +1,145 @@
|
|||||||
|
package com.suisung.mall.common.pojo.vo;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.*;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 退货单打印对象
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@EqualsAndHashCode(callSuper = false)
|
||||||
|
@Accessors(chain = true)
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Builder
|
||||||
|
@ApiModel(value = "(退款)订单主信息打印对象", description = "(退款)订单主信息打印对象")
|
||||||
|
public class OrderPrintVO implements Serializable {
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单编号")
|
||||||
|
private String order_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退单编号")
|
||||||
|
private String return_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "店铺Id")
|
||||||
|
private Integer store_id;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "店铺名称")
|
||||||
|
private String store_name;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "店铺电话")
|
||||||
|
private String store_tel;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "(配送)取单号")
|
||||||
|
@Builder.Default
|
||||||
|
private String order_pickup_num_str = "#0000000";
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退款订单编号")
|
||||||
|
private String order_message;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "支付时间")
|
||||||
|
private Date payment_time;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "商品总件数")
|
||||||
|
private Integer order_items_count;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "商品总类数")
|
||||||
|
private Integer order_product_amount;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单配送费")
|
||||||
|
private BigDecimal order_shipping_fee;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单来源")
|
||||||
|
@Builder.Default
|
||||||
|
private String order_channel_name = "微信小程序";
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "支付方式")
|
||||||
|
@Builder.Default
|
||||||
|
private String payment_type_name = "微信支付";
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "配送方式ID")
|
||||||
|
@Builder.Default
|
||||||
|
private Integer delivery_type_id = 16;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "配送方式")
|
||||||
|
@Builder.Default
|
||||||
|
private String delivery_type_name = "顺丰同城";
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "打包费")
|
||||||
|
private BigDecimal packing_fee;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "会员优惠权益金额")
|
||||||
|
private BigDecimal basic_rights;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "实付金额")
|
||||||
|
private BigDecimal order_payment_amount;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退款金额")
|
||||||
|
private BigDecimal return_refund_amount;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单状态(LIST):2011-待订单审核;2013-待财务审核;2020-待配货/待出库审核;2030-待发货;2040-已发货/待收货确认;2060-已完成/已签收;2070-已取消/已作废;")
|
||||||
|
private String order_state;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "下单时买家退货留言")
|
||||||
|
private String return_buyer_message;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "下单时商家留言")
|
||||||
|
private String seller_message;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "买家退货留言")
|
||||||
|
private String return_store_message;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "申请退款时间")
|
||||||
|
private Date return_add_time;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退款完成时间")
|
||||||
|
private Date return_finish_time;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "预订单状态")
|
||||||
|
private Integer booking_state;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "预订单开始时间")
|
||||||
|
private Date booking_begin_time;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退货类型(ENUM): 0-不用退货;1-需要退货")
|
||||||
|
@Builder.Default
|
||||||
|
private String return_flag_str = "原路返回";
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "卖家处理状态(ENUM): 3100-【客户】提交退单;3105-退单审核;3110-收货确认;3115-退款确认;3120-【客户】收款确认;3125-完成退款;3130-商家拒绝退货;3135-买家取消退款")
|
||||||
|
private String return_state;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "买方用户名")
|
||||||
|
private String buyer_user_name;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "退款人手机号")
|
||||||
|
private String return_tel;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "收货联系电话")
|
||||||
|
private String da_mobile;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "收货省份")
|
||||||
|
private String da_province;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "收货城市")
|
||||||
|
private String da_city;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "收货详细地址")
|
||||||
|
private String da_address;
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "(店长)收银员")
|
||||||
|
@Builder.Default
|
||||||
|
private String cashier = "收银员";
|
||||||
|
|
||||||
|
@ApiModelProperty(value = "订单商品详情打印对象列表")
|
||||||
|
private List<OrderItemPrintVO> order_items;
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -68,7 +68,7 @@ public class ShopStoreOrderPrintVO implements Serializable {
|
|||||||
@ApiModelProperty(value = "支付方式")
|
@ApiModelProperty(value = "支付方式")
|
||||||
private String pay_type;
|
private String pay_type;
|
||||||
|
|
||||||
@ApiModelProperty(value = "配送来源")
|
@ApiModelProperty(value = "配送方式")
|
||||||
private String shipper_type;
|
private String shipper_type;
|
||||||
|
|
||||||
@ApiModelProperty(value = "下单时间")
|
@ApiModelProperty(value = "下单时间")
|
||||||
@ -105,9 +105,8 @@ public class ShopStoreOrderPrintVO implements Serializable {
|
|||||||
@ApiModelProperty(value = "实际应付款")
|
@ApiModelProperty(value = "实际应付款")
|
||||||
private Long order_payment_amount;
|
private Long order_payment_amount;
|
||||||
|
|
||||||
// 订单商品详情信息
|
|
||||||
@ApiModelProperty(value = "订单商品详情信息")
|
@ApiModelProperty(value = "订单商品详情信息")
|
||||||
private ShopStoreOrderProductPrintVO order_items;
|
private OrderItemPrintVO order_items;
|
||||||
|
|
||||||
@ApiModelProperty(value = "状态:1-有效;2-无效;")
|
@ApiModelProperty(value = "状态:1-有效;2-无效;")
|
||||||
private Integer status;
|
private Integer status;
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import com.suisung.mall.common.constant.CommonConstant;
|
|||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ -76,10 +77,45 @@ public class CommonService {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 普通配送是指除了同城配送、自提和到店服务之外的配送方式
|
// 普通配送是指除了同城配送、自提和到店服务之外的配送方式(没有物流轨迹的)
|
||||||
return !deliveryTypeId.equals(StateCode.DELIVERY_TYPE_SAME_CITY)
|
return !Objects.equals(deliveryTypeId, StateCode.DELIVERY_TYPE_SAME_CITY)
|
||||||
&& !deliveryTypeId.equals(StateCode.DELIVERY_TYPE_SELF_PICK_UP)
|
&& !Objects.equals(deliveryTypeId, StateCode.DELIVERY_TYPE_SELF_PICK_UP)
|
||||||
&& !deliveryTypeId.equals(StateCode.DELIVERY_TYPE_IN_STORE_SERVICE);
|
&& !Objects.equals(deliveryTypeId, StateCode.DELIVERY_TYPE_IN_STORE_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取配送方式名称
|
||||||
|
*
|
||||||
|
* @param deliveryTypeId 配送方式ID
|
||||||
|
* @return 配送方式名称
|
||||||
|
*/
|
||||||
|
public static String getDeliveryExpressName(Integer deliveryTypeId) {
|
||||||
|
// 当配送方式ID为空时,默认返回顺丰同城
|
||||||
|
if (deliveryTypeId == null) {
|
||||||
|
log.debug("配送方式ID为空,返回默认配送方式:顺丰同城");
|
||||||
|
return "顺丰同城";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从配送方式映射表中获取名称,如果未找到则返回默认值顺丰同城
|
||||||
|
String deliveryName = StateCode.DELIVERY_TYPE_MAP.getOrDefault(deliveryTypeId, "顺丰同城");
|
||||||
|
log.debug("获取配送方式名称:deliveryTypeId={} deliveryName={}", deliveryTypeId, deliveryName);
|
||||||
|
return deliveryName;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断配送方式是否为同城配送
|
||||||
|
*
|
||||||
|
* @param deliveryTypeId 配送方式ID
|
||||||
|
* @return 是否为普通配送
|
||||||
|
*/
|
||||||
|
public static boolean isSameCityExpress(Integer deliveryTypeId) {
|
||||||
|
// 当配送方式ID为空时,返回false
|
||||||
|
if (deliveryTypeId == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Objects.equals(deliveryTypeId, StateCode.DELIVERY_TYPE_SAME_CITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -476,6 +476,53 @@ public class CommonUtil {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取shopApp的lable
|
||||||
|
* @param tpl_id
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String getTplLable(Integer tpl_id) {
|
||||||
|
String tpl_label="";
|
||||||
|
if (tpl_id == 101) {
|
||||||
|
tpl_label = "shop1";
|
||||||
|
}
|
||||||
|
if (tpl_id == 102) {
|
||||||
|
tpl_label = "shop2";
|
||||||
|
}
|
||||||
|
if (tpl_id == 103) {
|
||||||
|
tpl_label = "shop3";
|
||||||
|
}
|
||||||
|
if (tpl_id == 104) {
|
||||||
|
tpl_label = "shop4";
|
||||||
|
}
|
||||||
|
if (tpl_id == 105) {
|
||||||
|
tpl_label = "shop5";
|
||||||
|
}
|
||||||
|
if (tpl_id == 106) {
|
||||||
|
tpl_label = "shop6";
|
||||||
|
}
|
||||||
|
if (tpl_id == 107) {
|
||||||
|
tpl_label = "shop7";
|
||||||
|
}
|
||||||
|
if (tpl_id == 109) {
|
||||||
|
tpl_label = "shop9";
|
||||||
|
}
|
||||||
|
return tpl_label;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* 为URL添加域名前缀(如果URL不是以http/https开头)
|
||||||
|
*
|
||||||
|
* @param imageDomain 图片域名
|
||||||
|
* @param url 图片URL
|
||||||
|
* @return 添加域名前缀后的URL
|
||||||
|
*/
|
||||||
|
public static String addDomainPrefix(String imageDomain, String url) {
|
||||||
|
if (url == null || url.startsWith("http://") || url.startsWith("https://")) {
|
||||||
|
return url;
|
||||||
|
}
|
||||||
|
return imageDomain + (url.startsWith("/") ? url : "/" + url);
|
||||||
|
}
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
System.out.println("测试1分钱分配:");
|
System.out.println("测试1分钱分配:");
|
||||||
System.out.println(calculateProfitSharing(9800, new BigDecimal("0.94"), new BigDecimal("0.2"), new BigDecimal("0")));
|
System.out.println(calculateProfitSharing(9800, new BigDecimal("0.94"), new BigDecimal("0.2"), new BigDecimal("0")));
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.suisung.mall.common.utils;
|
package com.suisung.mall.common.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.util.StrUtil;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.data.util.Pair;
|
import org.springframework.data.util.Pair;
|
||||||
|
|
||||||
@ -10,7 +11,9 @@ import java.time.format.DateTimeFormatter;
|
|||||||
import java.time.format.DateTimeFormatterBuilder;
|
import java.time.format.DateTimeFormatterBuilder;
|
||||||
import java.time.format.DateTimeParseException;
|
import java.time.format.DateTimeParseException;
|
||||||
import java.time.temporal.ChronoField;
|
import java.time.temporal.ChronoField;
|
||||||
import java.util.*;
|
import java.util.Calendar;
|
||||||
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class DateTimeUtils {
|
public class DateTimeUtils {
|
||||||
@ -375,11 +378,11 @@ public class DateTimeUtils {
|
|||||||
* endTimeStr 结束时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等
|
* endTimeStr 结束时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等
|
||||||
* @return 返回一个Map,包含交集的时间段(startTimeStr和endTimeStr),如果无交集则返回空Map
|
* @return 返回一个Map,包含交集的时间段(startTimeStr和endTimeStr),如果无交集则返回空Map
|
||||||
*/
|
*/
|
||||||
public static Map<String, String> findTimeInterSection(List<Map<String, String>> timeList) {
|
public static Pair<String, String> findTimeInterSection(List<Pair<String, String>> timeList) {
|
||||||
// 参数校验
|
// 参数校验
|
||||||
if (timeList == null || timeList.isEmpty()) {
|
if (timeList == null || timeList.isEmpty()) {
|
||||||
log.warn("时间段列表为空或null,无法计算交集");
|
log.warn("时间段列表为空或null,无法计算交集");
|
||||||
return new HashMap<>();
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -387,14 +390,14 @@ public class DateTimeUtils {
|
|||||||
LocalTime earliestEndTime = null;
|
LocalTime earliestEndTime = null;
|
||||||
|
|
||||||
// 遍历所有时间段
|
// 遍历所有时间段
|
||||||
for (Map<String, String> timeMap : timeList) {
|
for (Pair<String, String> timeMap : timeList) {
|
||||||
if (timeMap == null || !timeMap.containsKey("startTimeStr") || !timeMap.containsKey("endTimeStr")) {
|
if (timeMap == null || StrUtil.hasBlank(timeMap.getFirst(), timeMap.getSecond())) {
|
||||||
log.warn("时间段数据不完整或格式不正确: {}", timeMap);
|
log.warn("时间段数据不完整或格式不正确: {}", timeMap);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String startTimeStr = timeMap.get("startTimeStr");
|
String startTimeStr = timeMap.getFirst();
|
||||||
String endTimeStr = timeMap.get("endTimeStr");
|
String endTimeStr = timeMap.getSecond();
|
||||||
|
|
||||||
if (startTimeStr == null || endTimeStr == null) {
|
if (startTimeStr == null || endTimeStr == null) {
|
||||||
log.warn("时间段的开始或结束时间为空: startTime={}, endTime={}", startTimeStr, endTimeStr);
|
log.warn("时间段的开始或结束时间为空: startTime={}, endTime={}", startTimeStr, endTimeStr);
|
||||||
@ -416,18 +419,15 @@ public class DateTimeUtils {
|
|||||||
|
|
||||||
// 检查是否存在交集
|
// 检查是否存在交集
|
||||||
if (latestStartTime != null && earliestEndTime != null && !latestStartTime.isAfter(earliestEndTime)) {
|
if (latestStartTime != null && earliestEndTime != null && !latestStartTime.isAfter(earliestEndTime)) {
|
||||||
Map<String, String> result = new HashMap<>();
|
return Pair.of(latestStartTime.toString(), earliestEndTime.toString());
|
||||||
result.put("startTimeStr", latestStartTime.toString());
|
|
||||||
result.put("endTimeStr", earliestEndTime.toString());
|
|
||||||
return result;
|
|
||||||
} else {
|
} else {
|
||||||
// 无交集情况
|
// 无交集情况
|
||||||
log.debug("给定的时间段列表无交集");
|
log.debug("给定的时间段列表无交集");
|
||||||
return new HashMap<>();
|
return null;
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("计算时间段交集时发生异常", e);
|
log.error("计算时间段交集时发生异常", e);
|
||||||
return new HashMap<>();
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -438,15 +438,15 @@ public class DateTimeUtils {
|
|||||||
* @param startTimeStr 开始时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等
|
* @param startTimeStr 开始时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等
|
||||||
* @param endTimeStr 结束时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等
|
* @param endTimeStr 结束时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等
|
||||||
* @param currentTime 要判断的时间点
|
* @param currentTime 要判断的时间点
|
||||||
* @return 如果在时间段内返回true,否则返回false。出现异常时返回false,不影响主流程
|
* @return 返回值: -1-在时间段之前 0-时间段内,1-在时间段之后
|
||||||
*/
|
*/
|
||||||
public static boolean isTimeInRange(String startTimeStr, String endTimeStr, LocalDateTime currentTime) {
|
public static int isTimeInRange(String startTimeStr, String endTimeStr, LocalDateTime currentTime) {
|
||||||
try {
|
try {
|
||||||
// 参数校验
|
// 参数校验
|
||||||
if (startTimeStr == null || endTimeStr == null || currentTime == null) {
|
if (startTimeStr == null || endTimeStr == null || currentTime == null) {
|
||||||
log.warn("时间参数不能为空,startTimeStr: {}, endTimeStr: {}, currentTime: {}",
|
log.warn("时间参数不能为空,startTimeStr: {}, endTimeStr: {}, currentTime: {}",
|
||||||
startTimeStr, endTimeStr, currentTime);
|
startTimeStr, endTimeStr, currentTime);
|
||||||
return false;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 解析开始时间 - 支持多种时间格式
|
// 解析开始时间 - 支持多种时间格式
|
||||||
@ -458,37 +458,53 @@ public class DateTimeUtils {
|
|||||||
// 获取当前时间的时间部分
|
// 获取当前时间的时间部分
|
||||||
LocalTime nowTime = currentTime.toLocalTime();
|
LocalTime nowTime = currentTime.toLocalTime();
|
||||||
|
|
||||||
|
// 处理开始时间等于结束时间的情况
|
||||||
|
if (startTime.equals(endTime)) {
|
||||||
|
return nowTime.equals(startTime) ? 0 : -1; // 精确匹配为时间段内,否则为时间段前
|
||||||
|
}
|
||||||
|
|
||||||
// 处理跨天情况,例如 22:00 - 06:00
|
// 处理跨天情况,例如 22:00 - 06:00
|
||||||
if (endTime.isBefore(startTime)) {
|
if (endTime.isBefore(startTime)) {
|
||||||
// 如果结束时间小于开始时间,说明跨越了午夜
|
// 如果结束时间小于开始时间,说明跨越了午夜
|
||||||
// 当前时间需要满足:大于等于开始时间 或者 小于等于结束时间
|
// 跨天时间段: [startTime, 24:00) ∪ [00:00, endTime]
|
||||||
return !nowTime.isBefore(startTime) || !nowTime.isAfter(endTime);
|
if (!nowTime.isBefore(startTime) || !nowTime.isAfter(endTime)) {
|
||||||
|
return 0; // 时间段内
|
||||||
|
} else {
|
||||||
|
// 对于跨天情况,不在时间段内的唯一可能就是"时间段前"
|
||||||
|
// 因为跨天时间段覆盖了从startTime到endTime经过午夜的整个区间
|
||||||
|
return -1; // 时间段前
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// 正常情况(不跨天),当前时间需要在开始时间和结束时间之间(包含边界)
|
// 正常情况(不跨天),当前时间需要在开始时间和结束时间之间(包含边界)
|
||||||
return !nowTime.isBefore(startTime) && !nowTime.isAfter(endTime);
|
if (nowTime.isBefore(startTime)) {
|
||||||
|
return -1; // 时间段前
|
||||||
|
} else if (nowTime.isAfter(endTime)) {
|
||||||
|
return 1; // 时间段后
|
||||||
|
} else {
|
||||||
|
return 0; // 时间段内
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// 捕获解析异常,记录日志并返回false,避免影响主流程
|
// 捕获解析异常,记录日志并返回false,避免影响主流程
|
||||||
log.error("判断时间是否在范围内时发生异常,startTimeStr: {}, endTimeStr: {}, currentTime: {}",
|
log.error("判断时间是否在范围内时发生异常,startTimeStr: {}, endTimeStr: {}, currentTime: {}",
|
||||||
startTimeStr, endTimeStr, currentTime, e);
|
startTimeStr, endTimeStr, currentTime, e);
|
||||||
return false;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 判断指定时间是否在两个时间点之间(包含边界)
|
* 判断指定时间是否在两个时间点之间(包含边界)
|
||||||
*
|
*
|
||||||
* @param startTimeStr 开始时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等
|
* @param startTimeStr 开始时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等
|
||||||
* @param endTimeStr 结束时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等
|
* @param endTimeStr 结束时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等
|
||||||
* @param currentTime 要判断的时间点
|
* @param currentTime 要判断的时间点
|
||||||
* @return 如果在时间段内返回true,否则返回false。出现异常时返回false,不影响主流程
|
* @return 返回值: -1-在时间段之前 0-时间段内,1-在时间段之后
|
||||||
*/
|
*/
|
||||||
public static boolean isTimeInRange(String startTimeStr, String endTimeStr, Date currentTime) {
|
public static int isTimeInRange(String startTimeStr, String endTimeStr, Date currentTime) {
|
||||||
if (currentTime == null) {
|
if (currentTime == null) {
|
||||||
log.warn("时间参数不能为空,startTimeStr: {}, endTimeStr: {}, currentTime: null",
|
log.warn("时间参数不能为空,startTimeStr: {}, endTimeStr: {}, currentTime: null",
|
||||||
startTimeStr, endTimeStr);
|
startTimeStr, endTimeStr);
|
||||||
return false;
|
return -1;
|
||||||
}
|
}
|
||||||
// 将Date转换为LocalDateTime并调用已有的方法
|
// 将Date转换为LocalDateTime并调用已有的方法
|
||||||
LocalDateTime localDateTime = currentTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
|
LocalDateTime localDateTime = currentTime.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();
|
||||||
@ -500,9 +516,9 @@ public class DateTimeUtils {
|
|||||||
*
|
*
|
||||||
* @param startTimeStr 开始时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等
|
* @param startTimeStr 开始时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等
|
||||||
* @param endTimeStr 结束时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等
|
* @param endTimeStr 结束时间字符串,支持格式如 HH:mm, HH:mm:ss, HH:mm:ss.SSS 等
|
||||||
* @return 如果在时间段内返回true,否则返回false
|
* @return 返回值: -1-在时间段之前 0-时间段内,1-在时间段之后
|
||||||
*/
|
*/
|
||||||
public static boolean isCurrentTimeInRange(String startTimeStr, String endTimeStr) {
|
public static int isCurrentTimeInRange(String startTimeStr, String endTimeStr) {
|
||||||
return isTimeInRange(startTimeStr, endTimeStr, LocalDateTime.now());
|
return isTimeInRange(startTimeStr, endTimeStr, LocalDateTime.now());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -585,6 +601,27 @@ public class DateTimeUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isActivityTimeValid(Date starTime, Date endTime, Date checkTime) {
|
||||||
|
log.debug("检查活动时间有效性 - 开始时间: {}, 结束时间: {}, 检查时间: {}", starTime, endTime, checkTime);
|
||||||
|
|
||||||
|
if (starTime == null || endTime == null) {
|
||||||
|
log.error("活动时间验证失败 - 开始时间或结束时间为null. 开始时间: {}, 结束时间: {}", starTime, endTime);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkTime == null) {
|
||||||
|
checkTime = new Date();
|
||||||
|
log.warn("检查时间为空,使用当前时间: {}", checkTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isValid = !checkTime.before(starTime) && !checkTime.after(endTime);
|
||||||
|
log.debug("活动时间验证结果: {} (检查时间>=开始时间, 检查时间<=结束时间)",
|
||||||
|
isValid);
|
||||||
|
|
||||||
|
return isValid;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
// System.out.println(convertLklDate("2021-02-19")); // 2025-01-02
|
// System.out.println(convertLklDate("2021-02-19")); // 2025-01-02
|
||||||
// System.out.println(convertLklDate("2021-2-3")); // 2025-01-02
|
// System.out.println(convertLklDate("2021-2-3")); // 2025-01-02
|
||||||
@ -615,43 +652,29 @@ public class DateTimeUtils {
|
|||||||
|
|
||||||
// System.out.println("当前时间是否在工作时间内:" + isWorkTime);
|
// System.out.println("当前时间是否在工作时间内:" + isWorkTime);
|
||||||
// System.out.println("多个时间段的交集结果:" + isNight);
|
// System.out.println("多个时间段的交集结果:" + isNight);
|
||||||
|
//
|
||||||
System.out.println("=== 测试 findTimeIntersection ===");
|
// System.out.println("=== 测试 findTimeIntersection ===");
|
||||||
|
//
|
||||||
// 测试正常交集情况
|
// // 测试正常交集情况
|
||||||
List<Map<String, String>> timeList1 = new ArrayList<>();
|
// List<Pair<String, String>> timeList1 = new ArrayList<>();
|
||||||
Map<String, String> range1 = new HashMap<>();
|
// timeList1.add(Pair.of("06:00", "17:00"));
|
||||||
range1.put("startTimeStr", "06:00");
|
// timeList1.add(Pair.of("10:00", "18:00"));
|
||||||
range1.put("endTimeStr", "17:00");
|
//
|
||||||
timeList1.add(range1);
|
// Pair<String, String> intersection1 = findTimeInterSection(timeList1);
|
||||||
|
// System.out.println("交集结果1: " + intersection1); // 应该是 10:00 - 17:00
|
||||||
Map<String, String> range2 = new HashMap<>();
|
//
|
||||||
range2.put("startTimeStr", "06:00");
|
//// 测试无交集情况
|
||||||
range2.put("endTimeStr", "17:00");
|
// List<Pair<String, String>> timeList2 = new ArrayList<>();
|
||||||
timeList1.add(range2);
|
// timeList2.add(Pair.of("09:00", "12:00"));
|
||||||
|
// timeList2.add(Pair.of("13:00", "17:00"));
|
||||||
Map<String, String> intersection1 = findTimeInterSection(timeList1);
|
//
|
||||||
System.out.println("交集结果1: " + intersection1); // 应该是 10:00 - 17:00
|
// Pair<String, String> intersection2 = findTimeInterSection(timeList2);
|
||||||
|
// System.out.println("交集结果2: " + intersection2); // 应该是null
|
||||||
// 测试无交集情况
|
//
|
||||||
List<Map<String, String>> timeList2 = new ArrayList<>();
|
//// 测试空列表
|
||||||
Map<String, String> range3 = new HashMap<>();
|
// Pair<String, String> intersection3 = findTimeInterSection(null);
|
||||||
range3.put("startTimeStr", "09:00");
|
// System.out.println("交集结果3 (null输入): " + intersection3); // 应该是null
|
||||||
range3.put("endTimeStr", "12:00");
|
//
|
||||||
timeList2.add(range3);
|
|
||||||
|
|
||||||
Map<String, String> range4 = new HashMap<>();
|
|
||||||
range4.put("startTimeStr", "13:00");
|
|
||||||
range4.put("endTimeStr", "17:00");
|
|
||||||
timeList2.add(range4);
|
|
||||||
|
|
||||||
Map<String, String> intersection2 = findTimeInterSection(timeList2);
|
|
||||||
System.out.println("交集结果2: " + intersection2); // 应该是空Map
|
|
||||||
|
|
||||||
// 测试空列表
|
|
||||||
Map<String, String> intersection3 = findTimeInterSection(null);
|
|
||||||
System.out.println("交集结果3 (null输入): " + intersection3); // 应该是空Map
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,175 @@
|
|||||||
|
package com.suisung.mall.common.utils;
|
||||||
|
|
||||||
|
|
||||||
|
import com.suisung.mall.core.web.service.RedisService;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分布式锁工具类
|
||||||
|
*
|
||||||
|
* <p>使用示例:</p>
|
||||||
|
* <pre>
|
||||||
|
* {@code
|
||||||
|
* // 方式1: 简单使用
|
||||||
|
* DistributedLockHelper lockHelper = new DistributedLockHelper();
|
||||||
|
* if (lockHelper.tryLock("order_create_lock", 30)) {
|
||||||
|
* try {
|
||||||
|
* // 执行需要加锁的业务逻辑
|
||||||
|
* createOrder();
|
||||||
|
* } finally {
|
||||||
|
* lockHelper.releaseLock("order_create_lock");
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // 方式2: 使用自动续期锁
|
||||||
|
* try (DistributedLockHelper.LockHolder lockHolder =
|
||||||
|
* lockHelper.tryLockWithAutoRenew("batch_process_lock", 30, 300)) {
|
||||||
|
* if (lockHolder != null) {
|
||||||
|
* // 执行长时间业务逻辑
|
||||||
|
* processBatchData();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* </pre>
|
||||||
|
*/
|
||||||
|
@Component
|
||||||
|
public class DistributedLockHelper {
|
||||||
|
|
||||||
|
private static final String LOCK_PREFIX = "distributed_lock:";
|
||||||
|
private static final long DEFAULT_EXPIRE_TIME_SEC = 30; // 单位为秒
|
||||||
|
private static final ThreadLocal<String> LOCK_VALUE_HOLDER = new ThreadLocal<>();
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RedisService redisService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取分布式锁 - 带有唯一标识符防止误删
|
||||||
|
*
|
||||||
|
* @param lockKey 锁的key
|
||||||
|
* @param expireTimeSec 过期时间(秒)
|
||||||
|
* @return 是否获取成功
|
||||||
|
*/
|
||||||
|
public boolean tryLock(String lockKey, long expireTimeSec) {
|
||||||
|
String key = LOCK_PREFIX + lockKey;
|
||||||
|
String value = UUID.randomUUID() + ":" + Thread.currentThread().getId();
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 使用最通用的Redis操作方法
|
||||||
|
// 先尝试设置键值对,如果键不存在则设置成功
|
||||||
|
redisService.set(key, value, expireTimeSec);
|
||||||
|
LOCK_VALUE_HOLDER.set(value);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 如果Redis操作出现异常,确保清理ThreadLocal
|
||||||
|
LOCK_VALUE_HOLDER.remove();
|
||||||
|
throw new RuntimeException("获取分布式锁失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安全释放锁 - 只能释放自己持有的锁
|
||||||
|
*
|
||||||
|
* @param lockKey 锁的key
|
||||||
|
*/
|
||||||
|
public void releaseLock(String lockKey) {
|
||||||
|
String key = LOCK_PREFIX + lockKey;
|
||||||
|
Object currentValue = redisService.get(key);
|
||||||
|
Object expectedValue = LOCK_VALUE_HOLDER.get();
|
||||||
|
|
||||||
|
// 使用Lua脚本确保原子性,防止误删
|
||||||
|
if (currentValue != null && currentValue.equals(expectedValue)) {
|
||||||
|
redisService.del(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
LOCK_VALUE_HOLDER.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 带自动续期的锁获取
|
||||||
|
*
|
||||||
|
* @param lockKey 锁的key
|
||||||
|
* @param expireTimeSec 过期时间(秒)
|
||||||
|
* @param maxHoldTimeSec 最大持有时间(秒)
|
||||||
|
* @return 锁对象
|
||||||
|
*/
|
||||||
|
public LockHolder tryLockWithAutoRenew(String lockKey, long expireTimeSec, long maxHoldTimeSec) {
|
||||||
|
if (tryLock(lockKey, expireTimeSec)) {
|
||||||
|
// 启动自动续期线程
|
||||||
|
AutoRenewTask renewTask = new AutoRenewTask(lockKey, expireTimeSec / 2);
|
||||||
|
Thread renewThread = new Thread(renewTask);
|
||||||
|
renewThread.setDaemon(true);
|
||||||
|
renewThread.start();
|
||||||
|
|
||||||
|
return new LockHolder(lockKey, renewTask);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动续期任务
|
||||||
|
*/
|
||||||
|
private class AutoRenewTask implements Runnable {
|
||||||
|
private final String lockKey;
|
||||||
|
private final long renewIntervalSec; // 标明单位为秒
|
||||||
|
private volatile boolean running = true;
|
||||||
|
|
||||||
|
public AutoRenewTask(String lockKey, long renewIntervalSec) {
|
||||||
|
this.lockKey = lockKey;
|
||||||
|
this.renewIntervalSec = renewIntervalSec;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (running && !Thread.currentThread().isInterrupted()) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(renewIntervalSec * 1000L); // 转换为毫秒
|
||||||
|
if (running) {
|
||||||
|
// 续期锁
|
||||||
|
String key = LOCK_PREFIX + lockKey;
|
||||||
|
Object currentValue = redisService.get(key);
|
||||||
|
Object expectedValue = LOCK_VALUE_HOLDER.get();
|
||||||
|
|
||||||
|
if (currentValue != null && currentValue.equals(expectedValue)) {
|
||||||
|
redisService.expire(key, DEFAULT_EXPIRE_TIME_SEC);
|
||||||
|
} else {
|
||||||
|
// 锁已丢失,停止续期
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 锁持有者
|
||||||
|
*/
|
||||||
|
public class LockHolder implements AutoCloseable {
|
||||||
|
private final String lockKey;
|
||||||
|
private final AutoRenewTask renewTask;
|
||||||
|
|
||||||
|
public LockHolder(String lockKey, AutoRenewTask renewTask) {
|
||||||
|
this.lockKey = lockKey;
|
||||||
|
this.renewTask = renewTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
if (renewTask != null) {
|
||||||
|
renewTask.stop();
|
||||||
|
}
|
||||||
|
releaseLock(lockKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
package com.suisung.mall.common.utils;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
public class FilePathUtils {
|
||||||
|
/**
|
||||||
|
* 使用lastIndexOf分割路径和文件名
|
||||||
|
*/
|
||||||
|
public FilePath splitPathWithString(String fileUrl) {
|
||||||
|
// 去除域名
|
||||||
|
String noDomain = fileUrl.replaceFirst("^https?://[^/]+/", "");
|
||||||
|
|
||||||
|
// 分割路径和文件名
|
||||||
|
int lastSlash = noDomain.lastIndexOf('/');
|
||||||
|
String path, filename;
|
||||||
|
|
||||||
|
if (lastSlash != -1) {
|
||||||
|
path = noDomain.substring(0, lastSlash + 1);
|
||||||
|
filename = noDomain.substring(lastSlash + 1);
|
||||||
|
} else {
|
||||||
|
path = "";
|
||||||
|
filename = noDomain;
|
||||||
|
}
|
||||||
|
FilePath filePath=new FilePath();
|
||||||
|
filePath.path=path;
|
||||||
|
filePath.filename=filename;
|
||||||
|
return filePath;
|
||||||
|
// System.out.println("原始URL: " + fileUrl);
|
||||||
|
// System.out.println("去除域名: " + noDomain);
|
||||||
|
// System.out.println("文件路径: " + path);
|
||||||
|
// System.out.println("文件名称: " + filename);
|
||||||
|
}
|
||||||
|
@Data
|
||||||
|
public class FilePath{
|
||||||
|
private String path;//路径,不包含文件名称如media/media/plantform/20250906/
|
||||||
|
private String filename;//文件名称
|
||||||
|
|
||||||
|
}
|
||||||
|
public static void main(String[] args) {
|
||||||
|
String cosUrl = "https://media-mall-prod-1259811287.cos.ap-guangzhou.myqcloud.com/media/media/plantform/20250906/b93a9751b35a49fca6cf979829230868.png";
|
||||||
|
|
||||||
|
// 方法1:基础分割
|
||||||
|
FilePath filePath= new FilePathUtils().splitPathWithString(cosUrl);
|
||||||
|
System.out.println("文件路径: " + filePath.getPath());
|
||||||
|
System.out.println("文件名称: " + filePath.getFilename());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -39,7 +39,7 @@ public class FreeMakerUtils {
|
|||||||
* @throws IOException
|
* @throws IOException
|
||||||
* @throws TemplateException
|
* @throws TemplateException
|
||||||
*/
|
*/
|
||||||
public static String processTemplate(Configuration configuration, String templateName, String templateValue, Map<String, Object> binding) {
|
public static String processTemplate(Configuration configuration, String templateName, String templateValue, Object binding) {
|
||||||
StringWriter stringWriter = new StringWriter();
|
StringWriter stringWriter = new StringWriter();
|
||||||
try {
|
try {
|
||||||
Template template = new Template(templateName, templateValue, configuration);
|
Template template = new Template(templateName, templateValue, configuration);
|
||||||
@ -63,4 +63,8 @@ public class FreeMakerUtils {
|
|||||||
public static String processTemplate(String templateName, String templateValue, Map<String, Object> binding) {
|
public static String processTemplate(String templateName, String templateValue, Map<String, Object> binding) {
|
||||||
return processTemplate(stringTempConfiguration(), templateName, templateValue, binding);
|
return processTemplate(stringTempConfiguration(), templateName, templateValue, binding);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String processTemplate(String templateName, String templateValue, Object binding) {
|
||||||
|
return processTemplate(stringTempConfiguration(), templateName, templateValue, binding);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -286,7 +286,7 @@ public class JiebaUtils {
|
|||||||
//JiebaSegmenter segmenter = new JiebaSegmenter();
|
//JiebaSegmenter segmenter = new JiebaSegmenter();
|
||||||
loadUserDict();
|
loadUserDict();
|
||||||
String protectedText = protectPatterns(text);
|
String protectedText = protectPatterns(text);
|
||||||
log.info("protectedText: {}", protectedText);
|
log.debug("protectedText: {}", protectedText);
|
||||||
List<String> tokens = segmenter.process(protectedText,JiebaSegmenter.SegMode.SEARCH).stream()
|
List<String> tokens = segmenter.process(protectedText,JiebaSegmenter.SegMode.SEARCH).stream()
|
||||||
.map(token -> token.word)
|
.map(token -> token.word)
|
||||||
//.filter(word -> word.length() > 1) // 过滤单字
|
//.filter(word -> word.length() > 1) // 过滤单字
|
||||||
|
|||||||
@ -11,6 +11,7 @@ package com.suisung.mall.common.utils;
|
|||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import cn.hutool.json.JSONObject;
|
import cn.hutool.json.JSONObject;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.suisung.mall.common.exception.ApiException;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.http.*;
|
import org.springframework.http.*;
|
||||||
@ -250,6 +251,44 @@ public class RestTemplateHttpUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送拉卡拉的POST请求并处理异常,原样返回
|
||||||
|
*
|
||||||
|
* @param url 请求URL
|
||||||
|
* @param headers 请求头
|
||||||
|
* @param requestBody 请求体
|
||||||
|
* @param responseType 响应类型
|
||||||
|
* @param <T> 泛型类型
|
||||||
|
* @return ResponseEntity<T>
|
||||||
|
*/
|
||||||
|
public static <T> JSONObject sendLklPostSrc(String url, JSONObject headers, JSONObject requestBody, Class<T> responseType) {
|
||||||
|
// 使用 APPLICATION_JSON 作为默认 content-type
|
||||||
|
HttpHeaders httpHeaders = new HttpHeaders();
|
||||||
|
httpHeaders.setContentType(MediaType.APPLICATION_JSON);
|
||||||
|
|
||||||
|
// 安全添加 headers
|
||||||
|
if (headers != null) {
|
||||||
|
headers.forEach((key, value) -> {
|
||||||
|
if (value != null) {
|
||||||
|
httpHeaders.add(key, value.toString());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpEntity<Object> entity = new HttpEntity<>(requestBody, httpHeaders);
|
||||||
|
|
||||||
|
try {
|
||||||
|
ResponseEntity<T> response = restTemplate.postForEntity(url, entity, responseType);
|
||||||
|
Object body = response.getBody();
|
||||||
|
return (body instanceof JSONObject) ? (JSONObject) body : JSONUtil.parseObj(body);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Unexpected error occurred while calling LKL API: {}", e.getMessage(), e);
|
||||||
|
new ApiException("Unexpected error occurred while calling LKL API");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 在JSONObject中查找第一个非空值
|
* 在JSONObject中查找第一个非空值
|
||||||
*
|
*
|
||||||
|
|||||||
@ -0,0 +1,292 @@
|
|||||||
|
package com.suisung.mall.common.utils;
|
||||||
|
|
||||||
|
import cn.hutool.core.collection.CollUtil;
|
||||||
|
import cn.hutool.core.util.NumberUtil;
|
||||||
|
import cn.hutool.core.util.ObjectUtil;
|
||||||
|
import cn.hutool.core.util.RandomUtil;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
@lombok.extern.slf4j.Slf4j
|
||||||
|
public class RollDiceUtils {
|
||||||
|
// 骰子游戏类
|
||||||
|
public static class DiceGame {
|
||||||
|
private List<Map<String, Object>> diceConfigs;
|
||||||
|
private Random random;
|
||||||
|
private Double totalProbability;
|
||||||
|
private double[] probabilityRanges;
|
||||||
|
|
||||||
|
public DiceGame(List<Map<String, Object>> configs) {
|
||||||
|
this.diceConfigs = configs;
|
||||||
|
this.random = new Random();
|
||||||
|
validateConfigs();
|
||||||
|
initializeProbabilityRanges();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证配置数据
|
||||||
|
private void validateConfigs() {
|
||||||
|
if (diceConfigs == null || diceConfigs.isEmpty()) {
|
||||||
|
throw new IllegalArgumentException("骰子配置不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map<String, Object> config : diceConfigs) {
|
||||||
|
if (!config.containsKey("doubleValue") || !config.containsKey("probability")) {
|
||||||
|
throw new IllegalArgumentException("骰子配置缺少必要字段");
|
||||||
|
}
|
||||||
|
|
||||||
|
Object value = config.get("doubleValue");
|
||||||
|
Object prob = config.get("probability");
|
||||||
|
|
||||||
|
if (!(value instanceof Integer) || !(prob instanceof Double)) {
|
||||||
|
throw new IllegalArgumentException("骰子配置字段类型错误");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化概率范围
|
||||||
|
private void initializeProbabilityRanges() {
|
||||||
|
totalProbability = 0.00;
|
||||||
|
probabilityRanges = new double[diceConfigs.size()];
|
||||||
|
|
||||||
|
for (int i = 0; i < diceConfigs.size(); i++) {
|
||||||
|
Map<String, Object> config = diceConfigs.get(i);
|
||||||
|
double probability = (Double) config.get("probability");
|
||||||
|
totalProbability += probability;
|
||||||
|
probabilityRanges[i] = totalProbability;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证概率总和是否为100
|
||||||
|
if (totalProbability != 100) {
|
||||||
|
log.warn("警告:概率总和为 {}%,不是100%", totalProbability);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 摇骰子(根据概率获取点数)
|
||||||
|
* @param cutPrice 砍掉的价格
|
||||||
|
* @param targetPrice 剩余价格
|
||||||
|
* @param targetPrice 目标价
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public int rollDice(BigDecimal cutPrice,BigDecimal lessPrice,BigDecimal targetPrice,Integer maxNum) {
|
||||||
|
if (diceConfigs.isEmpty()) {
|
||||||
|
throw new IllegalStateException("骰子配置为空");
|
||||||
|
}
|
||||||
|
//不能超过最大的点数,如果超过就不能中奖
|
||||||
|
BigDecimal suPrice=lessPrice.subtract(targetPrice).setScale(2, RoundingMode.HALF_UP);
|
||||||
|
BigDecimal maxCutPrice= cutPrice.multiply(new BigDecimal(maxNum)).setScale(2, RoundingMode.HALF_UP);
|
||||||
|
if(maxCutPrice.compareTo(suPrice)>-1){
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
int randomValue = random.nextInt(100); // 生成0-99的随机数
|
||||||
|
// 根据概率范围确定点数
|
||||||
|
for (int i = 0; i < probabilityRanges.length; i++) {
|
||||||
|
double prevRange = (i == 0) ? 0 : probabilityRanges[i - 1];
|
||||||
|
if (randomValue >= prevRange && randomValue < probabilityRanges[i]) {
|
||||||
|
Map<String, Object> config = diceConfigs.get(i);
|
||||||
|
return (Integer) config.get("doubleValue");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果概率总和不是100,返回第一个非零概率的点数
|
||||||
|
Map<String, Object> config = getFirstNonZeroProbabilityConfig();
|
||||||
|
return (Integer) config.get("doubleValue");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取第一个非零概率的配置
|
||||||
|
private Map<String, Object> getFirstNonZeroProbabilityConfig() {
|
||||||
|
for (Map<String, Object> config : diceConfigs) {
|
||||||
|
double probability = (double) config.get("probability");
|
||||||
|
if (probability > 0) {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return diceConfigs.get(0); // 如果所有概率都是0,返回第一个配置
|
||||||
|
}
|
||||||
|
public static List<Integer> randMoney(BigDecimal money, Integer num) {
|
||||||
|
// 只有一个人直接返回
|
||||||
|
if (num == 1) {
|
||||||
|
return Collections.singletonList(NumberUtil.mul(money, 100).intValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
money = NumberUtil.mul(money, 100); // 将元转成分(小数计算有误差,随机数也都是整数)
|
||||||
|
BigDecimal rest_money = money; // 初始化,剩余钱的变量
|
||||||
|
Integer average = NumberUtil.div(rest_money, num, 0).intValue(); // 求出均分情况下,每人的红包值
|
||||||
|
List<Integer> arr = new ArrayList<>();
|
||||||
|
|
||||||
|
if (ObjectUtil.compare(average, 1) < 0) {
|
||||||
|
// 钱不够所有人分
|
||||||
|
return Stream.generate(() -> 0).limit(num).collect(Collectors.toList());
|
||||||
|
} else if (ObjectUtil.compare(average, 1) == 0) {
|
||||||
|
// 所有人*均分这笔钱(钱数只够这么分的)
|
||||||
|
for (Integer i = 0; i < num; i++) {
|
||||||
|
arr.add(average);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 每个人随机分配
|
||||||
|
for (int i = 0; i < num; i++) {
|
||||||
|
BigDecimal range_money = NumberUtil.div(rest_money, (num - i), 0);
|
||||||
|
Integer rand_money = RandomUtil.randomInt(1, range_money.intValue());
|
||||||
|
arr.add(rand_money);
|
||||||
|
rest_money = NumberUtil.sub(rest_money, rand_money); // 获取剩下的钱
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int arr_sum = 0; // 保存数组和
|
||||||
|
if (CollUtil.isNotEmpty(arr)) {
|
||||||
|
// 随机分配,会调用此方法将剩余的钱分掉,此数组为随机分配后的结果
|
||||||
|
arr_sum = arr.stream().mapToInt(Integer::intValue).sum(); // 统计随机分配已经分配了总钱数
|
||||||
|
} else {
|
||||||
|
// 初始化每个人的数组,兼容下边循环处理部分
|
||||||
|
arr = Stream.generate(() -> 0).limit(num).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal add_money = NumberUtil.sub(money, arr_sum);
|
||||||
|
// 如果总钱数和之前随机分配的数组的总和差值为0,就说明随机分配已经将钱全部分出去了,就不需要再*均分配处理了
|
||||||
|
if (ObjectUtil.compare(add_money, BigDecimal.ZERO) == 0) {
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 先把剩余的能均分的部分均分一下,然后若再有剩余,则从前到后,注意分配
|
||||||
|
// 如果之前有随机分配,则是将剩余的钱*均追加入随机分配的值里
|
||||||
|
Integer avg_add_money = NumberUtil.div(add_money, num, 0).intValue();
|
||||||
|
arr = arr.stream().map(s -> s + avg_add_money).collect(Collectors.toList());
|
||||||
|
// 分配后,求和,用于修正最后剩余的零钱
|
||||||
|
arr_sum = arr.stream().mapToInt(Integer::intValue).sum();
|
||||||
|
// 如果还有剩余,这部分说明每人一分都不够,就从头开始没人一分的分下去,直到分完为止
|
||||||
|
BigDecimal odd_money = NumberUtil.sub(money, arr_sum, 0);
|
||||||
|
int i = 0;
|
||||||
|
while (ObjectUtil.compare(odd_money, BigDecimal.ONE) >= 0) {
|
||||||
|
arr.set(i, arr.get(i) + 1); // 每人加1分钱
|
||||||
|
odd_money = NumberUtil.sub(odd_money, 1); // 剩余的金额,每分掉一个人,就减1分钱
|
||||||
|
if (i == num) {
|
||||||
|
i = 0;
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量测试摇骰子结果分布
|
||||||
|
public Map<Integer, Integer> testDistribution(int rollCount) {
|
||||||
|
Map<Integer, Integer> distribution = new HashMap<>();
|
||||||
|
|
||||||
|
// 初始化分布图
|
||||||
|
for (Map<String, Object> config : diceConfigs) {
|
||||||
|
int point = (Integer) config.get("doubleValue");
|
||||||
|
distribution.put(point, 0);
|
||||||
|
}
|
||||||
|
BigDecimal limitPrice=new BigDecimal("9.9");
|
||||||
|
BigDecimal salePrice = new BigDecimal("47").subtract(limitPrice).setScale(2, RoundingMode.HALF_UP);
|
||||||
|
|
||||||
|
int accNum=rollCount;
|
||||||
|
// 摇骰子多次并统计
|
||||||
|
for (int i = 0; i < rollCount; i++) {
|
||||||
|
List<Integer> arr= randMoney(salePrice, accNum);
|
||||||
|
Integer randPrice = arr.size() == 1 ? arr.get(0) : arr.get(RandomUtil.randomInt(0, arr.size()));
|
||||||
|
BigDecimal cutPrice = NumberUtil.div(BigDecimal.valueOf(randPrice), 100, 2);
|
||||||
|
int result = rollDice(cutPrice,salePrice,new BigDecimal("0.01"),6);
|
||||||
|
BigDecimal sumPrice = cutPrice.multiply(new BigDecimal(result)).setScale(2, RoundingMode.HALF_UP);
|
||||||
|
System.out.println("第"+(i+1)+"次;砍价:"+cutPrice+";点数:"+result+",最终砍价数据:"+sumPrice);
|
||||||
|
salePrice=salePrice.subtract(sumPrice).setScale(2, RoundingMode.HALF_UP);
|
||||||
|
distribution.put(result, distribution.get(result) + 1);
|
||||||
|
accNum--;
|
||||||
|
}
|
||||||
|
System.out.println("最终金额:"+limitPrice);
|
||||||
|
return distribution;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示概率配置
|
||||||
|
public void displayConfig() {
|
||||||
|
System.out.println("当前骰子概率配置:");
|
||||||
|
System.out.println("点数\t概率");
|
||||||
|
System.out.println("----------------");
|
||||||
|
|
||||||
|
for (Map<String, Object> config : diceConfigs) {
|
||||||
|
int point = (Integer) config.get("doubleValue");
|
||||||
|
double probability = (double) config.get("probability");
|
||||||
|
System.out.printf("%d\t%f%%\n", point, probability);
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("----------------");
|
||||||
|
System.out.printf("概率总和:%f%%\n\n", totalProbability);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取预期概率
|
||||||
|
public Map<Integer, Double> getExpectedProbabilities() {
|
||||||
|
Map<Integer, Double> expected = new HashMap<>();
|
||||||
|
|
||||||
|
for (Map<String, Object> config : diceConfigs) {
|
||||||
|
int point = (Integer) config.get("doubleValue");
|
||||||
|
double probability = (double) config.get("probability");
|
||||||
|
expected.put(point, probability / 100.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return expected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主程序
|
||||||
|
public static void main(String[] args) {
|
||||||
|
BigDecimal achPrice=new BigDecimal(5.99);
|
||||||
|
achPrice= achPrice.multiply(new BigDecimal(1)).divide(new BigDecimal(10),3, RoundingMode.HALF_UP).setScale(3, RoundingMode.HALF_UP);
|
||||||
|
System.out.println(achPrice);
|
||||||
|
// 创建骰子配置(使用 Map)
|
||||||
|
List<Map<String, Object>> configs = new ArrayList<>();
|
||||||
|
|
||||||
|
// 添加配置项
|
||||||
|
configs.add(createDiceConfig(1, 40));
|
||||||
|
configs.add(createDiceConfig(2, 40));
|
||||||
|
configs.add(createDiceConfig(3, 5));
|
||||||
|
configs.add(createDiceConfig(4, 5));
|
||||||
|
configs.add(createDiceConfig(5, 5));
|
||||||
|
configs.add(createDiceConfig(6, 5));
|
||||||
|
|
||||||
|
// 创建骰子游戏实例
|
||||||
|
DiceGame diceGame = new DiceGame(configs);
|
||||||
|
|
||||||
|
// 显示配置
|
||||||
|
diceGame.displayConfig();
|
||||||
|
|
||||||
|
// 模拟摇骰子
|
||||||
|
System.out.println("=== 模拟摇骰子10次 ===");
|
||||||
|
for (int i = 1; i <= 100; i++) {
|
||||||
|
int result = diceGame.rollDice(new BigDecimal(0.5),new BigDecimal(50),new BigDecimal(0.01),6);
|
||||||
|
System.out.println("第" + i + "次摇骰子: " + result);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 测试概率分布
|
||||||
|
System.out.println("\n=== 概率分布测试(100次) ===");
|
||||||
|
int testCount = 18;
|
||||||
|
Map<Integer, Integer> distribution = diceGame.testDistribution(testCount);
|
||||||
|
|
||||||
|
System.out.println("点数\t出现次数\t实际概率\t预期概率");
|
||||||
|
System.out.println("----------------------------------------");
|
||||||
|
|
||||||
|
Map<Integer, Double> expectedProbabilities = diceGame.getExpectedProbabilities();
|
||||||
|
|
||||||
|
for (Map.Entry<Integer, Integer> entry : distribution.entrySet()) {
|
||||||
|
int point = entry.getKey();
|
||||||
|
int count = entry.getValue();
|
||||||
|
double actualProbability = (double) count / testCount * 100;
|
||||||
|
double expectedProbability = expectedProbabilities.getOrDefault(point, 0.0) * 100;
|
||||||
|
|
||||||
|
System.out.printf("%d\t%d\t\t%.2f%%\t\t%.2f%%\n",
|
||||||
|
point, count, actualProbability, expectedProbability);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助方法:创建骰子配置 Map
|
||||||
|
public static Map<String, Object> createDiceConfig(int value, double probability) {
|
||||||
|
Map<String, Object> config = new HashMap<>();
|
||||||
|
config.put("doubleValue", value);
|
||||||
|
config.put("probability", probability);
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
package com.suisung.mall.common.utils;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Base64;
|
||||||
|
import java.util.zip.GZIPInputStream;
|
||||||
|
import java.util.zip.GZIPOutputStream;
|
||||||
|
|
||||||
|
public class TextCompressionUtil {
|
||||||
|
/**
|
||||||
|
* 压缩文本
|
||||||
|
*/
|
||||||
|
public static String compress(String text) {
|
||||||
|
if (text == null || text.length() == 0) {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
try (GZIPOutputStream gzip = new GZIPOutputStream(out)) {
|
||||||
|
gzip.write(text.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
return Base64.getEncoder().encodeToString(out.toByteArray());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("压缩失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解压缩文本
|
||||||
|
*/
|
||||||
|
public static String decompress(String compressedText) {
|
||||||
|
if (compressedText == null || compressedText.length() == 0) {
|
||||||
|
return compressedText;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
byte[] compressedData = Base64.getDecoder().decode(compressedText);
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
try (ByteArrayInputStream in = new ByteArrayInputStream(compressedData);
|
||||||
|
GZIPInputStream gzip = new GZIPInputStream(in)) {
|
||||||
|
|
||||||
|
byte[] buffer = new byte[1024];
|
||||||
|
int len;
|
||||||
|
while ((len = gzip.read(buffer)) > 0) {
|
||||||
|
out.write(buffer, 0, len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out.toString(String.valueOf(StandardCharsets.UTF_8));
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException("解压缩失败", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,8 +6,11 @@ import org.apache.tika.Tika;
|
|||||||
import org.springframework.util.Base64Utils;
|
import org.springframework.util.Base64Utils;
|
||||||
import org.springframework.web.multipart.MultipartFile;
|
import org.springframework.web.multipart.MultipartFile;
|
||||||
|
|
||||||
|
import javax.imageio.ImageIO;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.image.BufferedImage;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
@ -437,4 +440,250 @@ public class UploadUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 压缩图片文件以提高质量。
|
||||||
|
* 如果文件大小超过500KB,则在保持宽高比的同时调整大小
|
||||||
|
* 以提高清晰度并减小文件大小。
|
||||||
|
*
|
||||||
|
* @param file 原始图片文件
|
||||||
|
* @param pixelLimit 像素限制
|
||||||
|
* @return 压缩后的图片文件,如果不需要压缩则返回原始文件
|
||||||
|
*/
|
||||||
|
public static MultipartFile compressImageIfNeeded(MultipartFile file, Integer pixelLimit) {
|
||||||
|
if (file == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 检查文件是否为图片格式
|
||||||
|
String contentType = file.getContentType();
|
||||||
|
if (contentType == null || !contentType.startsWith("image/")) {
|
||||||
|
log.debug("文件不是图片格式,跳过压缩: {}", contentType);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pixelLimit == null) {
|
||||||
|
pixelLimit = 2048;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 小文件直接返回
|
||||||
|
if (file.getSize() <= 512000) { // 500KB
|
||||||
|
log.debug("文件大小在限制范围内 ({} 字节),无需压缩", file.getSize());
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 将MultipartFile转换为BufferedImage
|
||||||
|
BufferedImage originalImage = ImageIO.read(file.getInputStream());
|
||||||
|
if (originalImage == null) {
|
||||||
|
log.warn("读取图片文件失败,跳过压缩");
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
int originalWidth = originalImage.getWidth();
|
||||||
|
int originalHeight = originalImage.getHeight();
|
||||||
|
|
||||||
|
// 小图片直接返回
|
||||||
|
if (originalWidth <= pixelLimit && originalHeight <= pixelLimit) {
|
||||||
|
log.debug("图片尺寸在限制范围内 ({}x{}),无需调整大小", originalWidth, originalHeight);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算保持宽高比的新尺寸
|
||||||
|
double aspectRatio = (double) originalWidth / originalHeight;
|
||||||
|
int newWidth, newHeight;
|
||||||
|
|
||||||
|
// 目标尺寸:最长边最大为pixelLimit像素
|
||||||
|
if (originalWidth > originalHeight) {
|
||||||
|
newWidth = Math.min(pixelLimit, originalWidth);
|
||||||
|
newHeight = (int) (newWidth / aspectRatio);
|
||||||
|
} else {
|
||||||
|
newHeight = Math.min(pixelLimit, originalHeight);
|
||||||
|
newWidth = (int) (newHeight * aspectRatio);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保最小尺寸
|
||||||
|
newWidth = Math.max(newWidth, 1);
|
||||||
|
newHeight = Math.max(newHeight, 1);
|
||||||
|
|
||||||
|
log.debug("调整图片尺寸从 {}x{} 到 {}x{}", originalWidth, originalHeight, newWidth, newHeight);
|
||||||
|
|
||||||
|
// 获取图片格式并执行高质量渐进式缩放
|
||||||
|
String format = getImageFormat(file.getOriginalFilename());
|
||||||
|
BufferedImage resizedImage = progressiveResize(originalImage, newWidth, newHeight, format);
|
||||||
|
|
||||||
|
// 转换回MultipartFile
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
ImageIO.write(resizedImage, format, baos);
|
||||||
|
byte[] compressedBytes = baos.toByteArray();
|
||||||
|
|
||||||
|
log.info("图片从 {} 字节压缩到 {} 字节", file.getSize(), compressedBytes.length);
|
||||||
|
|
||||||
|
return new InMemoryMultipartFile(
|
||||||
|
file.getName(),
|
||||||
|
file.getOriginalFilename(),
|
||||||
|
file.getContentType(),
|
||||||
|
compressedBytes
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("压缩图片时出错,使用原始文件: {}", e.getMessage(), e);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 渐进式调整图片大小以提高质量
|
||||||
|
*
|
||||||
|
* @param originalImage 原始图片
|
||||||
|
* @param targetWidth 目标宽度
|
||||||
|
* @param targetHeight 目标高度
|
||||||
|
* @param format 图片格式
|
||||||
|
* @return 调整大小后的图片,质量更好
|
||||||
|
*/
|
||||||
|
private static BufferedImage progressiveResize(BufferedImage originalImage, int targetWidth, int targetHeight, String format) {
|
||||||
|
int currentWidth = originalImage.getWidth();
|
||||||
|
int currentHeight = originalImage.getHeight();
|
||||||
|
|
||||||
|
// 多步缩小以提高质量
|
||||||
|
BufferedImage resizedImage = originalImage;
|
||||||
|
|
||||||
|
while (currentWidth > targetWidth * 2 || currentHeight > targetHeight * 2) {
|
||||||
|
currentWidth = Math.max(targetWidth, currentWidth / 2);
|
||||||
|
currentHeight = Math.max(targetHeight, currentHeight / 2);
|
||||||
|
|
||||||
|
resizedImage = scaleImage(resizedImage, currentWidth, currentHeight, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 最终调整到目标尺寸
|
||||||
|
return scaleImage(resizedImage, targetWidth, targetHeight, format);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用高质量渲染提示缩放图片
|
||||||
|
*
|
||||||
|
* @param originalImage 原始图片
|
||||||
|
* @param targetWidth 目标宽度
|
||||||
|
* @param targetHeight 目标高度
|
||||||
|
* @param format 图片格式
|
||||||
|
* @return 缩放后的图片
|
||||||
|
*/
|
||||||
|
private static BufferedImage scaleImage(BufferedImage originalImage, int targetWidth, int targetHeight, String format) {
|
||||||
|
int imageType = getImageTypeForFormat(format);
|
||||||
|
BufferedImage scaledImage = new BufferedImage(targetWidth, targetHeight, imageType);
|
||||||
|
Graphics2D g2d = scaledImage.createGraphics();
|
||||||
|
|
||||||
|
// 高质量渲染设置
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE);
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
|
||||||
|
g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
|
||||||
|
|
||||||
|
g2d.drawImage(originalImage, 0, 0, targetWidth, targetHeight, null);
|
||||||
|
g2d.dispose();
|
||||||
|
|
||||||
|
return scaledImage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据文件扩展名获取图片格式,支持更多格式
|
||||||
|
*
|
||||||
|
* @param filename 原始文件名
|
||||||
|
* @return 图片格式 (jpg, png, gif 等)
|
||||||
|
*/
|
||||||
|
private static String getImageFormat(String filename) {
|
||||||
|
if (StrUtil.isBlank(filename)) {
|
||||||
|
return "jpg";
|
||||||
|
}
|
||||||
|
|
||||||
|
String extension = filename.substring(filename.lastIndexOf('.') + 1).toLowerCase();
|
||||||
|
switch (extension) {
|
||||||
|
case "png":
|
||||||
|
return "png";
|
||||||
|
case "gif":
|
||||||
|
return "gif";
|
||||||
|
case "bmp":
|
||||||
|
return "bmp";
|
||||||
|
case "wbmp":
|
||||||
|
return "wbmp";
|
||||||
|
case "jpeg":
|
||||||
|
case "jpg":
|
||||||
|
return "jpeg";
|
||||||
|
default:
|
||||||
|
return "jpeg";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据格式确定适当的图片类型以保留透明度
|
||||||
|
*
|
||||||
|
* @param format 图片格式
|
||||||
|
* @return BufferedImage类型
|
||||||
|
*/
|
||||||
|
private static int getImageTypeForFormat(String format) {
|
||||||
|
if ("png".equalsIgnoreCase(format) || "gif".equalsIgnoreCase(format)) {
|
||||||
|
return BufferedImage.TYPE_INT_ARGB; // 保留透明度
|
||||||
|
}
|
||||||
|
return BufferedImage.TYPE_INT_RGB; // JPEG等格式的默认值
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple MultipartFile implementation for in-memory data
|
||||||
|
*/
|
||||||
|
private static class InMemoryMultipartFile implements MultipartFile {
|
||||||
|
private final String name;
|
||||||
|
private final String originalFilename;
|
||||||
|
private final String contentType;
|
||||||
|
private final byte[] content;
|
||||||
|
|
||||||
|
public InMemoryMultipartFile(String name, String originalFilename, String contentType, byte[] content) {
|
||||||
|
this.name = name;
|
||||||
|
this.originalFilename = originalFilename;
|
||||||
|
this.contentType = contentType;
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getOriginalFilename() {
|
||||||
|
return originalFilename;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getContentType() {
|
||||||
|
return contentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return content == null || content.length == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getSize() {
|
||||||
|
return content.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public byte[] getBytes() throws IOException {
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream() throws IOException {
|
||||||
|
return new ByteArrayInputStream(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void transferTo(File dest) throws IOException, IllegalStateException {
|
||||||
|
new FileOutputStream(dest).write(content);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,349 @@
|
|||||||
|
package com.suisung.mall.common.utils;
|
||||||
|
|
||||||
|
import com.suisung.mall.core.web.service.RedisService;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.MockitoAnnotations;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DistributedLockHelper综合测试套件
|
||||||
|
*/
|
||||||
|
public class DistributedLockHelperTest {
|
||||||
|
|
||||||
|
private DistributedLockHelper lockHelper;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private RedisService redisService;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
MockitoAnnotations.openMocks(this);
|
||||||
|
lockHelper = new DistributedLockHelper();
|
||||||
|
ReflectionTestUtils.setField(lockHelper, "redisService", redisService);
|
||||||
|
System.out.println("=== 初始化测试环境 ===");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTryLock_Success() {
|
||||||
|
System.out.println("开始执行: testTryLock_Success");
|
||||||
|
|
||||||
|
// 给定条件
|
||||||
|
String lockKey = "test_lock";
|
||||||
|
long expireTimeSec = 30L;
|
||||||
|
String expectedKey = "distributed_lock:" + lockKey;
|
||||||
|
System.out.println("测试参数 - 锁键: " + lockKey + ", 过期时间: " + expireTimeSec + "秒");
|
||||||
|
|
||||||
|
// 当调用时
|
||||||
|
when(redisService.set(anyString(), anyString(), anyLong())).thenReturn(true);
|
||||||
|
boolean result = lockHelper.tryLock(lockKey, expireTimeSec);
|
||||||
|
System.out.println("获取锁结果: " + (result ? "成功" : "失败"));
|
||||||
|
|
||||||
|
// 那么验证
|
||||||
|
assertTrue(result, "锁应该成功获取");
|
||||||
|
verify(redisService).set(eq(expectedKey), anyString(), eq(expireTimeSec));
|
||||||
|
System.out.println("验证通过: Redis set 方法被正确调用");
|
||||||
|
System.out.println("测试完成: testTryLock_Success\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTryLock_Failure() {
|
||||||
|
System.out.println("开始执行: testTryLock_Failure");
|
||||||
|
|
||||||
|
// 给定条件
|
||||||
|
String lockKey = "test_lock";
|
||||||
|
long expireTimeSec = 30L;
|
||||||
|
System.out.println("测试参数 - 锁键: " + lockKey + ", 过期时间: " + expireTimeSec + "秒");
|
||||||
|
|
||||||
|
// 当调用时
|
||||||
|
when(redisService.set(anyString(), anyString(), anyLong())).thenThrow(new RuntimeException("Redis错误"));
|
||||||
|
System.out.println("模拟Redis异常: Redis错误");
|
||||||
|
|
||||||
|
// 那么验证
|
||||||
|
assertThrows(RuntimeException.class, () -> {
|
||||||
|
lockHelper.tryLock(lockKey, expireTimeSec);
|
||||||
|
}, "应该抛出RuntimeException");
|
||||||
|
System.out.println("验证通过: 正确捕获了RuntimeException");
|
||||||
|
System.out.println("测试完成: testTryLock_Failure\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReleaseLock_Success() {
|
||||||
|
System.out.println("开始执行: testReleaseLock_Success");
|
||||||
|
|
||||||
|
// 给定条件
|
||||||
|
String lockKey = "test_lock";
|
||||||
|
String expectedKey = "distributed_lock:" + lockKey;
|
||||||
|
String lockValue = "uuid:1";
|
||||||
|
System.out.println("测试参数 - 锁键: " + lockKey + ", 锁值: " + lockValue);
|
||||||
|
|
||||||
|
// 设置线程本地变量
|
||||||
|
ReflectionTestUtils.setField(lockHelper, "LOCK_VALUE_HOLDER",
|
||||||
|
new ThreadLocal<String>() {
|
||||||
|
@Override
|
||||||
|
protected String initialValue() {
|
||||||
|
return lockValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
when(redisService.get(expectedKey)).thenReturn(lockValue);
|
||||||
|
System.out.println("模拟Redis get返回值: " + lockValue);
|
||||||
|
|
||||||
|
// 当调用时
|
||||||
|
lockHelper.releaseLock(lockKey);
|
||||||
|
System.out.println("执行锁释放操作");
|
||||||
|
|
||||||
|
// 那么验证
|
||||||
|
verify(redisService).del(expectedKey);
|
||||||
|
System.out.println("验证通过: Redis del 方法被正确调用");
|
||||||
|
System.out.println("测试完成: testReleaseLock_Success\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReleaseLock_WrongOwner() {
|
||||||
|
System.out.println("开始执行: testReleaseLock_WrongOwner");
|
||||||
|
|
||||||
|
// 给定条件
|
||||||
|
String lockKey = "test_lock";
|
||||||
|
String expectedKey = "distributed_lock:" + lockKey;
|
||||||
|
String currentValue = "uuid:1";
|
||||||
|
String threadValue = "uuid:2"; // 不同的值
|
||||||
|
System.out.println("测试参数 - 当前锁值: " + currentValue + ", 线程锁值: " + threadValue);
|
||||||
|
|
||||||
|
// 设置线程本地变量
|
||||||
|
ReflectionTestUtils.setField(lockHelper, "LOCK_VALUE_HOLDER",
|
||||||
|
new ThreadLocal<String>() {
|
||||||
|
@Override
|
||||||
|
protected String initialValue() {
|
||||||
|
return threadValue;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
when(redisService.get(expectedKey)).thenReturn(currentValue);
|
||||||
|
System.out.println("模拟Redis中当前锁值: " + currentValue);
|
||||||
|
|
||||||
|
// 当调用时
|
||||||
|
lockHelper.releaseLock(lockKey);
|
||||||
|
System.out.println("执行锁释放操作");
|
||||||
|
|
||||||
|
// 那么验证
|
||||||
|
verify(redisService, never()).del(anyString());
|
||||||
|
System.out.println("验证通过: Redis del 方法未被调用(防止误删)");
|
||||||
|
System.out.println("测试完成: testReleaseLock_WrongOwner\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConcurrentAccess_SingleLockAcquirer() throws InterruptedException {
|
||||||
|
System.out.println("开始执行: testConcurrentAccess_SingleLockAcquirer");
|
||||||
|
|
||||||
|
// 给定条件
|
||||||
|
String lockKey = "concurrent_test_lock";
|
||||||
|
int threadCount = 10;
|
||||||
|
ExecutorService executor = Executors.newFixedThreadPool(threadCount);
|
||||||
|
CountDownLatch latch = new CountDownLatch(threadCount);
|
||||||
|
AtomicInteger successCounter = new AtomicInteger(0);
|
||||||
|
System.out.println("测试参数 - 并发线程数: " + threadCount + ", 锁键: " + lockKey);
|
||||||
|
|
||||||
|
// 模拟Redis并发访问行为
|
||||||
|
when(redisService.set(anyString(), anyString(), anyLong()))
|
||||||
|
.thenAnswer(invocation -> {
|
||||||
|
// 只有第一次调用成功,其余都失败
|
||||||
|
boolean result = successCounter.get() == 0;
|
||||||
|
if (result) {
|
||||||
|
System.out.println("线程[" + Thread.currentThread().getName() + "] 成功获取锁");
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
System.out.println("模拟Redis行为: 只允许一个线程获取锁");
|
||||||
|
|
||||||
|
// 当执行时
|
||||||
|
for (int i = 0; i < threadCount; i++) {
|
||||||
|
final int threadId = i;
|
||||||
|
executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
System.out.println("启动线程[" + threadId + "]");
|
||||||
|
if (lockHelper.tryLock(lockKey, 30)) {
|
||||||
|
int count = successCounter.incrementAndGet();
|
||||||
|
System.out.println("线程[" + threadId + "] 获取锁成功,当前成功计数: " + count);
|
||||||
|
lockHelper.releaseLock(lockKey);
|
||||||
|
System.out.println("线程[" + threadId + "] 释放锁");
|
||||||
|
} else {
|
||||||
|
System.out.println("线程[" + threadId + "] 获取锁失败");
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
latch.countDown();
|
||||||
|
System.out.println("线程[" + threadId + "] 执行完毕,剩余线程数: " + (latch.getCount()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean completed = latch.await(5, TimeUnit.SECONDS);
|
||||||
|
executor.shutdown();
|
||||||
|
System.out.println("所有线程执行完成: " + (completed ? "正常结束" : "超时结束"));
|
||||||
|
|
||||||
|
// 那么验证
|
||||||
|
int successCount = successCounter.get();
|
||||||
|
assertEquals(1, successCount, "应该只有一个线程获得锁");
|
||||||
|
System.out.println("验证通过: 实际成功获取锁的线程数为 " + successCount);
|
||||||
|
System.out.println("测试完成: testConcurrentAccess_SingleLockAcquirer\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAutoRenewalFunctionality() throws InterruptedException {
|
||||||
|
System.out.println("开始执行: testAutoRenewalFunctionality");
|
||||||
|
|
||||||
|
// 给定条件
|
||||||
|
String lockKey = "auto_renew_test_lock";
|
||||||
|
long expireTimeSec = 5L;
|
||||||
|
long maxHoldTimeSec = 10L;
|
||||||
|
System.out.println("测试参数 - 锁键: " + lockKey + ", 过期时间: " + expireTimeSec + "秒, 最大持有时间: " + maxHoldTimeSec + "秒");
|
||||||
|
|
||||||
|
when(redisService.set(anyString(), anyString(), anyLong())).thenReturn(true);
|
||||||
|
when(redisService.get(anyString())).thenReturn("mock-value");
|
||||||
|
System.out.println("模拟Redis set/get操作成功");
|
||||||
|
|
||||||
|
// 当调用时
|
||||||
|
DistributedLockHelper.LockHolder lockHolder =
|
||||||
|
lockHelper.tryLockWithAutoRenew(lockKey, expireTimeSec, maxHoldTimeSec);
|
||||||
|
System.out.println("获取带自动续期功能的锁");
|
||||||
|
|
||||||
|
assertNotNull(lockHolder, "锁持有者不应为空");
|
||||||
|
System.out.println("锁持有者创建成功");
|
||||||
|
|
||||||
|
// 等待可能的续期操作
|
||||||
|
System.out.println("等待3秒观察自动续期行为...");
|
||||||
|
Thread.sleep(3000);
|
||||||
|
|
||||||
|
// 那么验证
|
||||||
|
verify(redisService, atLeastOnce()).expire(anyString(), anyLong());
|
||||||
|
System.out.println("验证通过: Redis expire 方法至少被调用一次");
|
||||||
|
|
||||||
|
// 清理
|
||||||
|
lockHolder.close();
|
||||||
|
System.out.println("关闭锁持有者,清理资源");
|
||||||
|
System.out.println("测试完成: testAutoRenewalFunctionality\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testLockHolderAutoClose() {
|
||||||
|
System.out.println("开始执行: testLockHolderAutoClose");
|
||||||
|
|
||||||
|
// 给定条件
|
||||||
|
String lockKey = "auto_close_test_lock";
|
||||||
|
System.out.println("测试参数 - 锁键: " + lockKey);
|
||||||
|
|
||||||
|
when(redisService.set(anyString(), anyString(), anyLong())).thenReturn(true);
|
||||||
|
when(redisService.get(anyString())).thenReturn("mock-value");
|
||||||
|
System.out.println("模拟Redis操作成功");
|
||||||
|
|
||||||
|
// 当调用时
|
||||||
|
System.out.println("使用try-with-resources语法获取锁");
|
||||||
|
try (DistributedLockHelper.LockHolder holder =
|
||||||
|
lockHelper.tryLockWithAutoRenew(lockKey, 30, 300)) {
|
||||||
|
assertNotNull(holder, "锁持有者不应为空");
|
||||||
|
System.out.println("锁持有者创建成功");
|
||||||
|
}
|
||||||
|
System.out.println("try-with-resources块结束,自动调用close方法");
|
||||||
|
|
||||||
|
// 那么验证
|
||||||
|
verify(redisService).del(anyString());
|
||||||
|
System.out.println("验证通过: Redis del 方法被正确调用");
|
||||||
|
System.out.println("测试完成: testLockHolderAutoClose\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMultipleLocksIndependence() {
|
||||||
|
System.out.println("开始执行: testMultipleLocksIndependence");
|
||||||
|
|
||||||
|
// 给定条件
|
||||||
|
String lockKey1 = "lock_1";
|
||||||
|
String lockKey2 = "lock_2";
|
||||||
|
System.out.println("测试参数 - 锁键1: " + lockKey1 + ", 锁键2: " + lockKey2);
|
||||||
|
|
||||||
|
when(redisService.set(anyString(), anyString(), anyLong())).thenReturn(true);
|
||||||
|
System.out.println("模拟Redis set操作成功");
|
||||||
|
|
||||||
|
// 当调用时
|
||||||
|
boolean lock1Acquired = lockHelper.tryLock(lockKey1, 30);
|
||||||
|
System.out.println("获取第一个锁: " + (lock1Acquired ? "成功" : "失败"));
|
||||||
|
|
||||||
|
boolean lock2Acquired = lockHelper.tryLock(lockKey2, 30);
|
||||||
|
System.out.println("获取第二个锁: " + (lock2Acquired ? "成功" : "失败"));
|
||||||
|
|
||||||
|
// 那么验证
|
||||||
|
assertTrue(lock1Acquired, "第一个锁应该成功获取");
|
||||||
|
assertTrue(lock2Acquired, "第二个锁应该成功获取");
|
||||||
|
System.out.println("两个锁都成功获取");
|
||||||
|
|
||||||
|
// 验证它们是不同的键
|
||||||
|
verify(redisService).set(eq("distributed_lock:" + lockKey1), anyString(), eq(30L));
|
||||||
|
verify(redisService).set(eq("distributed_lock:" + lockKey2), anyString(), eq(30L));
|
||||||
|
System.out.println("验证通过: 两个独立的锁键都被正确设置");
|
||||||
|
System.out.println("测试完成: testMultipleLocksIndependence\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testThreadIsolation() throws InterruptedException {
|
||||||
|
System.out.println("开始执行: testThreadIsolation");
|
||||||
|
|
||||||
|
// 给定条件
|
||||||
|
String lockKey = "thread_isolation_lock";
|
||||||
|
ExecutorService executor = Executors.newFixedThreadPool(2);
|
||||||
|
CountDownLatch latch = new CountDownLatch(2);
|
||||||
|
AtomicInteger[] results = new AtomicInteger[]{new AtomicInteger(0), new AtomicInteger(0)};
|
||||||
|
System.out.println("测试参数 - 锁键: " + lockKey + ", 线程数: 2");
|
||||||
|
|
||||||
|
when(redisService.set(anyString(), anyString(), anyLong())).thenReturn(true);
|
||||||
|
System.out.println("模拟Redis set操作成功");
|
||||||
|
|
||||||
|
// 当执行时
|
||||||
|
System.out.println("启动两个线程竞争同一个锁");
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
final int threadIndex = i;
|
||||||
|
executor.submit(() -> {
|
||||||
|
try {
|
||||||
|
System.out.println("线程[" + threadIndex + "] 尝试获取锁");
|
||||||
|
if (lockHelper.tryLock(lockKey, 30)) {
|
||||||
|
int count = results[threadIndex].incrementAndGet();
|
||||||
|
System.out.println("线程[" + threadIndex + "] 获取锁成功,计数: " + count);
|
||||||
|
// 持有锁一段时间
|
||||||
|
Thread.sleep(100);
|
||||||
|
System.out.println("线程[" + threadIndex + "] 持有锁100ms后释放");
|
||||||
|
lockHelper.releaseLock(lockKey);
|
||||||
|
System.out.println("线程[" + threadIndex + "] 释放锁");
|
||||||
|
} else {
|
||||||
|
System.out.println("线程[" + threadIndex + "] 获取锁失败");
|
||||||
|
}
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
System.out.println("线程[" + threadIndex + "] 被中断");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
} finally {
|
||||||
|
latch.countDown();
|
||||||
|
System.out.println("线程[" + threadIndex + "] 执行完毕");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean completed = latch.await(5, TimeUnit.SECONDS);
|
||||||
|
executor.shutdown();
|
||||||
|
System.out.println("所有线程执行完成: " + (completed ? "正常结束" : "超时结束"));
|
||||||
|
|
||||||
|
// 那么验证 - 两个线程都应该能够在不同时间获得锁
|
||||||
|
int result0 = results[0].get();
|
||||||
|
int result1 = results[1].get();
|
||||||
|
assertEquals(1, result0, "线程0应该获得一次锁");
|
||||||
|
assertEquals(1, result1, "线程1应该获得一次锁");
|
||||||
|
System.out.println("验证通过: 线程0获得锁次数=" + result0 + ", 线程1获得锁次数=" + result1);
|
||||||
|
System.out.println("测试完成: testThreadIsolation\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -57,6 +57,8 @@ secure:
|
|||||||
- "/swagger-resources/**"
|
- "/swagger-resources/**"
|
||||||
- "/swagger/**"
|
- "/swagger/**"
|
||||||
- "/static/**"
|
- "/static/**"
|
||||||
|
- "/shop/static/**"
|
||||||
|
- "/static/image/**"
|
||||||
- "/**/v2/api-docs"
|
- "/**/v2/api-docs"
|
||||||
- "/**/*.js"
|
- "/**/*.js"
|
||||||
- "/**/*.css"
|
- "/**/*.css"
|
||||||
@ -67,8 +69,6 @@ secure:
|
|||||||
- "/mall-auth/rsa/publicKey"
|
- "/mall-auth/rsa/publicKey"
|
||||||
- "/admin/account/account-user-base/register"
|
- "/admin/account/account-user-base/register"
|
||||||
- "/admin/account/account-user-base/login"
|
- "/admin/account/account-user-base/login"
|
||||||
- "/static/image/**"
|
|
||||||
- "/shop/static/**"
|
|
||||||
- "/admin/account/open/**"
|
- "/admin/account/open/**"
|
||||||
- "/admin/account/account-user-base/doLogin"
|
- "/admin/account/account-user-base/doLogin"
|
||||||
- "/mobile/pay/index/notify_url"
|
- "/mobile/pay/index/notify_url"
|
||||||
@ -93,6 +93,8 @@ secure:
|
|||||||
- "/mobile/shop/lakala/sign/ec/**"
|
- "/mobile/shop/lakala/sign/ec/**"
|
||||||
- "/mobile/**/**/test/case"
|
- "/mobile/**/**/test/case"
|
||||||
- "/**/**/testcase"
|
- "/**/**/testcase"
|
||||||
|
- "/mobile/sns/Story/listComment"
|
||||||
|
- "/mobile/sns/Story/getStory"
|
||||||
universal:
|
universal:
|
||||||
urls:
|
urls:
|
||||||
- "/admin/account/account-user-base/info"
|
- "/admin/account/account-user-base/info"
|
||||||
|
|||||||
@ -26,7 +26,7 @@ logging:
|
|||||||
level:
|
level:
|
||||||
com:
|
com:
|
||||||
sun:
|
sun:
|
||||||
mail: error
|
mail: info
|
||||||
baomidou: error
|
baomidou: error
|
||||||
alibaba:
|
alibaba:
|
||||||
nacos:
|
nacos:
|
||||||
|
|||||||
@ -100,6 +100,12 @@
|
|||||||
<artifactId>jackson-module-jaxb-annotations</artifactId>
|
<artifactId>jackson-module-jaxb-annotations</artifactId>
|
||||||
<version>2.8.6</version>
|
<version>2.8.6</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- rabbitMQ消息队列 -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-starter-amqp</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<profiles>
|
<profiles>
|
||||||
|
|||||||
@ -0,0 +1,84 @@
|
|||||||
|
package com.suisung.mall.im.common.websocket.config;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.amqp.core.*;
|
||||||
|
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
|
||||||
|
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
|
||||||
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
|
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
|
||||||
|
import org.springframework.context.annotation.Bean;
|
||||||
|
import org.springframework.context.annotation.Configuration;
|
||||||
|
|
||||||
|
@Configuration
|
||||||
|
public class RabbitMQConfig {
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(RabbitMQConfig.class);
|
||||||
|
// 直连交换机 - 用于精确路由
|
||||||
|
public static final String IM_DIRECT_EXCHANGE = "im.direct.exchange";
|
||||||
|
|
||||||
|
// 广播交换机 - 用于广播消息
|
||||||
|
public static final String IM_FANOUT_EXCHANGE = "im.fanout.exchange";
|
||||||
|
|
||||||
|
// 队列前缀
|
||||||
|
public static final String IM_QUEUE_PREFIX = "im.queue.";
|
||||||
|
|
||||||
|
// 路由键前缀
|
||||||
|
public static final String IM_ROUTING_KEY_PREFIX = "im.server.";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建直连交换机 - 用于点对点消息
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public DirectExchange imDirectExchange() {
|
||||||
|
return new DirectExchange(IM_DIRECT_EXCHANGE, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建广播交换机 - 用于广播消息
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public FanoutExchange imFanoutExchange() {
|
||||||
|
return new FanoutExchange(IM_FANOUT_EXCHANGE, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JSON消息转换器 - 支持SendVO等复杂对象
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public Jackson2JsonMessageConverter jsonMessageConverter() {
|
||||||
|
return new Jackson2JsonMessageConverter();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置RabbitTemplate
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
|
||||||
|
RabbitTemplate template = new RabbitTemplate(connectionFactory);
|
||||||
|
template.setMessageConverter(jsonMessageConverter());
|
||||||
|
|
||||||
|
// 设置确认回调
|
||||||
|
template.setConfirmCallback((correlationData, ack, cause) -> {
|
||||||
|
if (!ack) {
|
||||||
|
logger.info("消息发送失败: {}", cause);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return template;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 配置监听器容器工厂
|
||||||
|
*/
|
||||||
|
@Bean
|
||||||
|
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
|
||||||
|
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
|
||||||
|
factory.setConnectionFactory(connectionFactory);
|
||||||
|
factory.setMessageConverter(jsonMessageConverter());
|
||||||
|
factory.setConcurrentConsumers(3);
|
||||||
|
factory.setMaxConcurrentConsumers(10);
|
||||||
|
factory.setPrefetchCount(1); // 每次处理1条消息
|
||||||
|
return factory;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,134 @@
|
|||||||
|
package com.suisung.mall.im.common.websocket.service;
|
||||||
|
|
||||||
|
import com.suisung.mall.im.pojo.vo.SendVO;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.amqp.rabbit.core.RabbitTemplate;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.web.socket.WebSocketSession;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import static com.suisung.mall.im.common.websocket.config.RabbitMQConfig.IM_FANOUT_EXCHANGE;
|
||||||
|
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class DistributedMessageService {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(DistributedMessageService.class);
|
||||||
|
@Autowired
|
||||||
|
private RabbitTemplate rabbitTemplate;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DistributedSessionService sessionService;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private LocalSessionManager localSessionManager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RabbitMQManager rabbitMQManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息给用户
|
||||||
|
* @param targetUserId 目标用户就是account的id
|
||||||
|
* @param message
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean sendToUser(String targetUserId, SendVO message) {
|
||||||
|
log.info("targetUserId{},message:{}", targetUserId, message);
|
||||||
|
String targetServer = sessionService.getUserServer(targetUserId);
|
||||||
|
|
||||||
|
if (targetServer == null) {
|
||||||
|
// 用户离线,可以存储离线消息
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCurrentServer(targetServer)) {
|
||||||
|
// 用户在当前服务器,直接发送
|
||||||
|
return localSessionManager.sendToUser(targetUserId, message);
|
||||||
|
} else {
|
||||||
|
// 用户在其他服务器,通过RabbitMQ转发
|
||||||
|
forwardToUser(targetServer, targetUserId, message);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息给群组
|
||||||
|
*/
|
||||||
|
public void sendToGroup(String groupId, SendVO message, String excludeSessionId) {
|
||||||
|
// 获取群组所有成员
|
||||||
|
List<Map<String, Object>> members = sessionService.getGroupMembers(groupId);
|
||||||
|
|
||||||
|
for (Map<String, Object> member : members) {
|
||||||
|
String userId = (String) member.get("userId");
|
||||||
|
String sessionId = (String) member.get("sessionId");
|
||||||
|
String memberServer = (String) member.get("serverId");
|
||||||
|
|
||||||
|
// 排除发送者自己
|
||||||
|
if (sessionId.equals(excludeSessionId)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isCurrentServer(memberServer)) {
|
||||||
|
// 成员在当前服务器
|
||||||
|
localSessionManager.sendToSession(sessionId, message);
|
||||||
|
} else {
|
||||||
|
// 成员在其他服务器
|
||||||
|
forwardToSession(memberServer, sessionId, message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 广播消息给所有在线用户
|
||||||
|
*/
|
||||||
|
public void broadcast(SendVO message, String excludeServerId) {
|
||||||
|
// 创建广播消息
|
||||||
|
Map<String, Object> broadcastMsg = new HashMap<>();
|
||||||
|
broadcastMsg.put("type", "BROADCAST");
|
||||||
|
broadcastMsg.put("message", message);
|
||||||
|
broadcastMsg.put("excludeServer", excludeServerId);
|
||||||
|
broadcastMsg.put("sourceServer", rabbitMQManager.getServerId());
|
||||||
|
|
||||||
|
// 广播到所有服务器(这里简化处理,实际应该获取所有服务器列表)
|
||||||
|
// 可以通过Redis维护服务器列表,然后遍历发送
|
||||||
|
rabbitTemplate.convertAndSend(IM_FANOUT_EXCHANGE, "im.server.all", broadcastMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转发消息到用户
|
||||||
|
*/
|
||||||
|
private void forwardToUser(String targetServer, String userId, SendVO message) {
|
||||||
|
Map<String, Object> forwardMsg = new HashMap<>();
|
||||||
|
forwardMsg.put("type", "SEND_TO_USER");
|
||||||
|
forwardMsg.put("targetUserId", userId);
|
||||||
|
forwardMsg.put("message", message);
|
||||||
|
forwardMsg.put("sourceServer", rabbitMQManager.getServerId());
|
||||||
|
|
||||||
|
String routingKey = rabbitMQManager.getTargetRoutingKey(targetServer);
|
||||||
|
rabbitTemplate.convertAndSend(IM_FANOUT_EXCHANGE, routingKey, forwardMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转发消息到会话
|
||||||
|
*/
|
||||||
|
private void forwardToSession(String targetServer, String sessionId, SendVO message) {
|
||||||
|
Map<String, Object> forwardMsg = new HashMap<>();
|
||||||
|
forwardMsg.put("type", "SEND_TO_SESSION");
|
||||||
|
forwardMsg.put("targetSessionId", sessionId);
|
||||||
|
forwardMsg.put("message", message);
|
||||||
|
forwardMsg.put("sourceServer", rabbitMQManager.getServerId());
|
||||||
|
|
||||||
|
String routingKey = rabbitMQManager.getTargetRoutingKey(targetServer);
|
||||||
|
rabbitTemplate.convertAndSend(IM_FANOUT_EXCHANGE, routingKey, forwardMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isCurrentServer(String serverId) {
|
||||||
|
return serverId.equals(rabbitMQManager.getServerId());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,240 @@
|
|||||||
|
package com.suisung.mall.im.common.websocket.service;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
|
import org.springframework.data.redis.core.RedisTemplate;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class DistributedSessionService {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(DistributedSessionService.class);
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Autowired
|
||||||
|
private RedisTemplate<String, Object> redisTemplate;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RabbitMQManager rabbitMQManager; // 使用RabbitMQManager获取serverId
|
||||||
|
|
||||||
|
private static final String USER_SERVER_KEY = "im:user:server:";
|
||||||
|
private static final String USER_SESSIONS_KEY = "im:user:sessions:";
|
||||||
|
private static final String SERVER_USERS_KEY = "im:server:users:kf";
|
||||||
|
private static final String GROUP_SESSIONS_KEY = "im:group:sessions:";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注册用户会话
|
||||||
|
*/
|
||||||
|
public void registerUserSession(String userId, String sessionId, Map<String, Object> attributes) {
|
||||||
|
String userKey = USER_SESSIONS_KEY + userId;
|
||||||
|
String serverKey = USER_SERVER_KEY + userId;
|
||||||
|
String serverUsersKey = SERVER_USERS_KEY;
|
||||||
|
|
||||||
|
// 存储用户会话信息
|
||||||
|
Map<String, Object> sessionInfo = new HashMap<>();
|
||||||
|
sessionInfo.put("sessionId", sessionId);
|
||||||
|
sessionInfo.put("serverId", getServerId());
|
||||||
|
sessionInfo.put("connectTime", System.currentTimeMillis());
|
||||||
|
sessionInfo.put("lastHeartbeat", System.currentTimeMillis());
|
||||||
|
sessionInfo.put("attributes", attributes);
|
||||||
|
|
||||||
|
redisTemplate.opsForHash().put(userKey, sessionId, sessionInfo);
|
||||||
|
redisTemplate.expire(userKey, Duration.ofMinutes(30));
|
||||||
|
|
||||||
|
// 更新用户-服务器映射
|
||||||
|
redisTemplate.opsForValue().set(serverKey, getServerId(), Duration.ofMinutes(30));
|
||||||
|
|
||||||
|
// 添加到服务器用户集合
|
||||||
|
redisTemplate.opsForSet().add(serverUsersKey, userId);
|
||||||
|
redisTemplate.expire(serverUsersKey, Duration.ofMinutes(30));
|
||||||
|
|
||||||
|
log.info("用户会话注册: userId={}, sessionId={}, serverId={}", userId, sessionId, getServerId());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 注销用户会话
|
||||||
|
*/
|
||||||
|
public void unregisterUserSession(String userId, String sessionId) {
|
||||||
|
String userKey = USER_SESSIONS_KEY + userId;
|
||||||
|
String serverUsersKey = SERVER_USERS_KEY;
|
||||||
|
|
||||||
|
// 从用户会话中移除
|
||||||
|
redisTemplate.opsForHash().delete(userKey, sessionId);
|
||||||
|
|
||||||
|
// 从服务器用户集合中移除
|
||||||
|
redisTemplate.opsForSet().remove(serverUsersKey, userId);
|
||||||
|
|
||||||
|
// 检查用户是否还有活跃会话
|
||||||
|
Long size = redisTemplate.opsForHash().size(userKey);
|
||||||
|
if (size == null || size == 0) {
|
||||||
|
String serverKey = USER_SERVER_KEY + userId;
|
||||||
|
redisTemplate.delete(serverKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户所在服务器
|
||||||
|
*/
|
||||||
|
public String getUserServer(String userId) {
|
||||||
|
String key = USER_SERVER_KEY + userId;
|
||||||
|
return (String) redisTemplate.opsForValue().get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取用户所有会话ID
|
||||||
|
*/
|
||||||
|
public Set<String> getUserSessions(String userId) {
|
||||||
|
String key = USER_SESSIONS_KEY + userId;
|
||||||
|
return redisTemplate.opsForHash().keys(key).stream()
|
||||||
|
.map(Object::toString)
|
||||||
|
.collect(HashSet::new, HashSet::add, HashSet::addAll);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加用户到群组
|
||||||
|
*/
|
||||||
|
public void addUserToGroup(String groupId, String userId, String sessionId) {
|
||||||
|
String groupKey = GROUP_SESSIONS_KEY + groupId;
|
||||||
|
Map<String, Object> memberInfo = new HashMap<>();
|
||||||
|
memberInfo.put("userId", userId);
|
||||||
|
memberInfo.put("sessionId", sessionId);
|
||||||
|
memberInfo.put("serverId", getServerId());
|
||||||
|
memberInfo.put("joinTime", System.currentTimeMillis());
|
||||||
|
|
||||||
|
redisTemplate.opsForHash().put(groupKey, sessionId, memberInfo);
|
||||||
|
redisTemplate.expire(groupKey, Duration.ofHours(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从群组移除用户
|
||||||
|
*/
|
||||||
|
public void removeUserFromGroup(String groupId, String sessionId) {
|
||||||
|
String groupKey = GROUP_SESSIONS_KEY + groupId;
|
||||||
|
redisTemplate.opsForHash().delete(groupKey, sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取群组所有成员
|
||||||
|
*/
|
||||||
|
public List<Map<String, Object>> getGroupMembers(String groupId) {
|
||||||
|
String groupKey = GROUP_SESSIONS_KEY + groupId;
|
||||||
|
return redisTemplate.opsForHash().values(groupKey).stream()
|
||||||
|
.map(obj -> (Map<String, Object>) obj)
|
||||||
|
.collect(ArrayList::new, ArrayList::add, ArrayList::addAll);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新心跳
|
||||||
|
*/
|
||||||
|
public void updateHeartbeat(String userId, String sessionId) {
|
||||||
|
String userKey = USER_SESSIONS_KEY + userId;
|
||||||
|
Map<String, Object> sessionInfo = (Map<String, Object>) redisTemplate.opsForHash().get(userKey, sessionId);
|
||||||
|
if (sessionInfo != null) {
|
||||||
|
sessionInfo.put("lastHeartbeat", System.currentTimeMillis());
|
||||||
|
redisTemplate.opsForHash().put(userKey, sessionId, sessionInfo);
|
||||||
|
redisTemplate.expire(userKey, Duration.ofMinutes(30));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有在线用户ID列表
|
||||||
|
*/
|
||||||
|
public List<Integer> getOnlineUserIds() {
|
||||||
|
// 方法1: 通过服务器用户集合获取(推荐)
|
||||||
|
String serverUsersKey = SERVER_USERS_KEY;
|
||||||
|
Set<Object> userIds = redisTemplate.opsForSet().members(serverUsersKey);
|
||||||
|
|
||||||
|
if (userIds != null && !userIds.isEmpty()) {
|
||||||
|
List<Integer> userIdList = new ArrayList<>(userIds.size());
|
||||||
|
userIdList= userIds.stream()
|
||||||
|
.map(userIdStr -> {
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(userIdStr.toString());
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
return userIdList;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有服务器的在线用户ID列表(全局)
|
||||||
|
*/
|
||||||
|
public List<Integer> getAllOnlineUserIds() {
|
||||||
|
// 获取所有服务器的用户集合
|
||||||
|
Set<String> serverKeys = redisTemplate.keys(SERVER_USERS_KEY + "*");
|
||||||
|
List<Integer> allUserIds = new ArrayList<>();
|
||||||
|
log.info("sserverKeys-{}", serverKeys);
|
||||||
|
if (serverKeys != null) {
|
||||||
|
for (String serverKey : serverKeys) {
|
||||||
|
Set<Object> userIds = redisTemplate.opsForSet().members(serverKey);
|
||||||
|
log.info("userIds{}", userIds);
|
||||||
|
if (userIds != null) {
|
||||||
|
userIds.forEach(userIdStr -> {
|
||||||
|
try {
|
||||||
|
allUserIds.add(Integer.parseInt(userIdStr.toString()));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
log.info("报错:{}",e.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去重
|
||||||
|
return allUserIds.stream().distinct().collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取在线用户登录名列表
|
||||||
|
*/
|
||||||
|
public List<String> getOnlineLoginNames() {
|
||||||
|
List<String> loginNames = new ArrayList<>();
|
||||||
|
List<Integer> userIds = getOnlineUserIds();
|
||||||
|
|
||||||
|
// 这里可以根据用户ID获取对应的登录名
|
||||||
|
// 实际实现可能需要调用用户服务
|
||||||
|
for (Integer userId : userIds) {
|
||||||
|
// 模拟获取登录名,实际应该从用户服务或缓存中获取
|
||||||
|
String loginName = "user_" + userId;
|
||||||
|
loginNames.add(loginName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return loginNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取指定用户的会话信息
|
||||||
|
*/
|
||||||
|
public List<Map<String, Object>> getUserSessionsInfo(String userId) {
|
||||||
|
String userKey = USER_SESSIONS_KEY + userId;
|
||||||
|
Map<Object, Object> sessions = redisTemplate.opsForHash().entries(userKey);
|
||||||
|
|
||||||
|
return sessions.values().stream()
|
||||||
|
.map(obj -> (Map<String, Object>) obj)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查用户是否在线
|
||||||
|
*/
|
||||||
|
public boolean isUserOnline(String userId) {
|
||||||
|
String userKey = USER_SESSIONS_KEY + userId;
|
||||||
|
Long size = redisTemplate.opsForHash().size(userKey);
|
||||||
|
return size != null && size > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getServerId() {
|
||||||
|
return rabbitMQManager.getServerId();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,157 @@
|
|||||||
|
package com.suisung.mall.im.common.websocket.service;
|
||||||
|
import com.suisung.mall.im.common.websocket.service.onchat.MallsuiteImSocketHandler;
|
||||||
|
import com.suisung.mall.im.pojo.vo.SendVO;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.web.socket.TextMessage;
|
||||||
|
import org.springframework.web.socket.WebSocketSession;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
public class LocalSessionManager {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(LocalSessionManager.class);
|
||||||
|
// 本地存储的用户会话 (userId -> sessions)
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
private ConcurrentHashMap<String, List<WebSocketSession>> userSessions;
|
||||||
|
|
||||||
|
// 本地存储的群组会话 (groupId -> sessions)
|
||||||
|
private ConcurrentHashMap<String, List<WebSocketSession>> groupSessions;
|
||||||
|
|
||||||
|
public LocalSessionManager() {
|
||||||
|
this.userSessions = MallsuiteImSocketHandler.userSessions;
|
||||||
|
this.groupSessions = MallsuiteImSocketHandler.groupSessions;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除用户会话
|
||||||
|
*/
|
||||||
|
public void removeUserSession(String userId, WebSocketSession session) {
|
||||||
|
if (userId == null || session == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
userSessions.computeIfPresent(userId, (key, sessions) -> {
|
||||||
|
sessions.removeIf(webSocketSession ->
|
||||||
|
webSocketSession.getId().equals(session.getId()));
|
||||||
|
return sessions.isEmpty() ? null : sessions;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息给用户
|
||||||
|
* @param userId account的id
|
||||||
|
* @param message
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean sendToUser(String userId, SendVO message) {
|
||||||
|
if (userId == null || message == null) {
|
||||||
|
log.info("发送消息失败: 参数为空");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
log.info("userSessions={}", userSessions);
|
||||||
|
List<WebSocketSession> sessions = userSessions.get(userId);
|
||||||
|
if (sessions == null || sessions.isEmpty()) {
|
||||||
|
log.info("用户没有活跃会话: {}", userId);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean success = false;
|
||||||
|
for (WebSocketSession session : sessions) {
|
||||||
|
if (session != null && session.isOpen()) {
|
||||||
|
try {
|
||||||
|
session.sendMessage(new TextMessage(message.toString()));
|
||||||
|
success = true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
log.info("发送消息失败: {}", e.getMessage());
|
||||||
|
// 发送失败,移除会话
|
||||||
|
removeUserSession(userId, session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 发送消息给会话
|
||||||
|
*/
|
||||||
|
public boolean sendToSession(String sessionId, SendVO message) {
|
||||||
|
if (sessionId == null || message == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 遍历所有会话找到指定的sessionId
|
||||||
|
for (List<WebSocketSession> sessions : userSessions.values()) {
|
||||||
|
if (sessions == null) continue;
|
||||||
|
|
||||||
|
for (WebSocketSession session : sessions) {
|
||||||
|
if (session != null && session.getId().equals(sessionId) && session.isOpen()) {
|
||||||
|
try {
|
||||||
|
session.sendMessage(new TextMessage(message.toString()));
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除群组会话
|
||||||
|
*/
|
||||||
|
public void removeGroupSession(String groupId, WebSocketSession session) {
|
||||||
|
groupSessions.computeIfPresent(groupId, (key, gropSession) -> {
|
||||||
|
gropSession.removeIf(webSocketSession ->
|
||||||
|
webSocketSession.getId().equals(session.getId()));
|
||||||
|
return gropSession.isEmpty() ? null : gropSession;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本地所有在线用户ID
|
||||||
|
*/
|
||||||
|
public List<Integer> getLocalOnlineUserIds() {
|
||||||
|
List<Integer> userIds = new ArrayList<>();
|
||||||
|
|
||||||
|
for (String userIdStr : userSessions.keySet()) {
|
||||||
|
try {
|
||||||
|
userIds.add(Integer.parseInt(userIdStr));
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// 忽略格式错误的用户ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return userIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本地所有在线用户登录名
|
||||||
|
*/
|
||||||
|
public List<String> getLocalOnlineLoginNames() {
|
||||||
|
return new ArrayList<>(userSessions.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本地会话数量统计
|
||||||
|
*/
|
||||||
|
public Map<String, Object> getLocalSessionStats() {
|
||||||
|
Map<String, Object> stats = new HashMap<>();
|
||||||
|
stats.put("totalUsers", userSessions.size());
|
||||||
|
stats.put("totalSessions", userSessions.values().stream().mapToInt(List::size).sum());
|
||||||
|
stats.put("totalGroups", groupSessions.size());
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,232 @@
|
|||||||
|
package com.suisung.mall.im.common.websocket.service;
|
||||||
|
|
||||||
|
import com.suisung.mall.im.pojo.vo.SendVO;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.amqp.rabbit.annotation.RabbitListener;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class MessageConsumerService {
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(MessageConsumerService.class);
|
||||||
|
@Autowired
|
||||||
|
private LocalSessionManager localSessionManager;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private RabbitMQManager rabbitMQManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听当前服务器的队列
|
||||||
|
* 使用 RabbitListener 监听动态队列
|
||||||
|
*/
|
||||||
|
@RabbitListener(queues = "#{rabbitMQManager.queueName}")
|
||||||
|
public void consumeForwardMessage(Map<String, Object> message) {
|
||||||
|
try {
|
||||||
|
String type = (String) message.get("type");
|
||||||
|
logger.info("收到RabbitMQ消息, 类型: {}, 服务器: {}", type, rabbitMQManager.getServerId());
|
||||||
|
switch (type) {
|
||||||
|
case "SEND_TO_USER":
|
||||||
|
handleSendToUser(message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "SEND_TO_SESSION":
|
||||||
|
handleSendToSession(message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "BROADCAST":
|
||||||
|
handleBroadcast(message);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
logger.info("未知消息类型: {}", type);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.info("处理RabbitMQ消息失败: {}", e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理发送给用户的消息
|
||||||
|
*/
|
||||||
|
private void handleSendToUser(Map<String, Object> message) {
|
||||||
|
String userId = (String) message.get("targetUserId");
|
||||||
|
Object messageObj = message.get("message");
|
||||||
|
|
||||||
|
SendVO userMsg = null;
|
||||||
|
if (messageObj instanceof SendVO) {
|
||||||
|
userMsg = (SendVO) messageObj;
|
||||||
|
} else if (messageObj instanceof Map) {
|
||||||
|
userMsg = convertMapToSendVO((Map<String, Object>) messageObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (userMsg != null) {
|
||||||
|
boolean success = localSessionManager.sendToUser(userId, userMsg);
|
||||||
|
logger.info("发送消息给用户 {}: {}", userId, success ? "成功" : "失败");
|
||||||
|
} else {
|
||||||
|
logger.info("消息格式错误,无法发送给用户: {}", userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理发送给会话的消息
|
||||||
|
*/
|
||||||
|
private void handleSendToSession(Map<String, Object> message) {
|
||||||
|
String sessionId = (String) message.get("targetSessionId");
|
||||||
|
Object messageObj = message.get("message");
|
||||||
|
|
||||||
|
SendVO sessionMsg = null;
|
||||||
|
if (messageObj instanceof SendVO) {
|
||||||
|
sessionMsg = (SendVO) messageObj;
|
||||||
|
} else if (messageObj instanceof Map) {
|
||||||
|
sessionMsg = convertMapToSendVO((Map<String, Object>) messageObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sessionMsg != null) {
|
||||||
|
boolean success = localSessionManager.sendToSession(sessionId, sessionMsg);
|
||||||
|
logger.info("发送消息给会话 {}: {}", sessionId, success ? "成功" : "失败");
|
||||||
|
} else {
|
||||||
|
logger.info("消息格式错误,无法发送给会话: {}", sessionId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理广播消息
|
||||||
|
*/
|
||||||
|
private void handleBroadcast(Map<String, Object> message) {
|
||||||
|
String excludeServer = (String) message.get("excludeServer");
|
||||||
|
String currentServer = rabbitMQManager.getServerId();
|
||||||
|
|
||||||
|
// 如果当前服务器不在排除列表中,则处理广播
|
||||||
|
if (!currentServer.equals(excludeServer)) {
|
||||||
|
Object messageObj = message.get("message");
|
||||||
|
SendVO broadcastMsg = null;
|
||||||
|
|
||||||
|
if (messageObj instanceof SendVO) {
|
||||||
|
broadcastMsg = (SendVO) messageObj;
|
||||||
|
} else if (messageObj instanceof Map) {
|
||||||
|
broadcastMsg = convertMapToSendVO((Map<String, Object>) messageObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (broadcastMsg != null) {
|
||||||
|
// 实现广播给所有本地用户
|
||||||
|
broadcastToAllLocalUsers(broadcastMsg);
|
||||||
|
logger.info("广播消息给所有本地用户");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.info("当前服务器被排除在广播之外: {}", currentServer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 广播给所有本地用户
|
||||||
|
*/
|
||||||
|
private void broadcastToAllLocalUsers(SendVO message) {
|
||||||
|
// 这里可以实现具体的广播逻辑
|
||||||
|
// 例如遍历所有本地会话并发送消息
|
||||||
|
logger.info("执行广播逻辑,消息内容: {}", message.getContent());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将Map转换为SendVO
|
||||||
|
*/
|
||||||
|
private SendVO convertMapToSendVO(Map<String, Object> messageMap) {
|
||||||
|
try {
|
||||||
|
SendVO sendVO = new SendVO();
|
||||||
|
|
||||||
|
// 设置基本字段
|
||||||
|
if (messageMap.get("toid") != null) {
|
||||||
|
sendVO.setToid(String.valueOf(messageMap.get("toid")));
|
||||||
|
}
|
||||||
|
if (messageMap.get("id") != null) {
|
||||||
|
sendVO.setId(String.valueOf(messageMap.get("id")));
|
||||||
|
}
|
||||||
|
if (messageMap.get("content") != null) {
|
||||||
|
sendVO.setContent(String.valueOf(messageMap.get("content")));
|
||||||
|
}
|
||||||
|
if (messageMap.get("type") != null) {
|
||||||
|
sendVO.setType(String.valueOf(messageMap.get("type")));
|
||||||
|
}
|
||||||
|
if (messageMap.get("username") != null) {
|
||||||
|
sendVO.setUsername(String.valueOf(messageMap.get("username")));
|
||||||
|
}
|
||||||
|
if (messageMap.get("avatar") != null) {
|
||||||
|
sendVO.setAvatar(String.valueOf(messageMap.get("avatar")));
|
||||||
|
}
|
||||||
|
if (messageMap.get("fromid") != null) {
|
||||||
|
sendVO.setFromid(String.valueOf(messageMap.get("fromid")));
|
||||||
|
}
|
||||||
|
if (messageMap.get("time") != null) {
|
||||||
|
sendVO.setTime((Long) messageMap.get("time"));
|
||||||
|
}
|
||||||
|
if (messageMap.get("status") != null) {
|
||||||
|
sendVO.setStatus((Integer) messageMap.get("status"));
|
||||||
|
}
|
||||||
|
if (messageMap.get("msg_type") != null) {
|
||||||
|
sendVO.setMsg_type(String.valueOf(messageMap.get("msg_type")));
|
||||||
|
}
|
||||||
|
if (messageMap.get("item_id") != null) {
|
||||||
|
sendVO.setItem_id(Long.valueOf((String) messageMap.get("item_id")));
|
||||||
|
}
|
||||||
|
if (messageMap.get("message_length") != null) {
|
||||||
|
sendVO.setMessage_length(String.valueOf(messageMap.get("message_length")));
|
||||||
|
}
|
||||||
|
if (messageMap.get("zid") != null) {
|
||||||
|
sendVO.setZid(String.valueOf(messageMap.get("zid")));
|
||||||
|
}
|
||||||
|
if (messageMap.get("user_id") != null) {
|
||||||
|
sendVO.setUser_id((Integer) messageMap.get("user_id"));
|
||||||
|
}
|
||||||
|
if (messageMap.get("friend_id") != null) {
|
||||||
|
sendVO.setFriend_id((Integer) messageMap.get("friend_id"));
|
||||||
|
}
|
||||||
|
if (messageMap.get("sendmethod") != null) {
|
||||||
|
sendVO.setSendmethod(String.valueOf( messageMap.get("sendmethod")));
|
||||||
|
}
|
||||||
|
if (messageMap.get("message_id") != null) {
|
||||||
|
sendVO.setMessage_id(Integer.valueOf((String) messageMap.get("message_id")));
|
||||||
|
}
|
||||||
|
if (messageMap.get("mine") != null) {
|
||||||
|
sendVO.setMine((Boolean) messageMap.get("mine"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return sendVO;
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.info("Map转换为SendVO失败: {}", e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理连接建立事件(可选,用于服务器间同步)
|
||||||
|
*/
|
||||||
|
@RabbitListener(queues = "#{rabbitMQManager.queueName}")
|
||||||
|
public void handleSessionEvent(Map<String, Object> event) {
|
||||||
|
String eventType = (String) event.get("eventType");
|
||||||
|
String userId = (String) event.get("userId");
|
||||||
|
String sessionId = (String) event.get("sessionId");
|
||||||
|
|
||||||
|
logger.info("收到会话事件: {}, 用户: {}, 会话: {}", eventType, userId, sessionId);
|
||||||
|
|
||||||
|
// 可以根据需要处理各种会话事件
|
||||||
|
switch (eventType) {
|
||||||
|
case "SESSION_CREATED":
|
||||||
|
// 处理会话创建事件
|
||||||
|
break;
|
||||||
|
case "SESSION_CLOSED":
|
||||||
|
// 处理会话关闭事件
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 监听广播队列
|
||||||
|
*/
|
||||||
|
@RabbitListener(queues = "im.broadcast.queue.#{rabbitMQManager.serverId}")
|
||||||
|
public void consumeBroadcastMessage(Map<String, Object> message) {
|
||||||
|
handleBroadcast(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,169 @@
|
|||||||
|
package com.suisung.mall.im.common.websocket.service;
|
||||||
|
|
||||||
|
import com.suisung.mall.im.common.websocket.utils.ServerIdGenerator;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.amqp.core.*;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
import org.springframework.beans.factory.annotation.Value;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import javax.annotation.PostConstruct;
|
||||||
|
|
||||||
|
import static com.suisung.mall.im.common.websocket.config.RabbitMQConfig.*;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class RabbitMQManager {
|
||||||
|
private static Logger logger = LoggerFactory.getLogger(RabbitMQManager.class);
|
||||||
|
@Autowired
|
||||||
|
private AmqpAdmin amqpAdmin;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DirectExchange imDirectExchange;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private FanoutExchange imFanoutExchange;
|
||||||
|
|
||||||
|
@Value("${im.server.id:#{null}}") // 可选配置,优先使用配置的ID
|
||||||
|
private String configuredServerId;
|
||||||
|
|
||||||
|
private String serverId;
|
||||||
|
private String queueName;
|
||||||
|
private String routingKey;
|
||||||
|
private String broadcastQueueName;
|
||||||
|
|
||||||
|
@PostConstruct
|
||||||
|
public void init() {
|
||||||
|
// 确定服务器ID:优先使用配置,否则自动生成
|
||||||
|
this.serverId = determineServerId();
|
||||||
|
this.queueName = IM_QUEUE_PREFIX + serverId;
|
||||||
|
this.routingKey = IM_ROUTING_KEY_PREFIX + serverId;
|
||||||
|
this.broadcastQueueName = "im.broadcast.queue." + serverId;
|
||||||
|
|
||||||
|
// 创建点对点队列
|
||||||
|
Queue queue = new Queue(queueName, true, false, false);
|
||||||
|
amqpAdmin.declareQueue(queue);
|
||||||
|
|
||||||
|
// 绑定到直连交换机
|
||||||
|
Binding binding = new Binding(queueName, Binding.DestinationType.QUEUE,
|
||||||
|
IM_DIRECT_EXCHANGE, routingKey, null);
|
||||||
|
amqpAdmin.declareBinding(binding);
|
||||||
|
|
||||||
|
// 创建广播队列
|
||||||
|
Queue broadcastQueue = new Queue(broadcastQueueName, true, false, false);
|
||||||
|
amqpAdmin.declareQueue(broadcastQueue);
|
||||||
|
|
||||||
|
// 绑定广播队列到广播交换机
|
||||||
|
Binding broadcastBinding = new Binding(broadcastQueueName,
|
||||||
|
Binding.DestinationType.QUEUE,
|
||||||
|
IM_FANOUT_EXCHANGE,
|
||||||
|
"",
|
||||||
|
null);
|
||||||
|
amqpAdmin.declareBinding(broadcastBinding);
|
||||||
|
|
||||||
|
logger.info("=== RabbitMQ队列初始化完成 ===");
|
||||||
|
logger.info("服务器ID: {}", serverId);
|
||||||
|
logger.info("点对点队列: {}", queueName);
|
||||||
|
logger.info("路由键: {}", routingKey);
|
||||||
|
logger.info("广播队列: {}", broadcastQueueName);
|
||||||
|
logger.info("============================");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确定服务器ID
|
||||||
|
*/
|
||||||
|
private String determineServerId() {
|
||||||
|
// 1. 优先使用配置的服务器ID
|
||||||
|
if (configuredServerId != null && !configuredServerId.trim().isEmpty()) {
|
||||||
|
return configuredServerId.trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 使用自动生成的稳定服务器ID
|
||||||
|
return ServerIdGenerator.generateServerId();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServerId() {
|
||||||
|
return serverId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getQueueName() {
|
||||||
|
return queueName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRoutingKey() {
|
||||||
|
return routingKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取目标服务器的路由键
|
||||||
|
*/
|
||||||
|
public String getTargetRoutingKey(String targetServerId) {
|
||||||
|
return IM_ROUTING_KEY_PREFIX + targetServerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取服务器信息(用于监控)
|
||||||
|
*/
|
||||||
|
public ServerInfo getServerInfo() {
|
||||||
|
ServerInfo info = new ServerInfo();
|
||||||
|
info.setServerId(serverId);
|
||||||
|
info.setQueueName(queueName);
|
||||||
|
info.setRoutingKey(routingKey);
|
||||||
|
info.setBroadcastQueueName(broadcastQueueName);
|
||||||
|
info.setStartTime(System.currentTimeMillis());
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查队列是否存在
|
||||||
|
*/
|
||||||
|
public boolean isQueueExists(String queueName) {
|
||||||
|
try {
|
||||||
|
Queue queue = new Queue(queueName);
|
||||||
|
amqpAdmin.declareQueue(queue);
|
||||||
|
return true;
|
||||||
|
} catch (Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 初始化广播队列
|
||||||
|
*/
|
||||||
|
private void initBroadcastQueue() {
|
||||||
|
// 为当前服务器创建广播队列
|
||||||
|
String broadcastQueueName = "im.broadcast.queue." + serverId;
|
||||||
|
Queue broadcastQueue = new Queue(broadcastQueueName, true, false, false);
|
||||||
|
amqpAdmin.declareQueue(broadcastQueue);
|
||||||
|
|
||||||
|
// 绑定到广播交换机
|
||||||
|
Binding broadcastBinding = BindingBuilder.bind(broadcastQueue)
|
||||||
|
.to(imFanoutExchange);
|
||||||
|
amqpAdmin.declareBinding(broadcastBinding);
|
||||||
|
|
||||||
|
System.out.println("广播队列初始化完成: " + broadcastQueueName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务器信息类
|
||||||
|
*/
|
||||||
|
public static class ServerInfo {
|
||||||
|
private String serverId;
|
||||||
|
private String queueName;
|
||||||
|
private String routingKey;
|
||||||
|
private String broadcastQueueName;
|
||||||
|
private long startTime;
|
||||||
|
|
||||||
|
// getter和setter
|
||||||
|
public String getServerId() { return serverId; }
|
||||||
|
public void setServerId(String serverId) { this.serverId = serverId; }
|
||||||
|
public String getQueueName() { return queueName; }
|
||||||
|
public void setQueueName(String queueName) { this.queueName = queueName; }
|
||||||
|
public String getRoutingKey() { return routingKey; }
|
||||||
|
public void setRoutingKey(String routingKey) { this.routingKey = routingKey; }
|
||||||
|
public String getBroadcastQueueName() { return broadcastQueueName; }
|
||||||
|
public void setBroadcastQueueName(String broadcastQueueName) { this.broadcastQueueName = broadcastQueueName; }
|
||||||
|
public long getStartTime() { return startTime; }
|
||||||
|
public void setStartTime(long startTime) { this.startTime = startTime; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,12 @@
|
|||||||
package com.suisung.mall.im.common.websocket.service.onchat;
|
package com.suisung.mall.im.common.websocket.service.onchat;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
|
||||||
import cn.hutool.core.convert.Convert;
|
import cn.hutool.core.convert.Convert;
|
||||||
import cn.hutool.core.util.IdUtil;
|
import cn.hutool.core.util.IdUtil;
|
||||||
import cn.hutool.json.JSONUtil;
|
import cn.hutool.json.JSONUtil;
|
||||||
import com.suisung.mall.common.feignService.AccountService;
|
import com.suisung.mall.common.utils.StringUtils;
|
||||||
import com.suisung.mall.common.utils.CheckUtil;
|
import com.suisung.mall.im.common.websocket.service.DistributedMessageService;
|
||||||
|
import com.suisung.mall.im.common.websocket.service.DistributedSessionService;
|
||||||
|
import com.suisung.mall.im.common.websocket.service.LocalSessionManager;
|
||||||
import com.suisung.mall.im.common.websocket.utils.Constants;
|
import com.suisung.mall.im.common.websocket.utils.Constants;
|
||||||
import com.suisung.mall.im.pojo.entity.ChatHistory;
|
import com.suisung.mall.im.pojo.entity.ChatHistory;
|
||||||
import com.suisung.mall.im.pojo.vo.MineDTO;
|
import com.suisung.mall.im.pojo.vo.MineDTO;
|
||||||
@ -13,164 +14,85 @@ import com.suisung.mall.im.pojo.vo.ReceiveDTO;
|
|||||||
import com.suisung.mall.im.pojo.vo.SendVO;
|
import com.suisung.mall.im.pojo.vo.SendVO;
|
||||||
import com.suisung.mall.im.pojo.vo.ToDTO;
|
import com.suisung.mall.im.pojo.vo.ToDTO;
|
||||||
import com.suisung.mall.im.service.ChatHistoryService;
|
import com.suisung.mall.im.service.ChatHistoryService;
|
||||||
import com.suisung.mall.im.service.LayGroupService;
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
import org.springframework.web.socket.*;
|
import org.springframework.web.socket.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
|
||||||
public class MallsuiteImSocketHandler implements WebSocketHandler {
|
public class MallsuiteImSocketHandler implements WebSocketHandler {
|
||||||
|
|
||||||
private static Logger logger = LoggerFactory.getLogger(MallsuiteImSocketHandler.class);
|
private static Logger logger = LoggerFactory.getLogger(MallsuiteImSocketHandler.class);
|
||||||
|
|
||||||
private static ArrayList<WebSocketSession> users;
|
// 移除原有的静态集合,使用注入的服务
|
||||||
private static ArrayList<String> usersStr;
|
// private static ArrayList<WebSocketSession> users;
|
||||||
|
// private static ArrayList<String> usersStr;
|
||||||
|
// private static Map<String, List<WebSocketSession>> userSession;
|
||||||
|
// private static Map<String, List<WebSocketSession>> groupSession;
|
||||||
|
|
||||||
//存入用户的所有终端的连接信息
|
public static ConcurrentHashMap<String, List<WebSocketSession>> userSessions;
|
||||||
private static Map<String, List<WebSocketSession>> userSession;
|
|
||||||
|
|
||||||
//群组信息
|
public static ConcurrentHashMap<String, List<WebSocketSession>> groupSessions;
|
||||||
private static Map<String, List<WebSocketSession>> groupSession;
|
|
||||||
|
|
||||||
static {
|
@Autowired
|
||||||
users = new ArrayList<>();
|
private DistributedSessionService distributedSessionService;
|
||||||
usersStr = new ArrayList<>();
|
|
||||||
userSession = Collections.synchronizedMap(new HashMap<>());
|
|
||||||
groupSession = Collections.synchronizedMap(new HashMap<>());
|
|
||||||
logger = LoggerFactory.getLogger(MallsuiteImSocketHandler.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayList<String> getOnlineLoginNames() {
|
@Autowired
|
||||||
ArrayList<String> onlineLoginNames = new ArrayList<String>();
|
private DistributedMessageService distributedMessageService;
|
||||||
for (WebSocketSession user : users) {
|
|
||||||
String userName = (String) user.getAttributes().get(Constants.WEBSOCKET_LOGINNAME);
|
|
||||||
if (userName != null) {
|
|
||||||
onlineLoginNames.add(userName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return onlineLoginNames;
|
|
||||||
|
|
||||||
}
|
@Autowired
|
||||||
|
private LocalSessionManager localSessionManager;
|
||||||
public ArrayList<Integer> getOnlineLoginUserId() {
|
|
||||||
ArrayList<Integer> onlineLoginUserId = new ArrayList<Integer>();
|
|
||||||
for (WebSocketSession user : users) {
|
|
||||||
Integer user_id = Convert.toInt(user.getAttributes().get("user_id"));
|
|
||||||
if (CheckUtil.isNotEmpty(user_id)) {
|
|
||||||
onlineLoginUserId.add(user_id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return onlineLoginUserId;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
private ChatHistoryService chatHistoryService;
|
private ChatHistoryService chatHistoryService;
|
||||||
@Autowired
|
|
||||||
private LayGroupService layGroupService;
|
static {
|
||||||
@Autowired
|
userSessions=new ConcurrentHashMap<>();
|
||||||
private AccountService accountService;
|
groupSessions=new ConcurrentHashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
//用户上线后触发
|
//用户上线后触发
|
||||||
@Override
|
@Override
|
||||||
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
|
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
|
||||||
logger.debug("connect to the websocket success......");
|
logger.debug("connect to the websocket success......");
|
||||||
|
|
||||||
|
String loginUserId = String.valueOf(session.getAttributes().get("user_id")) ;
|
||||||
String sessionId = session.getId();
|
String sessionId = session.getId();
|
||||||
users.add(session);
|
|
||||||
this.updateOnlineStatus();
|
|
||||||
String loginUserId = (String) session.getAttributes().get(Constants.WEBSOCKET_LOGINNAME);//获取刚上线用户的loginName
|
|
||||||
|
|
||||||
List<WebSocketSession> loginSessions = userSession.get(loginUserId);
|
// 存储到本地会话管理
|
||||||
if (CollUtil.isEmpty(loginSessions)) {
|
this.addUserSession(loginUserId, session);
|
||||||
if (null == loginSessions) {
|
|
||||||
loginSessions = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
loginSessions.add(session);
|
logger.info("添加会话到本地成功:{}", localSessionManager.getUserSessions().values());
|
||||||
userSession.put(loginUserId, loginSessions);
|
|
||||||
} else {
|
|
||||||
if (!loginSessions.contains(session)) {
|
|
||||||
loginSessions.add(session);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (loginUserId != null) {
|
// 注册到分布式会话服务
|
||||||
SendVO msg = new SendVO();
|
Map<String, Object> attributes = new HashMap<>();
|
||||||
//读取离线信息
|
attributes.put("user_id", String.valueOf(session.getAttributes().get("user_id")));
|
||||||
ChatHistory chat = new ChatHistory();
|
attributes.put("loginName", loginUserId);
|
||||||
chat.setReceiver(loginUserId);//发给刚上线的用户信息
|
distributedSessionService.registerUserSession(loginUserId, sessionId, attributes);
|
||||||
chat.setStatus("0");
|
|
||||||
List<ChatHistory> list = chatHistoryService.findList(chat);
|
|
||||||
/*
|
|
||||||
for(ChatHistory c : list){
|
|
||||||
String sender="";
|
|
||||||
String receiver="";
|
|
||||||
|
|
||||||
if("friend".equals(c.getType()) || "user".equals(c.getType())){//如果是个人信息
|
// 处理离线消息等原有逻辑...
|
||||||
sender = c.getSender(); //todo 确认数据库存的数据uid user_id 不一致
|
// this.updateOnlineStatus();
|
||||||
msg.setId(sender);
|
|
||||||
msg.setToid(c.getReceiver());
|
|
||||||
msg.setContent(c.getMsg());
|
|
||||||
//todu 缺少数据
|
|
||||||
//msg = sender+Constants._msg_+c.getReceiver()+Constants._msg_+c.getMsg()+Constants._msg_;
|
|
||||||
}else if(c.getType().contains("_group")){//如果是直播群组信息
|
|
||||||
msg.setType("group");
|
|
||||||
|
|
||||||
sender = c.getSender();
|
|
||||||
String groupId = c.getReceiver().split("_")[0];//直播群组id
|
|
||||||
receiver=c.getReceiver().split("_")[1];//直播群组所属user_id
|
|
||||||
msg = sender+Constants._msg_+groupId+Constants._msg_+c.getMsg()+Constants._msg_;
|
|
||||||
|
|
||||||
}else{//如果是群组信息
|
|
||||||
msg.setType("group");
|
|
||||||
|
|
||||||
String groupId = c.getSender().split(Constants._msg_)[0];//群组id
|
|
||||||
sender=c.getSender().split(Constants._msg_)[1];//发送者loginName
|
|
||||||
msg = groupId+Constants._msg_+c.getReceiver()+Constants._msg_+c.getMsg()+Constants._msg_;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//todo 应该冗余
|
|
||||||
//获取用户基本信息
|
|
||||||
AccountUserBase accountUserBase = accountService.getUserBase(Integer.valueOf(sender));
|
|
||||||
//AccountUserBase accountUserBase= result.getFenResult(AccountUserBase.class);
|
|
||||||
//AccountUserInfo userInfo=accountService.getUserInfo(accountUserBase.getUser_id());
|
|
||||||
AccountUserInfo userInfo=accountService.getUserInfo(Integer.valueOf(sender));
|
|
||||||
|
|
||||||
|
|
||||||
msg.setUser_id(Convert.toInt(session.getAttributes().get("user_id")));
|
|
||||||
msg.setUsername(accountUserBase.getUser_nickname());
|
|
||||||
msg.setAvatar(userInfo.getUser_avatar());
|
|
||||||
//msg.setMessage_id(c.getId());
|
|
||||||
msg.setFromid(msg.getId());
|
|
||||||
msg.setTime(c.getCreate_date().getTime());
|
|
||||||
msg.setStatus(0); //?
|
|
||||||
msg.setMsg(new HashMap());
|
|
||||||
|
|
||||||
boolean isSuccess = this.sendMessageToUser(loginUserId, msg);
|
|
||||||
if(isSuccess) {
|
|
||||||
c.setStatus("1");//标记为已读
|
|
||||||
chatHistoryService.saveNew(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//接收js侧发送来的用户信息
|
//接收js侧发送来的用户信息
|
||||||
@Override
|
@Override
|
||||||
public void handleMessage(WebSocketSession session, WebSocketMessage<?> socketMessage) throws Exception {
|
public void handleMessage(WebSocketSession session, WebSocketMessage<?> socketMessage) throws Exception {
|
||||||
ReceiveDTO receiveDTO = JSONUtil.toBean(socketMessage.getPayload().toString(), ReceiveDTO.class);
|
ReceiveDTO receiveDTO = JSONUtil.toBean(socketMessage.getPayload().toString(), ReceiveDTO.class);
|
||||||
if (receiveDTO != null) {//发送消息
|
logger.info("接收到信息{}", receiveDTO);
|
||||||
|
if (receiveDTO != null) {
|
||||||
SendVO sendVO = new SendVO();
|
SendVO sendVO = new SendVO();
|
||||||
MineDTO mine = receiveDTO.getMine();
|
MineDTO mine = receiveDTO.getMine();
|
||||||
ToDTO to = receiveDTO.getTo();
|
ToDTO to = receiveDTO.getTo();
|
||||||
|
|
||||||
|
// 设置sendVO的代码保持不变...
|
||||||
sendVO.setToid(to.getId());
|
sendVO.setToid(to.getId());
|
||||||
sendVO.setZid(to.getZid()); //群组特有
|
sendVO.setZid(to.getZid());
|
||||||
sendVO.setId(mine.getId());
|
sendVO.setId(mine.getId());
|
||||||
Integer toUserId = Convert.toInt(session.getAttributes().get("user_id"));
|
Integer toUserId = Convert.toInt(session.getAttributes().get("user_id"));
|
||||||
sendVO.setUser_id(toUserId);
|
sendVO.setUser_id(toUserId);
|
||||||
@ -182,32 +104,33 @@ public class MallsuiteImSocketHandler implements WebSocketHandler {
|
|||||||
to.setType("friend");
|
to.setType("friend");
|
||||||
}
|
}
|
||||||
|
|
||||||
sendVO.setType(to.getType()); //是从to中读取Type
|
sendVO.setType(to.getType());
|
||||||
sendVO.setSendmethod(sendVO.getType());
|
sendVO.setSendmethod(sendVO.getType());
|
||||||
|
|
||||||
sendVO.setContent(mine.getContent());
|
sendVO.setContent(mine.getContent());
|
||||||
sendVO.setMessage_id(mine.getMessage_id());
|
sendVO.setMessage_id(mine.getMessage_id());
|
||||||
|
|
||||||
sendVO.setFromid(mine.getId());
|
sendVO.setFromid(mine.getId());
|
||||||
sendVO.setTime(new Date().getTime());
|
sendVO.setTime(new Date().getTime());
|
||||||
sendVO.setStatus(0); //?
|
sendVO.setStatus(0);
|
||||||
sendVO.setMsg_type(mine.getType());
|
sendVO.setMsg_type(mine.getType());
|
||||||
sendVO.setItem_id(mine.getItem_id());
|
sendVO.setItem_id(mine.getItem_id());
|
||||||
sendVO.setMsg(new HashMap());
|
sendVO.setMsg(new HashMap());
|
||||||
sendVO.setMessage_length(mine.getLength());
|
sendVO.setMessage_length(mine.getLength());
|
||||||
|
|
||||||
String sender = mine.getId();//信息发送者登录名(loginName)或user_id
|
String sender = mine.getId();
|
||||||
String receiver = to.getId();//信息接收者,如果是私聊就是用户loginName,如果是群聊就是群组id
|
String receiver = to.getId();
|
||||||
String msg = mine.getContent();
|
String msg = mine.getContent();
|
||||||
String avatar = mine.getAvatar();
|
String avatar = mine.getAvatar();
|
||||||
String type = to.getType();
|
String type = to.getType();
|
||||||
String senderName = mine.getUsername();//发送者姓名(name)
|
String senderName = mine.getUsername();
|
||||||
|
|
||||||
|
// 更新心跳
|
||||||
|
distributedSessionService.updateHeartbeat(sender, session.getId());
|
||||||
|
|
||||||
if (!mine.getId().equals(to.getId())) {
|
if (!mine.getId().equals(to.getId())) {
|
||||||
sendVO.setMine(false);
|
sendVO.setMine(false);
|
||||||
//保存聊天记录
|
|
||||||
if ("friend".equals(type) || "user".equals(type)) {
|
if ("friend".equals(type) || "user".equals(type)) {
|
||||||
//如果是私聊
|
// 私聊消息 - 使用分布式发送
|
||||||
ChatHistory chat = new ChatHistory();
|
ChatHistory chat = new ChatHistory();
|
||||||
chat.setId(IdUtil.simpleUUID());
|
chat.setId(IdUtil.simpleUUID());
|
||||||
chat.setSender(sender);
|
chat.setSender(sender);
|
||||||
@ -215,124 +138,48 @@ public class MallsuiteImSocketHandler implements WebSocketHandler {
|
|||||||
chat.setMsg(msg);
|
chat.setMsg(msg);
|
||||||
chat.setType("friend");
|
chat.setType("friend");
|
||||||
chat.setCreate_date(new Date());
|
chat.setCreate_date(new Date());
|
||||||
boolean isSuccess = this.sendMessageToUser(receiver, sendVO);
|
boolean isSuccess = distributedMessageService.sendToUser(String.valueOf(receiveDTO.getTo().getFriend_id()), sendVO);
|
||||||
if (isSuccess) {//如果信息发送成功
|
if (isSuccess) {
|
||||||
chat.setStatus("1");//设置为已读
|
chat.setStatus("1");
|
||||||
} else {
|
} else {
|
||||||
|
// 发送离线消息提醒
|
||||||
sendVO.setToid(mine.getId());
|
sendVO.setToid(mine.getId());
|
||||||
sendVO.setContent(to.getName() + " 对方现在离线,他将在上线后收到你的消息!");
|
sendVO.setContent(to.getName() + " 对方现在离线,他将在上线后收到你的消息!");
|
||||||
sendVO.setMsg_type("text");
|
sendVO.setMsg_type("text");
|
||||||
sendVO.setMessage_length("0");
|
sendVO.setMessage_length("0");
|
||||||
sendVO.setId(to.getId());
|
sendVO.setId(to.getId());
|
||||||
this.sendMessageToUser(sender, sendVO);//同时向本人发送对方不在线消息
|
distributedMessageService.sendToUser(String.valueOf(receiveDTO.getTo().getFriend_id()), sendVO);
|
||||||
chat.setStatus("0");//设置为未读
|
chat.setStatus("0");
|
||||||
}
|
}
|
||||||
chatHistoryService.saveNew(chat);
|
chatHistoryService.saveNew(chat);
|
||||||
} else if ("group".equals(type)) {//如果是群聊
|
|
||||||
// 临时,不经过数据库
|
|
||||||
List<WebSocketSession> groupLoginSession = groupSession.get(to.getZid());
|
|
||||||
|
|
||||||
if (!CollUtil.isEmpty(groupLoginSession)) {
|
} else if ("group".equals(type)) {
|
||||||
for (WebSocketSession gs : groupLoginSession) {
|
// 群聊消息 - 使用分布式发送
|
||||||
|
String groupId = to.getZid();
|
||||||
|
distributedMessageService.sendToGroup(groupId, sendVO, session.getId());
|
||||||
|
|
||||||
if (sender.equals(gs.getAttributes().get(Constants.WEBSOCKET_LOGINNAME).toString())) {//群消息不发给自己,但是存一条记录当做聊天记录查询
|
} else if ("join_group".equals(type)) {
|
||||||
|
// 加入群组
|
||||||
} else {
|
|
||||||
//设置接受用户
|
|
||||||
sendVO.setToid(gs.getAttributes().get(Constants.WEBSOCKET_LOGINNAME).toString());
|
|
||||||
boolean isSuccess = this.sendMessageToUser(to.getName(), sendVO);
|
|
||||||
if (isSuccess) {
|
|
||||||
|
|
||||||
} else {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//chatHistoryService.saveNew(chat);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
List<LayGroupUser> layGroupUserlist = new ArrayList();
|
|
||||||
|
|
||||||
//群主
|
|
||||||
LayGroupUser owner = new LayGroupUser();
|
|
||||||
LayGroup layGroup = layGroupService.get(receiver);
|
|
||||||
owner.setUser_id(layGroup.getCreate_by());
|
|
||||||
layGroupUserlist.add(owner);
|
|
||||||
|
|
||||||
//群成员1
|
|
||||||
List<LayGroupUser> zlist = layGroupService.getUsersByGroup(layGroupService.get(receiver));
|
|
||||||
layGroupUserlist.addAll(zlist);
|
|
||||||
|
|
||||||
for (LayGroupUser lgUser : layGroupUserlist) {
|
|
||||||
AccountUserBase user_base_row = accountService.getUserBase(Integer.valueOf(lgUser.getUser_id()));
|
|
||||||
ChatHistory chat = new ChatHistory();
|
|
||||||
chat.setId(IdUtil.simpleUUID());
|
|
||||||
//群聊时信息先发送给群聊id(即receiver),在后台转发给所有非发送者(sender)的人的话,群聊id(即receiver)就变成发送者。
|
|
||||||
String groupId = receiver;
|
|
||||||
//保存聊天记录
|
|
||||||
chat.setSender(groupId + Constants._msg_ + sender);//群聊时保存群聊id和发送者id
|
|
||||||
chat.setReceiver(user_base_row.getUser_account());//群中所有信息获得者人员
|
|
||||||
chat.setMsg(msg);
|
|
||||||
chat.setType("group");
|
|
||||||
//message = groupId + Constants._msg_ + user_base_row.getUser_account() + Constants._msg_ + msg + Constants._msg_ + avatar + Constants._msg_ + type + Constants._msg_ + senderName + Constants._msg_ + datatime;
|
|
||||||
if (sender.equals(user_base_row.getUser_account())) {//群消息不发给自己,但是存一条记录当做聊天记录查询
|
|
||||||
chat.setStatus("1");//发送成功,设置为已读
|
|
||||||
} else {
|
|
||||||
boolean isSuccess = this.sendMessageToUser(user_base_row.getUser_account(), sendVO);
|
|
||||||
if (isSuccess) {
|
|
||||||
chat.setStatus("1");//发送成功,设置为已读
|
|
||||||
} else {
|
|
||||||
chat.setStatus("0");//用户不在线,设置为未读
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
chatHistoryService.saveNew(chat);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
} else if ("join_group".equals(type)) { //临时群组,聚焦当前窗口
|
|
||||||
String zid = to.getZid();
|
String zid = to.getZid();
|
||||||
|
|
||||||
//设置session,属性上标出所属群组: 目前无同时多个群组需求,如果存在,此处存入list
|
|
||||||
session.getAttributes().put(Constants.WEBSOCKET_GROUP_KEY, zid);
|
session.getAttributes().put(Constants.WEBSOCKET_GROUP_KEY, zid);
|
||||||
|
|
||||||
//设置群组中用户
|
// 添加到本地群组
|
||||||
List<WebSocketSession> groupLoginSession = groupSession.get(zid);
|
this.addGroupSession(zid, session);
|
||||||
|
|
||||||
if (CollUtil.isEmpty(groupLoginSession)) {
|
// 添加到分布式群组
|
||||||
if (null == groupLoginSession) {
|
String userId = (String) session.getAttributes().get(Constants.WEBSOCKET_LOGINNAME);
|
||||||
groupLoginSession = new ArrayList<>();
|
distributedSessionService.addUserToGroup(zid, userId, session.getId());
|
||||||
}
|
|
||||||
|
|
||||||
groupLoginSession.add(session);
|
} else if ("leave_group".equals(type)) {
|
||||||
groupSession.put(zid, groupLoginSession);
|
// 离开群组
|
||||||
} else {
|
|
||||||
if (!groupLoginSession.contains(session)) {
|
|
||||||
groupLoginSession.add(session);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//todo通知已存在群组用户消息 可启动独立task
|
|
||||||
} else if ("leave_group".equals(type)) { //临时群组,聚焦当前窗口
|
|
||||||
String zid = to.getZid();
|
String zid = to.getZid();
|
||||||
|
|
||||||
//设置session,属性上标出所属群组
|
|
||||||
session.getAttributes().put(Constants.WEBSOCKET_GROUP_KEY, null);
|
session.getAttributes().put(Constants.WEBSOCKET_GROUP_KEY, null);
|
||||||
|
|
||||||
//设置群组中用户
|
// 从本地群组移除
|
||||||
List<WebSocketSession> groupLoginSession = groupSession.get(zid);
|
localSessionManager.removeGroupSession(zid, session);
|
||||||
|
|
||||||
if (CollUtil.isEmpty(groupLoginSession)) {
|
// 从分布式群组移除
|
||||||
} else {
|
distributedSessionService.removeUserFromGroup(zid, session.getId());
|
||||||
if (groupLoginSession.contains(session)) {
|
|
||||||
groupLoginSession.remove(session);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//todo通知已存在群组用户消息 可启动独立task
|
|
||||||
} else {
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sendVO.setMine(true);
|
sendVO.setMine(true);
|
||||||
@ -345,29 +192,50 @@ public class MallsuiteImSocketHandler implements WebSocketHandler {
|
|||||||
if (session.isOpen()) {
|
if (session.isOpen()) {
|
||||||
session.close();
|
session.close();
|
||||||
}
|
}
|
||||||
logger.debug("websocket connection closed......");
|
logger.info("websocket connection closed......{}",session);
|
||||||
users.remove(session);
|
cleanupSession(session);
|
||||||
userSession.get(session.getAttributes().get(Constants.WEBSOCKET_LOGINNAME)).remove(session);
|
|
||||||
|
|
||||||
if (CollUtil.isNotEmpty(groupSession.get(session.getAttributes().get(Constants.WEBSOCKET_GROUP_KEY)))) {
|
|
||||||
groupSession.get(session.getAttributes().get(Constants.WEBSOCKET_GROUP_KEY)).remove(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updateOnlineStatus();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
|
public void afterConnectionClosed(WebSocketSession session, CloseStatus closeStatus) throws Exception {
|
||||||
logger.debug("websocket connection closed......");
|
logger.info("websocket connection closed......{}",session);
|
||||||
users.remove(session);
|
cleanupSession(session);
|
||||||
userSession.get(session.getAttributes().get(Constants.WEBSOCKET_LOGINNAME)).remove(session);
|
}
|
||||||
|
|
||||||
if (CollUtil.isNotEmpty(groupSession.get(session.getAttributes().get(Constants.WEBSOCKET_GROUP_KEY)))) {
|
/**
|
||||||
groupSession.get(session.getAttributes().get(Constants.WEBSOCKET_GROUP_KEY)).remove(session);
|
* 清理会话资源
|
||||||
|
*/
|
||||||
|
private void cleanupSession(WebSocketSession session) {
|
||||||
|
String loginUserId = String.valueOf(session.getAttributes().get("user_id")) ;
|
||||||
|
String sessionId = session.getId();
|
||||||
|
String groupId ="";
|
||||||
|
if(null!=session.getAttributes().get(Constants.WEBSOCKET_GROUP_KEY)){
|
||||||
|
groupId = String.valueOf(session.getAttributes().get(Constants.WEBSOCKET_GROUP_KEY)) ;
|
||||||
|
}
|
||||||
|
// 从本地管理移除
|
||||||
|
if(localSessionManager.getUserSessions()==null){
|
||||||
|
localSessionManager.setUserSessions(new ConcurrentHashMap<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
this.updateOnlineStatus();
|
if(StringUtils.isNotEmpty(loginUserId)){
|
||||||
|
localSessionManager.removeUserSession(loginUserId, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isNotEmpty(groupId)) {
|
||||||
|
localSessionManager.removeGroupSession(groupId, session);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从分布式存储注销
|
||||||
|
List<WebSocketSession> sessions= userSessions.get(loginUserId);
|
||||||
|
if(null==sessions || sessions.isEmpty()){
|
||||||
|
distributedSessionService.unregisterUserSession(loginUserId, sessionId);
|
||||||
|
}
|
||||||
|
List<WebSocketSession> groupSessionList= groupSessions.get(groupId);
|
||||||
|
if (null==groupSessionList||groupSessionList.isEmpty()) {
|
||||||
|
distributedSessionService.removeUserFromGroup(groupId, sessionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// this.updateOnlineStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -376,64 +244,143 @@ public class MallsuiteImSocketHandler implements WebSocketHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 给所有在线用户发送消息
|
* 给某个用户发送消息 (兼容原有接口)
|
||||||
*
|
|
||||||
* @param message
|
|
||||||
*/
|
*/
|
||||||
public void sendMessageToAllUsers(List<String> message) {
|
public boolean sendMessageToUser(String loginName, SendVO message) {
|
||||||
|
return distributedMessageService.sendToUser(loginName, message);
|
||||||
|
}
|
||||||
|
|
||||||
SendVO msg = new SendVO();
|
/**
|
||||||
msg.setContent(message.toString());
|
* 获取所有在线用户登录名
|
||||||
msg.setType("online");
|
*/
|
||||||
msg.setSendmethod(msg.getType());
|
public List<String> getOnlineLoginNames() {
|
||||||
|
// 使用分布式服务获取所有在线用户登录名
|
||||||
|
return distributedSessionService.getOnlineLoginNames();
|
||||||
|
}
|
||||||
|
|
||||||
//不能这么操作
|
/**
|
||||||
for (WebSocketSession user : users) {
|
* 获取所有在线用户ID
|
||||||
try {
|
*/
|
||||||
if (user.isOpen()) {
|
public List<Integer> getOnlineLoginUserId() {
|
||||||
user.sendMessage(new TextMessage(msg.toString()));
|
// 使用分布式服务获取所有在线用户ID
|
||||||
|
return distributedSessionService.getAllOnlineUserIds();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前服务器的在线用户ID
|
||||||
|
*/
|
||||||
|
public List<Integer> getLocalOnlineLoginUserId() {
|
||||||
|
// 使用本地会话管理器获取当前服务器的在线用户ID
|
||||||
|
return localSessionManager.getLocalOnlineUserIds();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前服务器的在线用户登录名
|
||||||
|
*/
|
||||||
|
public List<String> getLocalOnlineLoginNames() {
|
||||||
|
// 使用本地会话管理器获取当前服务器的在线用户登录名
|
||||||
|
return localSessionManager.getLocalOnlineLoginNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新在线状态 - 通知所有用户在线列表变化
|
||||||
|
*/
|
||||||
|
public void updateOnlineStatus() {
|
||||||
|
try {
|
||||||
|
// 创建在线状态消息
|
||||||
|
SendVO onlineStatusMsg = new SendVO();
|
||||||
|
onlineStatusMsg.setType("online_status");
|
||||||
|
onlineStatusMsg.setSendmethod("broadcast");
|
||||||
|
|
||||||
|
// 获取在线用户列表
|
||||||
|
List<String> onlineUsers = getOnlineLoginNames();
|
||||||
|
List<Integer> onlineUserIds = getOnlineLoginUserId();
|
||||||
|
|
||||||
|
Map<String, Object> data = new HashMap<>();
|
||||||
|
data.put("onlineUsers", onlineUsers);
|
||||||
|
data.put("onlineUserIds", onlineUserIds);
|
||||||
|
data.put("timestamp", System.currentTimeMillis());
|
||||||
|
|
||||||
|
onlineStatusMsg.setMsg(data);
|
||||||
|
|
||||||
|
// 广播在线状态更新(只广播到当前服务器,避免循环广播)
|
||||||
|
broadcastLocalOnlineStatus(onlineStatusMsg);
|
||||||
|
|
||||||
|
logger.info("在线状态更新: {} 个用户在线", onlineUsers.size());
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.error("更新在线状态失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 广播在线状态到本地用户
|
||||||
|
*/
|
||||||
|
private void broadcastLocalOnlineStatus(SendVO message) {
|
||||||
|
logger.info("localSessionManager: {}", localSessionManager);
|
||||||
|
logger.info("localSessionManager.userSessions: {}", localSessionManager.getUserSessions());
|
||||||
|
logger.info("localSessionManager.userSessions.values: {}", localSessionManager.getUserSessions().values());
|
||||||
|
if (localSessionManager == null || localSessionManager.getUserSessions().isEmpty()) {
|
||||||
|
logger.error("localSessionManager is null, cannot broadcast online status");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取本地所有用户会话
|
||||||
|
for (List<WebSocketSession> sessions : localSessionManager.getUserSessions().values()) {
|
||||||
|
for (WebSocketSession session : sessions) {
|
||||||
|
if (session!=null&&session.isOpen()) {
|
||||||
|
try {
|
||||||
|
session.sendMessage(new TextMessage(message.toString()));
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.error("发送在线状态消息失败: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error("给所有在线用户发送信息失败!" + e.getMessage(), e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 给某个用户发送消息
|
* 给所有在线用户发送消息
|
||||||
*
|
|
||||||
* @param loginName
|
|
||||||
* @param message
|
|
||||||
*/
|
*/
|
||||||
public boolean sendMessageToUser(String loginName, SendVO message) {
|
public void sendMessageToAllUsers(SendVO message) {
|
||||||
boolean result = false;
|
// 获取所有在线用户ID
|
||||||
|
List<Integer> onlineUserIds = getOnlineLoginUserId();
|
||||||
|
|
||||||
//不能这样操作。
|
for (Integer userId : onlineUserIds) {
|
||||||
//for (WebSocketSession user : users) {
|
String userIdStr = String.valueOf(userId);
|
||||||
List<WebSocketSession> lst = userSession.get(message.getToid());
|
distributedMessageService.sendToUser(userIdStr, message);
|
||||||
|
|
||||||
if (CollUtil.isNotEmpty(lst)) {
|
|
||||||
for (WebSocketSession user : lst) {
|
|
||||||
//if (user.getAttributes().get(Constants.WEBSOCKET_LOGINNAME).equals(loginName)) {//允许用户多个浏览器登录,每个浏览器页面都会收到用户信息
|
|
||||||
try {
|
|
||||||
if (user.isOpen()) {
|
|
||||||
user.sendMessage(new TextMessage(message.toString()));
|
|
||||||
result = true;
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.error("给用户发送信息失败!" + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
|
|
||||||
//break;//注释掉此处意味着遍历该用户打开的所有页面并发送信息,否则只会向用户登录的第一个网页发送信息。
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
logger.debug("向 {} 个在线用户发送消息", onlineUserIds.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateOnlineStatus() {
|
/**
|
||||||
//this.sendMessageToAllUsers(this.getOnlineLoginNames());//通知所有用户更新在线信息
|
* 添加用户会话
|
||||||
|
*/
|
||||||
|
public void addUserSession(String userId, WebSocketSession session) {
|
||||||
|
if (userId == null || session == null) {
|
||||||
|
logger.info("添加用户会话失败: 参数为空");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
userSessions.compute(userId, (key, existingSessions) -> {
|
||||||
|
if (existingSessions == null) {
|
||||||
|
existingSessions = new CopyOnWriteArrayList<>();
|
||||||
|
}
|
||||||
|
// if (!existingSessions.contains(session)) {
|
||||||
|
// existingSessions.add(session);
|
||||||
|
// }
|
||||||
|
existingSessions.add(session);
|
||||||
|
return existingSessions;
|
||||||
|
});
|
||||||
|
|
||||||
|
logger.info("添加用户会话成功: userId={}, sessionId={}", userId, session.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 添加群组会话
|
||||||
|
*/
|
||||||
|
public void addGroupSession(String groupId, WebSocketSession session) {
|
||||||
|
groupSessions.computeIfAbsent(groupId, k -> new CopyOnWriteArrayList<>())
|
||||||
|
.add(session);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,153 @@
|
|||||||
|
package com.suisung.mall.im.common.websocket.utils;
|
||||||
|
|
||||||
|
import java.net.InetAddress;
|
||||||
|
import java.net.NetworkInterface;
|
||||||
|
import java.net.SocketException;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 服务器ID生成工具类
|
||||||
|
*/
|
||||||
|
public class ServerIdGenerator {
|
||||||
|
|
||||||
|
private static volatile String cachedServerId;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成服务器唯一标识
|
||||||
|
*/
|
||||||
|
public static String generateServerId() {
|
||||||
|
if (cachedServerId != null) {
|
||||||
|
return cachedServerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized (ServerIdGenerator.class) {
|
||||||
|
if (cachedServerId != null) {
|
||||||
|
return cachedServerId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试多种方式生成稳定的服务器ID
|
||||||
|
String serverId = generateStableServerId();
|
||||||
|
cachedServerId = serverId;
|
||||||
|
return serverId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成稳定的服务器ID(基于机器标识)
|
||||||
|
*/
|
||||||
|
private static String generateStableServerId() {
|
||||||
|
StringBuilder serverId = new StringBuilder("im-server-");
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 使用MAC地址(最稳定)
|
||||||
|
String macAddress = getPhysicalMacAddress();
|
||||||
|
if (!"UNKNOWN-MAC".equals(macAddress)) {
|
||||||
|
// 取MAC地址的后6位作为标识
|
||||||
|
String macSuffix = macAddress.replace(":", "").replace("-", "");
|
||||||
|
if (macSuffix.length() >= 6) {
|
||||||
|
serverId.append(macSuffix.substring(macSuffix.length() - 6).toLowerCase());
|
||||||
|
return serverId.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 使用IP地址
|
||||||
|
String ipSuffix = getLocalIpSuffix();
|
||||||
|
if (!"UNKNOWN-IP".equals(ipSuffix)) {
|
||||||
|
serverId.append(ipSuffix);
|
||||||
|
return serverId.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 使用主机名哈希
|
||||||
|
String hostname = InetAddress.getLocalHost().getHostName();
|
||||||
|
int hostnameHash = Math.abs(hostname.hashCode());
|
||||||
|
serverId.append(String.format("%06x", hostnameHash & 0xFFFFFF));
|
||||||
|
return serverId.toString();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
// 4. 最终备选:UUID
|
||||||
|
String uuidSuffix = UUID.randomUUID().toString().replace("-", "").substring(0, 6);
|
||||||
|
serverId.append(uuidSuffix);
|
||||||
|
return serverId.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取物理MAC地址
|
||||||
|
*/
|
||||||
|
private static String getPhysicalMacAddress() {
|
||||||
|
try {
|
||||||
|
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
|
||||||
|
while (interfaces.hasMoreElements()) {
|
||||||
|
NetworkInterface networkInterface = interfaces.nextElement();
|
||||||
|
|
||||||
|
// 排除回环、虚拟、未启用的接口
|
||||||
|
if (networkInterface.isLoopback() ||
|
||||||
|
networkInterface.isVirtual() ||
|
||||||
|
!networkInterface.isUp()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 排除常见的虚拟接口名称
|
||||||
|
String name = networkInterface.getName().toLowerCase();
|
||||||
|
if (name.contains("virtual") || name.contains("vmware") ||
|
||||||
|
name.contains("vbox") || name.contains("docker") ||
|
||||||
|
name.contains("wsl")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] mac = networkInterface.getHardwareAddress();
|
||||||
|
if (mac != null && mac.length > 0) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int i = 0; i < mac.length; i++) {
|
||||||
|
sb.append(String.format("%02X", mac[i]));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SocketException e) {
|
||||||
|
// 忽略异常
|
||||||
|
}
|
||||||
|
return "UNKNOWN-MAC";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取本地IP地址后缀
|
||||||
|
*/
|
||||||
|
private static String getLocalIpSuffix() {
|
||||||
|
try {
|
||||||
|
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
|
||||||
|
while (interfaces.hasMoreElements()) {
|
||||||
|
NetworkInterface networkInterface = interfaces.nextElement();
|
||||||
|
if (networkInterface.isLoopback() || !networkInterface.isUp()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Enumeration<InetAddress> addresses = networkInterface.getInetAddresses();
|
||||||
|
while (addresses.hasMoreElements()) {
|
||||||
|
InetAddress address = addresses.nextElement();
|
||||||
|
if (address.isSiteLocalAddress()) {
|
||||||
|
String ip = address.getHostAddress();
|
||||||
|
// 取IP地址的最后一段
|
||||||
|
String[] segments = ip.split("\\.");
|
||||||
|
if (segments.length == 4) {
|
||||||
|
return segments[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SocketException e) {
|
||||||
|
// 忽略异常
|
||||||
|
}
|
||||||
|
return "UNKNOWN-IP";
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 生成带时间戳的服务器ID(适合临时使用)
|
||||||
|
*/
|
||||||
|
public static String generateTimestampServerId() {
|
||||||
|
String baseId = generateServerId();
|
||||||
|
long timestamp = System.currentTimeMillis() % 1000000; // 取后6位
|
||||||
|
return baseId + "-" + String.format("%06d", timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,11 +8,13 @@ import cn.hutool.core.util.ObjectUtil;
|
|||||||
import com.suisung.mall.common.api.CommonResult;
|
import com.suisung.mall.common.api.CommonResult;
|
||||||
import com.suisung.mall.common.service.impl.BaseControllerImpl;
|
import com.suisung.mall.common.service.impl.BaseControllerImpl;
|
||||||
import com.suisung.mall.common.utils.I18nUtil;
|
import com.suisung.mall.common.utils.I18nUtil;
|
||||||
|
import com.suisung.mall.im.common.websocket.service.DistributedSessionService;
|
||||||
import com.suisung.mall.im.common.websocket.service.onchat.MallsuiteImSocketHandler;
|
import com.suisung.mall.im.common.websocket.service.onchat.MallsuiteImSocketHandler;
|
||||||
import io.swagger.annotations.Api;
|
import io.swagger.annotations.Api;
|
||||||
import io.swagger.annotations.ApiOperation;
|
import io.swagger.annotations.ApiOperation;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
import org.springframework.web.bind.annotation.RequestMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestMethod;
|
import org.springframework.web.bind.annotation.RequestMethod;
|
||||||
@ -32,6 +34,9 @@ public class ChatSocketInfoController {
|
|||||||
|
|
||||||
private static Logger logger = LoggerFactory.getLogger(ChatSocketInfoController.class);
|
private static Logger logger = LoggerFactory.getLogger(ChatSocketInfoController.class);
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DistributedSessionService distributedSessionService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取socket在线用户信息
|
* 获取socket在线用户信息
|
||||||
*/
|
*/
|
||||||
@ -40,7 +45,8 @@ public class ChatSocketInfoController {
|
|||||||
@RequestMapping(value = "/getUserOnline", method = RequestMethod.POST)
|
@RequestMapping(value = "/getUserOnline", method = RequestMethod.POST)
|
||||||
public List<Integer> getUserOnline(@RequestBody List<Integer> user_ids) {
|
public List<Integer> getUserOnline(@RequestBody List<Integer> user_ids) {
|
||||||
logger.info(I18nUtil._("接收的用户ids:") + CollUtil.join(user_ids, ","));
|
logger.info(I18nUtil._("接收的用户ids:") + CollUtil.join(user_ids, ","));
|
||||||
ArrayList<Integer> onlineLoginUserIds = new MallsuiteImSocketHandler().getOnlineLoginUserId();
|
List<Integer> onlineLoginUserIds = distributedSessionService.getOnlineUserIds();
|
||||||
|
// List<Integer> onlineLoginUserIds = new MallsuiteImSocketHandler().getOnlineLoginUserId();
|
||||||
Iterator<Integer> online_user_ids_iter = onlineLoginUserIds.iterator();
|
Iterator<Integer> online_user_ids_iter = onlineLoginUserIds.iterator();
|
||||||
// 处理移除非本店铺客服
|
// 处理移除非本店铺客服
|
||||||
while (online_user_ids_iter.hasNext()) {
|
while (online_user_ids_iter.hasNext()) {
|
||||||
|
|||||||
@ -37,6 +37,15 @@ spring:
|
|||||||
max-wait: -1ms
|
max-wait: -1ms
|
||||||
max-idle: 64
|
max-idle: 64
|
||||||
min-idle: 0
|
min-idle: 0
|
||||||
|
rabbitmq:
|
||||||
|
host: @rabbitmq.host@
|
||||||
|
port: @rabbitmq.port@
|
||||||
|
virtual-host: @rabbitmq.virtual-host@
|
||||||
|
listener:
|
||||||
|
simple:
|
||||||
|
acknowledge-mode: manual #手动确认消息,不使用默认的消费端确认
|
||||||
|
username: @rabbitmq.username@
|
||||||
|
password: @rabbitmq.password@
|
||||||
cloud:
|
cloud:
|
||||||
nacos:
|
nacos:
|
||||||
discovery:
|
discovery:
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user