Skip to content
项目
群组
代码片段
帮助
正在加载...
登录
切换导航
R
run-score
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
jackma
run-score
Commits
d3b8a353
提交
d3b8a353
authored
11月 01, 2021
作者:
jacksmith1988
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
//init
上级
ed53a40a
隐藏空白字符变更
内嵌
并排
正在显示
12 个修改的文件
包含
1109 行增加
和
991 行删除
+1109
-991
Constant.java
...i/src/main/java/me/zohar/runscore/constants/Constant.java
+4
-0
GatheringCode.java
...me/zohar/runscore/gatheringcode/domain/GatheringCode.java
+59
-47
GatheringCodeParam.java
...ohar/runscore/gatheringcode/param/GatheringCodeParam.java
+13
-0
GatheringCodeService.java
.../runscore/gatheringcode/service/GatheringCodeService.java
+145
-139
MerchantOrderService.java
...zohar/runscore/merchant/service/MerchantOrderService.java
+677
-653
OrderGatheringCodeVO.java
...a/me/zohar/runscore/merchant/vo/OrderGatheringCodeVO.java
+4
-0
LoginLog.java
...n/java/me/zohar/runscore/useraccount/domain/LoginLog.java
+1
-0
UserAccountRepo.java
...a/me/zohar/runscore/useraccount/repo/UserAccountRepo.java
+3
-0
ApiController.java
.../java/me/zohar/runscore/api/controller/ApiController.java
+5
-0
StartOrderTest.java
.../src/test/java/me/zohar/runscore/test/StartOrderTest.java
+4
-3
gathering-code.js
...ore-mobile/src/main/resources/static/js/gathering-code.js
+26
-22
gathering-code.html
...e-mobile/src/main/resources/templates/gathering-code.html
+168
-127
没有找到文件。
runscore-api/src/main/java/me/zohar/runscore/constants/Constant.java
浏览文件 @
d3b8a353
...
...
@@ -160,4 +160,8 @@ public class Constant {
public
static
final
String
下级账号查询范围
_
指定账号及直接下级
=
"20"
;
public
static
final
String
支付渠道
_
银行卡
=
"bankcard"
;
public
static
final
String
支付渠道
_
微信码
=
"wechat"
;
public
static
final
String
支付渠道
_
支付宝码
=
"alipay"
;
}
runscore-api/src/main/java/me/zohar/runscore/gatheringcode/domain/GatheringCode.java
浏览文件 @
d3b8a353
...
...
@@ -29,52 +29,64 @@ import me.zohar.runscore.useraccount.domain.UserAccount;
@DynamicUpdate
(
true
)
public
class
GatheringCode
{
/**
* 主键id
*/
@Id
@Column
(
name
=
"id"
,
length
=
32
)
private
String
id
;
/**
* 收款渠道
*/
private
String
gatheringChannelCode
;
/**
* 状态,启用:1;禁用:0
*/
private
String
state
;
private
Boolean
fixedGatheringAmount
;
/**
* 收款金额
*/
private
Double
gatheringAmount
;
/**
* 收款人
*/
private
String
payee
;
/**
* 创建时间
*/
private
Date
createTime
;
@Column
(
name
=
"storage_id"
,
length
=
32
)
private
String
storageId
;
/**
* 用户账号id
*/
@Column
(
name
=
"user_account_id"
,
length
=
32
)
private
String
userAccountId
;
@NotFound
(
action
=
NotFoundAction
.
IGNORE
)
@ManyToOne
(
fetch
=
FetchType
.
LAZY
)
@JoinColumn
(
name
=
"user_account_id"
,
updatable
=
false
,
insertable
=
false
,
foreignKey
=
@ForeignKey
(
value
=
ConstraintMode
.
NO_CONSTRAINT
))
private
UserAccount
userAccount
;
/**
* 主键id
*/
@Id
@Column
(
name
=
"id"
,
length
=
32
)
private
String
id
;
/**
* 收款渠道
*/
private
String
gatheringChannelCode
;
/**
* 状态,启用:1;禁用:0
*/
private
String
state
;
private
Boolean
fixedGatheringAmount
;
/**
* 收款金额
*/
private
Double
gatheringAmount
;
/**
* 收款人
*/
private
String
payee
;
/**
* 开户行
*/
private
String
bankAddress
;
/**
* 卡号
*/
private
String
bankCode
;
/**
* 卡户主
*/
private
String
bankUsername
;
/**
* 创建时间
*/
private
Date
createTime
;
@Column
(
name
=
"storage_id"
,
length
=
32
)
private
String
storageId
;
/**
* 用户账号id
*/
@Column
(
name
=
"user_account_id"
,
length
=
32
)
private
String
userAccountId
;
@NotFound
(
action
=
NotFoundAction
.
IGNORE
)
@ManyToOne
(
fetch
=
FetchType
.
LAZY
)
@JoinColumn
(
name
=
"user_account_id"
,
updatable
=
false
,
insertable
=
false
,
foreignKey
=
@ForeignKey
(
value
=
ConstraintMode
.
NO_CONSTRAINT
))
private
UserAccount
userAccount
;
}
runscore-api/src/main/java/me/zohar/runscore/gatheringcode/param/GatheringCodeParam.java
浏览文件 @
d3b8a353
...
...
@@ -54,6 +54,19 @@ public class GatheringCodeParam {
private
String
storageId
;
/**
* 开户行
*/
private
String
bankAddress
;
/**
* 卡号
*/
private
String
bankCode
;
/**
* 卡户主
*/
private
String
bankUsername
;
public
GatheringCode
convertToPo
(
String
userAccountId
)
{
GatheringCode
po
=
new
GatheringCode
();
BeanUtils
.
copyProperties
(
this
,
po
);
...
...
runscore-api/src/main/java/me/zohar/runscore/gatheringcode/service/GatheringCodeService.java
浏览文件 @
d3b8a353
...
...
@@ -38,150 +38,156 @@ import me.zohar.runscore.useraccount.repo.UserAccountRepo;
@Service
public
class
GatheringCodeService
{
@Autowired
private
GatheringCodeRepo
gatheringCodeRepo
;
@Autowired
private
StorageRepo
storageRepo
;
@Autowired
private
UserAccountRepo
userAccountRepo
;
@Transactional
public
void
delMyGatheringCodeById
(
String
id
,
String
userAccountId
)
{
GatheringCode
gatheringCode
=
gatheringCodeRepo
.
getOne
(
id
);
if
(!
userAccountId
.
equals
(
gatheringCode
.
getUserAccountId
()))
{
throw
new
BizException
(
BizError
.
无权删除数据
);
}
delGatheringCodeById
(
id
);
}
@Transactional
public
void
delGatheringCodeById
(
String
id
)
{
GatheringCode
gatheringCode
=
gatheringCodeRepo
.
getOne
(
id
);
disassociationGatheringCodeStorage
(
gatheringCode
.
getStorageId
());
gatheringCodeRepo
.
delete
(
gatheringCode
);
}
@Transactional
(
readOnly
=
true
)
public
GatheringCodeVO
findMyGatheringCodeById
(
String
id
,
String
userAccountId
)
{
GatheringCodeVO
vo
=
findGatheringCodeById
(
id
);
if
(!
userAccountId
.
equals
(
vo
.
getUserAccountId
()))
{
throw
new
BizException
(
BizError
.
无权查看数据
);
}
return
vo
;
}
@Transactional
(
readOnly
=
true
)
public
GatheringCodeVO
findGatheringCodeById
(
String
id
)
{
GatheringCode
gatheringCode
=
gatheringCodeRepo
.
getOne
(
id
);
return
GatheringCodeVO
.
convertFor
(
gatheringCode
);
}
@Transactional
(
readOnly
=
true
)
public
PageResult
<
GatheringCodeVO
>
findMyGatheringCodeByPage
(
GatheringCodeQueryCondParam
param
)
{
if
(
StrUtil
.
isBlank
(
param
.
getUserAccountId
()))
{
throw
new
BizException
(
BizError
.
无权查看数据
);
}
return
findGatheringCodeByPage
(
param
);
}
@Transactional
(
readOnly
=
true
)
public
PageResult
<
GatheringCodeVO
>
findGatheringCodeByPage
(
GatheringCodeQueryCondParam
param
)
{
Specification
<
GatheringCode
>
spec
=
new
Specification
<
GatheringCode
>()
{
/**
*
*/
private
static
final
long
serialVersionUID
=
1L
;
public
Predicate
toPredicate
(
Root
<
GatheringCode
>
root
,
CriteriaQuery
<?>
query
,
CriteriaBuilder
builder
)
{
List
<
Predicate
>
predicates
=
new
ArrayList
<
Predicate
>();
if
(
StrUtil
.
isNotEmpty
(
param
.
getState
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"state"
),
param
.
getState
()));
}
if
(
StrUtil
.
isNotEmpty
(
param
.
getGatheringChannelCode
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"gatheringChannelCode"
),
param
.
getGatheringChannelCode
()));
}
if
(
StrUtil
.
isNotEmpty
(
param
.
getPayee
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"payee"
),
param
.
getPayee
()));
}
if
(
StrUtil
.
isNotEmpty
(
param
.
getUserName
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
join
(
"userAccount"
,
JoinType
.
INNER
).
get
(
"userName"
),
param
.
getUserName
()));
}
if
(
StrUtil
.
isNotEmpty
(
param
.
getUserAccountId
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"userAccountId"
),
param
.
getUserAccountId
()));
}
return
predicates
.
size
()
>
0
?
builder
.
and
(
predicates
.
toArray
(
new
Predicate
[
predicates
.
size
()]))
:
null
;
}
};
Page
<
GatheringCode
>
result
=
gatheringCodeRepo
.
findAll
(
spec
,
PageRequest
.
of
(
param
.
getPageNum
()
-
1
,
param
.
getPageSize
(),
Sort
.
by
(
Sort
.
Order
.
desc
(
"createTime"
))));
PageResult
<
GatheringCodeVO
>
pageResult
=
new
PageResult
<>(
GatheringCodeVO
.
convertFor
(
result
.
getContent
()),
param
.
getPageNum
(),
param
.
getPageSize
(),
result
.
getTotalElements
());
return
pageResult
;
}
@Transactional
public
void
associateGatheringCodeStorage
(
String
storageId
,
String
gatheringCodeId
)
{
Storage
storage
=
storageRepo
.
getOne
(
storageId
);
storage
.
setAssociateId
(
gatheringCodeId
);
storage
.
setAssociateBiz
(
"gatheringCode"
);
storageRepo
.
save
(
storage
);
}
@Transactional
public
void
disassociationGatheringCodeStorage
(
String
storageId
)
{
Storage
oldStorage
=
storageRepo
.
getOne
(
storageId
);
oldStorage
.
setAssociateId
(
null
);
oldStorage
.
setAssociateBiz
(
null
);
storageRepo
.
save
(
oldStorage
);
}
@Transactional
public
void
addOrUpdateGatheringCode
(
GatheringCodeParam
param
)
{
String
userAccountId
=
null
;
if
(
StrUtil
.
isBlank
(
param
.
getId
()))
{
if
(
StrUtil
.
isBlank
(
param
.
getUserName
()))
{
throw
new
BizException
(
BizError
.
参数异常
);
}
UserAccount
userAccount
=
userAccountRepo
.
findByUserName
(
param
.
getUserName
());
if
(
userAccount
==
null
)
{
throw
new
BizException
(
BizError
.
找不到所属账号无法新增收款码
);
}
userAccountId
=
userAccount
.
getId
();
}
addOrUpdateGatheringCode
(
param
,
userAccountId
);
}
@ParamValid
@Transactional
public
void
addOrUpdateGatheringCode
(
GatheringCodeParam
param
,
String
userAccountId
)
{
if
(
param
.
getFixedGatheringAmount
())
{
@Autowired
private
GatheringCodeRepo
gatheringCodeRepo
;
@Autowired
private
StorageRepo
storageRepo
;
@Autowired
private
UserAccountRepo
userAccountRepo
;
@Transactional
public
void
delMyGatheringCodeById
(
String
id
,
String
userAccountId
)
{
GatheringCode
gatheringCode
=
gatheringCodeRepo
.
getOne
(
id
);
if
(!
userAccountId
.
equals
(
gatheringCode
.
getUserAccountId
()))
{
throw
new
BizException
(
BizError
.
无权删除数据
);
}
delGatheringCodeById
(
id
);
}
@Transactional
public
void
delGatheringCodeById
(
String
id
)
{
GatheringCode
gatheringCode
=
gatheringCodeRepo
.
getOne
(
id
);
disassociationGatheringCodeStorage
(
gatheringCode
.
getStorageId
());
gatheringCodeRepo
.
delete
(
gatheringCode
);
}
@Transactional
(
readOnly
=
true
)
public
GatheringCodeVO
findMyGatheringCodeById
(
String
id
,
String
userAccountId
)
{
GatheringCodeVO
vo
=
findGatheringCodeById
(
id
);
if
(!
userAccountId
.
equals
(
vo
.
getUserAccountId
()))
{
throw
new
BizException
(
BizError
.
无权查看数据
);
}
return
vo
;
}
@Transactional
(
readOnly
=
true
)
public
GatheringCodeVO
findGatheringCodeById
(
String
id
)
{
GatheringCode
gatheringCode
=
gatheringCodeRepo
.
getOne
(
id
);
return
GatheringCodeVO
.
convertFor
(
gatheringCode
);
}
@Transactional
(
readOnly
=
true
)
public
PageResult
<
GatheringCodeVO
>
findMyGatheringCodeByPage
(
GatheringCodeQueryCondParam
param
)
{
if
(
StrUtil
.
isBlank
(
param
.
getUserAccountId
()))
{
throw
new
BizException
(
BizError
.
无权查看数据
);
}
return
findGatheringCodeByPage
(
param
);
}
@Transactional
(
readOnly
=
true
)
public
PageResult
<
GatheringCodeVO
>
findGatheringCodeByPage
(
GatheringCodeQueryCondParam
param
)
{
Specification
<
GatheringCode
>
spec
=
new
Specification
<
GatheringCode
>()
{
/**
*
*/
private
static
final
long
serialVersionUID
=
1L
;
public
Predicate
toPredicate
(
Root
<
GatheringCode
>
root
,
CriteriaQuery
<?>
query
,
CriteriaBuilder
builder
)
{
List
<
Predicate
>
predicates
=
new
ArrayList
<
Predicate
>();
if
(
StrUtil
.
isNotEmpty
(
param
.
getState
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"state"
),
param
.
getState
()));
}
if
(
StrUtil
.
isNotEmpty
(
param
.
getGatheringChannelCode
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"gatheringChannelCode"
),
param
.
getGatheringChannelCode
()));
}
if
(
StrUtil
.
isNotEmpty
(
param
.
getPayee
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"payee"
),
param
.
getPayee
()));
}
if
(
StrUtil
.
isNotEmpty
(
param
.
getUserName
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
join
(
"userAccount"
,
JoinType
.
INNER
).
get
(
"userName"
),
param
.
getUserName
()));
}
if
(
StrUtil
.
isNotEmpty
(
param
.
getUserAccountId
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"userAccountId"
),
param
.
getUserAccountId
()));
}
return
predicates
.
size
()
>
0
?
builder
.
and
(
predicates
.
toArray
(
new
Predicate
[
predicates
.
size
()]))
:
null
;
}
};
Page
<
GatheringCode
>
result
=
gatheringCodeRepo
.
findAll
(
spec
,
PageRequest
.
of
(
param
.
getPageNum
()
-
1
,
param
.
getPageSize
(),
Sort
.
by
(
Sort
.
Order
.
desc
(
"createTime"
))));
PageResult
<
GatheringCodeVO
>
pageResult
=
new
PageResult
<>(
GatheringCodeVO
.
convertFor
(
result
.
getContent
()),
param
.
getPageNum
(),
param
.
getPageSize
(),
result
.
getTotalElements
());
return
pageResult
;
}
@Transactional
public
void
associateGatheringCodeStorage
(
String
storageId
,
String
gatheringCodeId
)
{
Storage
storage
=
storageRepo
.
getOne
(
storageId
);
storage
.
setAssociateId
(
gatheringCodeId
);
storage
.
setAssociateBiz
(
"gatheringCode"
);
storageRepo
.
save
(
storage
);
}
@Transactional
public
void
disassociationGatheringCodeStorage
(
String
storageId
)
{
Storage
oldStorage
=
storageRepo
.
getOne
(
storageId
);
oldStorage
.
setAssociateId
(
null
);
oldStorage
.
setAssociateBiz
(
null
);
storageRepo
.
save
(
oldStorage
);
}
@Transactional
public
void
addOrUpdateGatheringCode
(
GatheringCodeParam
param
)
{
String
userAccountId
=
null
;
if
(
StrUtil
.
isBlank
(
param
.
getId
()))
{
if
(
StrUtil
.
isBlank
(
param
.
getUserName
()))
{
throw
new
BizException
(
BizError
.
参数异常
);
}
UserAccount
userAccount
=
userAccountRepo
.
findByUserName
(
param
.
getUserName
());
if
(
userAccount
==
null
)
{
throw
new
BizException
(
BizError
.
找不到所属账号无法新增收款码
);
}
userAccountId
=
userAccount
.
getId
();
}
addOrUpdateGatheringCode
(
param
,
userAccountId
);
}
@ParamValid
@Transactional
public
void
addOrUpdateGatheringCode
(
GatheringCodeParam
param
,
String
userAccountId
)
{
/*
if (param.getFixedGatheringAmount()) {
if (param.getGatheringAmount() == null) {
throw new BizException(BizError.参数异常);
}
if (param.getGatheringAmount() <= 0) {
throw new BizException(BizError.参数异常);
}
}
// 新增
if
(
StrUtil
.
isBlank
(
param
.
getId
()))
{
GatheringCode
gatheringCode
=
param
.
convertToPo
(
userAccountId
);
gatheringCodeRepo
.
save
(
gatheringCode
);
associateGatheringCodeStorage
(
param
.
getStorageId
(),
gatheringCode
.
getId
());
}
// 修改
else
{
GatheringCode
gatheringCode
=
gatheringCodeRepo
.
getOne
(
param
.
getId
());
// 取消关联旧的收款码图片
if
(!
param
.
getStorageId
().
equals
(
gatheringCode
.
getStorageId
()))
{
disassociationGatheringCodeStorage
(
gatheringCode
.
getStorageId
());
}
BeanUtils
.
copyProperties
(
param
,
gatheringCode
);
gatheringCodeRepo
.
save
(
gatheringCode
);
associateGatheringCodeStorage
(
param
.
getStorageId
(),
gatheringCode
.
getId
());
}
}
}*/
// 新增
if
(
StrUtil
.
isBlank
(
param
.
getId
()))
{
GatheringCode
gatheringCode
=
param
.
convertToPo
(
userAccountId
);
gatheringCodeRepo
.
save
(
gatheringCode
);
if
(
param
.
getGatheringChannelCode
().
equals
(
"bankcard"
))
{
return
;
}
associateGatheringCodeStorage
(
param
.
getStorageId
(),
gatheringCode
.
getId
());
}
// 修改
else
{
GatheringCode
gatheringCode
=
gatheringCodeRepo
.
getOne
(
param
.
getId
());
// 取消关联旧的收款码图片
if
(!
param
.
getStorageId
().
equals
(
gatheringCode
.
getStorageId
()))
{
disassociationGatheringCodeStorage
(
gatheringCode
.
getStorageId
());
}
BeanUtils
.
copyProperties
(
param
,
gatheringCode
);
gatheringCodeRepo
.
save
(
gatheringCode
);
if
(
param
.
getGatheringChannelCode
().
equals
(
"bankcard"
))
{
return
;
}
associateGatheringCodeStorage
(
param
.
getStorageId
(),
gatheringCode
.
getId
());
}
}
}
runscore-api/src/main/java/me/zohar/runscore/merchant/service/MerchantOrderService.java
浏览文件 @
d3b8a353
...
...
@@ -37,6 +37,7 @@ import cn.hutool.core.date.DateField;
import
cn.hutool.core.date.DatePattern
;
import
cn.hutool.core.date.DateUtil
;
import
cn.hutool.core.util.NumberUtil
;
import
cn.hutool.core.util.RandomUtil
;
import
cn.hutool.core.util.StrUtil
;
import
cn.hutool.crypto.digest.DigestAlgorithm
;
import
cn.hutool.crypto.digest.Digester
;
...
...
@@ -84,658 +85,681 @@ import me.zohar.runscore.useraccount.repo.UserAccountRepo;
@Service
public
class
MerchantOrderService
{
@Autowired
private
StringRedisTemplate
redisTemplate
;
@Autowired
private
MerchantOrderRepo
merchantOrderRepo
;
@Autowired
private
MerchantOrderPayInfoRepo
merchantOrderPayInfoRepo
;
@Autowired
private
MerchantRepo
merchantRepo
;
@Autowired
private
UserAccountRepo
userAccountRepo
;
@Autowired
private
GatheringCodeRepo
gatheringCodeRepo
;
@Autowired
private
AccountChangeLogRepo
accountChangeLogRepo
;
@Autowired
private
ReceiveOrderSettingRepo
platformOrderSettingRepo
;
@Autowired
private
OrderRebateRepo
orderRebateRepo
;
@Transactional
(
readOnly
=
true
)
public
MerchantOrderDetailsVO
findMerchantOrderDetailsById
(
@NotBlank
String
orderId
)
{
MerchantOrderDetailsVO
vo
=
MerchantOrderDetailsVO
.
convertFor
(
merchantOrderRepo
.
getOne
(
orderId
));
return
vo
;
}
/**
* 客服取消订单退款
*
* @param orderId
*/
@Transactional
public
void
customerServiceCancelOrderRefund
(
@NotBlank
String
orderId
)
{
MerchantOrder
merchantOrder
=
merchantOrderRepo
.
getOne
(
orderId
);
if
(!(
Constant
.
商户订单状态
_
申诉中
.
equals
(
merchantOrder
.
getOrderState
())))
{
throw
new
BizException
(
BizError
.
只有申诉中的商户订单才能取消订单退款
);
}
UserAccount
userAccount
=
merchantOrder
.
getReceivedAccount
();
Double
cashDeposit
=
NumberUtil
.
round
(
userAccount
.
getCashDeposit
()
+
merchantOrder
.
getGatheringAmount
(),
4
)
.
doubleValue
();
userAccount
.
setCashDeposit
(
cashDeposit
);
userAccountRepo
.
save
(
userAccount
);
merchantOrder
.
customerCancelOrderRefund
();
merchantOrderRepo
.
save
(
merchantOrder
);
accountChangeLogRepo
.
save
(
AccountChangeLog
.
buildWithCustomerCancelOrderRefund
(
userAccount
,
merchantOrder
));
}
// @Transactional
// public void merchantConfirmToPaid(@NotBlank String secretKey, @NotBlank
// String orderId) {
// Merchant merchant = merchantRepo.findByMerchantNum(secretKey);
// if (merchant == null) {
// throw new BizException(BizError.商户未接入);
// }
// MerchantOrder order = merchantOrderRepo.findById(orderId).orElse(null);
// if (order == null) {
// log.error("商家订单不存在;secretKey:{},orderId:{}", secretKey, orderId);
// throw new BizException(BizError.商户订单不存在);
// }
// if (!order.getMerchantId().equals(merchant.getId())) {
// log.error("无权更新商家订单状态为商户已确认支付;secretKey:{},orderId:{}", secretKey, orderId);
// throw new BizException(BizError.无权更新商户订单状态为商户已确认支付);
// }
// if (!Constant.商户订单状态_已接单.equals(order.getOrderState())) {
// throw new BizException(BizError.订单状态为已接单才能转为平台已确认支付);
// }
// order.merchantConfirmToPaid();
// merchantOrderRepo.save(order);
// }
@Transactional
(
readOnly
=
true
)
public
OrderGatheringCodeVO
getOrderGatheringCode
(
@NotBlank
String
orderNo
)
{
MerchantOrder
order
=
merchantOrderRepo
.
findByOrderNo
(
orderNo
);
if
(
order
==
null
)
{
log
.
error
(
"商户订单不存在;orderNo:{}"
,
orderNo
);
throw
new
BizException
(
BizError
.
商户订单不存在
);
}
String
gatheringCodeStorageId
=
getGatheringCodeStorageId
(
order
.
getReceivedAccountId
(),
order
.
getGatheringChannelCode
(),
order
.
getGatheringAmount
());
OrderGatheringCodeVO
vo
=
OrderGatheringCodeVO
.
convertFor
(
order
);
vo
.
setGatheringCodeStorageId
(
gatheringCodeStorageId
);
return
vo
;
}
@Transactional
(
readOnly
=
true
)
public
String
getGatheringCodeStorageId
(
String
receivedAccountId
,
String
gatheringChannelCode
,
Double
gatheringAmount
)
{
ReceiveOrderSetting
merchantOrderSetting
=
platformOrderSettingRepo
.
findTopByOrderByLatelyUpdateTime
();
if
(
merchantOrderSetting
.
getUnfixedGatheringCodeReceiveOrder
())
{
GatheringCode
gatheringCode
=
gatheringCodeRepo
.
findTopByUserAccountIdAndGatheringChannelCodeAndFixedGatheringAmountIsFalse
(
receivedAccountId
,
gatheringChannelCode
);
if
(
gatheringCode
!=
null
)
{
return
gatheringCode
.
getStorageId
();
}
}
else
{
GatheringCode
gatheringCode
=
gatheringCodeRepo
.
findTopByUserAccountIdAndGatheringChannelCodeAndGatheringAmount
(
receivedAccountId
,
gatheringChannelCode
,
gatheringAmount
);
if
(
gatheringCode
!=
null
)
{
return
gatheringCode
.
getStorageId
();
}
}
return
null
;
}
@Transactional
public
void
userConfirmToPaid
(
@NotBlank
String
userAccountId
,
@NotBlank
String
orderId
)
{
MerchantOrder
platformOrder
=
merchantOrderRepo
.
findByIdAndReceivedAccountId
(
orderId
,
userAccountId
);
if
(
platformOrder
==
null
)
{
throw
new
BizException
(
BizError
.
商户订单不存在
);
}
if
(!(
Constant
.
商户订单状态
_
已接单
.
equals
(
platformOrder
.
getOrderState
())
||
Constant
.
商户订单状态
_
商户已确认支付
.
equals
(
platformOrder
.
getOrderState
())))
{
throw
new
BizException
(
BizError
.
订单状态为已接单或平台已确认支付才能转为确认已支付
);
}
platformOrder
.
confirmToPaid
(
null
);
merchantOrderRepo
.
save
(
platformOrder
);
receiveOrderBountySettlement
(
platformOrder
);
}
/**
* 客服确认已支付
*
* @param orderId
*/
@Transactional
public
void
customerServiceConfirmToPaid
(
@NotBlank
String
orderId
,
String
note
)
{
MerchantOrder
platformOrder
=
merchantOrderRepo
.
findById
(
orderId
).
orElse
(
null
);
if
(
platformOrder
==
null
)
{
throw
new
BizException
(
BizError
.
商户订单不存在
);
}
if
(!
Constant
.
商户订单状态
_
申诉中
.
equals
(
platformOrder
.
getOrderState
()))
{
throw
new
BizException
(
BizError
.
订单状态为申述中才能转为确认已支付
);
}
platformOrder
.
confirmToPaid
(
note
);
merchantOrderRepo
.
save
(
platformOrder
);
receiveOrderBountySettlement
(
platformOrder
);
}
/**
* 接单奖励金结算
*/
@Transactional
public
void
receiveOrderBountySettlement
(
MerchantOrder
merchantOrder
)
{
UserAccount
userAccount
=
merchantOrder
.
getReceivedAccount
();
double
bounty
=
NumberUtil
.
round
(
merchantOrder
.
getGatheringAmount
()
*
userAccount
.
getRebate
()
*
0.01
,
4
)
.
doubleValue
();
merchantOrder
.
updateBounty
(
bounty
);
merchantOrderRepo
.
save
(
merchantOrder
);
userAccount
.
setCashDeposit
(
NumberUtil
.
round
(
userAccount
.
getCashDeposit
()
+
bounty
,
4
).
doubleValue
());
userAccountRepo
.
save
(
userAccount
);
accountChangeLogRepo
.
save
(
AccountChangeLog
.
buildWithReceiveOrderBounty
(
userAccount
,
bounty
,
userAccount
.
getRebate
()));
generateOrderRebate
(
merchantOrder
);
ThreadPoolUtils
.
getPaidMerchantOrderPool
().
schedule
(()
->
{
redisTemplate
.
opsForList
().
leftPush
(
Constant
.
商户订单
ID
,
merchantOrder
.
getId
());
},
1
,
TimeUnit
.
SECONDS
);
}
/**
* 生成订单返点
*
* @param bettingOrder
*/
public
void
generateOrderRebate
(
MerchantOrder
merchantOrder
)
{
UserAccount
userAccount
=
merchantOrder
.
getReceivedAccount
();
UserAccount
superior
=
merchantOrder
.
getReceivedAccount
().
getInviter
();
while
(
superior
!=
null
)
{
double
rebate
=
NumberUtil
.
round
(
superior
.
getRebate
()
-
userAccount
.
getRebate
(),
4
).
doubleValue
();
if
(
rebate
<
0
)
{
log
.
error
(
"订单返点异常,下级账号的返点不能大于上级账号;下级账号id:{},上级账号id:{}"
,
userAccount
.
getId
(),
superior
.
getId
());
break
;
}
double
rebateAmount
=
NumberUtil
.
round
(
merchantOrder
.
getGatheringAmount
()
*
rebate
*
0.01
,
4
).
doubleValue
();
OrderRebate
orderRebate
=
OrderRebate
.
build
(
rebate
,
rebateAmount
,
merchantOrder
.
getId
(),
superior
.
getId
());
orderRebateRepo
.
save
(
orderRebate
);
userAccount
=
superior
;
superior
=
superior
.
getInviter
();
}
}
@Transactional
(
readOnly
=
true
)
public
void
orderRebateAutoSettlement
()
{
List
<
OrderRebate
>
orderRebates
=
orderRebateRepo
.
findBySettlementTimeIsNull
();
for
(
OrderRebate
orderRebate
:
orderRebates
)
{
redisTemplate
.
opsForList
().
leftPush
(
Constant
.
订单返点
ID
,
orderRebate
.
getId
());
}
}
/**
* 通知指定的订单进行返点结算
*
* @param issueId
*/
@Transactional
(
readOnly
=
true
)
public
void
noticeOrderRebateSettlement
(
@NotBlank
String
orderId
)
{
List
<
OrderRebate
>
orderRebates
=
orderRebateRepo
.
findByMerchantOrderId
(
orderId
);
for
(
OrderRebate
orderRebate
:
orderRebates
)
{
redisTemplate
.
opsForList
().
leftPush
(
Constant
.
订单返点
ID
,
orderRebate
.
getId
());
}
}
/**
* 订单返点结算
*/
@Transactional
public
void
orderRebateSettlement
(
@NotBlank
String
orderRebateId
)
{
OrderRebate
orderRebate
=
orderRebateRepo
.
getOne
(
orderRebateId
);
if
(
orderRebate
.
getSettlementTime
()
!=
null
)
{
log
.
warn
(
"当前的订单返点记录已结算,无法重复结算;id:{}"
,
orderRebateId
);
return
;
}
orderRebate
.
settlement
();
orderRebateRepo
.
save
(
orderRebate
);
UserAccount
userAccount
=
orderRebate
.
getRebateAccount
();
double
cashDeposit
=
userAccount
.
getCashDeposit
()
+
orderRebate
.
getRebateAmount
();
userAccount
.
setCashDeposit
(
NumberUtil
.
round
(
cashDeposit
,
4
).
doubleValue
());
userAccountRepo
.
save
(
userAccount
);
accountChangeLogRepo
.
save
(
AccountChangeLog
.
buildWithOrderRebate
(
userAccount
,
orderRebate
));
}
@Transactional
(
readOnly
=
true
)
public
List
<
MyWaitConfirmOrderVO
>
findMyWaitConfirmOrder
(
@NotBlank
String
userAccountId
)
{
return
MyWaitConfirmOrderVO
.
convertFor
(
merchantOrderRepo
.
findByOrderStateInAndReceivedAccountIdOrderBySubmitTimeDesc
(
Arrays
.
asList
(
Constant
.
商户订单状态
_
已接单
,
Constant
.
商户订单状态
_
商户已确认支付
),
userAccountId
));
}
@Transactional
(
readOnly
=
true
)
public
List
<
MyWaitReceivingOrderVO
>
findMyWaitReceivingOrder
(
@NotBlank
String
userAccountId
)
{
UserAccount
userAccount
=
userAccountRepo
.
getOne
(
userAccountId
);
List
<
GatheringCode
>
gatheringCodes
=
gatheringCodeRepo
.
findByUserAccountId
(
userAccountId
);
if
(
CollectionUtil
.
isEmpty
(
gatheringCodes
))
{
throw
new
BizException
(
BizError
.
未设置收款码无法接单
);
}
ReceiveOrderSetting
merchantOrderSetting
=
platformOrderSettingRepo
.
findTopByOrderByLatelyUpdateTime
();
if
(
merchantOrderSetting
.
getUnfixedGatheringCodeReceiveOrder
())
{
Map
<
String
,
String
>
gatheringChannelCodeMap
=
new
HashMap
<>();
for
(
GatheringCode
gatheringCode
:
gatheringCodes
)
{
gatheringChannelCodeMap
.
put
(
gatheringCode
.
getGatheringChannelCode
(),
gatheringCode
.
getGatheringChannelCode
());
}
List
<
MerchantOrder
>
waitReceivingOrders
=
merchantOrderRepo
.
findTop10ByOrderStateAndGatheringAmountIsLessThanEqualAndGatheringChannelCodeInOrderBySubmitTimeDesc
(
Constant
.
商户订单状态
_
等待接单
,
userAccount
.
getCashDeposit
(),
new
ArrayList
<>(
gatheringChannelCodeMap
.
keySet
()));
return
MyWaitReceivingOrderVO
.
convertFor
(
waitReceivingOrders
);
}
Map
<
String
,
List
<
Double
>>
gatheringChannelCodeMap
=
new
HashMap
<>();
for
(
GatheringCode
gatheringCode
:
gatheringCodes
)
{
if
(
gatheringChannelCodeMap
.
get
(
gatheringCode
.
getGatheringChannelCode
())
==
null
)
{
gatheringChannelCodeMap
.
put
(
gatheringCode
.
getGatheringChannelCode
(),
new
ArrayList
<>());
}
if
(
userAccount
.
getCashDeposit
()
<
gatheringCode
.
getGatheringAmount
())
{
continue
;
}
gatheringChannelCodeMap
.
get
(
gatheringCode
.
getGatheringChannelCode
())
.
add
(
gatheringCode
.
getGatheringAmount
());
}
List
<
MerchantOrder
>
waitReceivingOrders
=
new
ArrayList
<>();
for
(
Entry
<
String
,
List
<
Double
>>
entry
:
gatheringChannelCodeMap
.
entrySet
())
{
if
(
CollectionUtil
.
isEmpty
(
entry
.
getValue
()))
{
continue
;
}
List
<
MerchantOrder
>
tmpOrders
=
merchantOrderRepo
.
findTop10ByOrderStateAndGatheringAmountInAndGatheringChannelCodeOrderBySubmitTimeDesc
(
Constant
.
商户订单状态
_
等待接单
,
entry
.
getValue
(),
entry
.
getKey
());
waitReceivingOrders
.
addAll
(
tmpOrders
);
}
Collections
.
sort
(
waitReceivingOrders
,
new
Comparator
<
MerchantOrder
>()
{
@Override
public
int
compare
(
MerchantOrder
o1
,
MerchantOrder
o2
)
{
return
o1
.
getSubmitTime
().
before
(
o2
.
getSubmitTime
())
?
1
:
-
1
;
}
});
if
(
waitReceivingOrders
.
isEmpty
())
{
return
MyWaitReceivingOrderVO
.
convertFor
(
waitReceivingOrders
);
}
return
MyWaitReceivingOrderVO
.
convertFor
(
waitReceivingOrders
.
subList
(
0
,
waitReceivingOrders
.
size
()
>=
10
?
10
:
waitReceivingOrders
.
size
()));
}
@ParamValid
@Transactional
public
MerchantOrderVO
manualStartOrder
(
ManualStartOrderParam
param
)
{
Merchant
merchant
=
merchantRepo
.
findByMerchantNum
(
param
.
getMerchantNum
());
if
(
merchant
==
null
)
{
throw
new
BizException
(
BizError
.
商户未接入
);
}
String
sign
=
param
.
getMerchantNum
()
+
param
.
getOrderNo
()
+
new
DecimalFormat
(
"###################.###########"
).
format
(
param
.
getGatheringAmount
())
+
param
.
getNotifyUrl
()
+
merchant
.
getSecretKey
();
sign
=
new
Digester
(
DigestAlgorithm
.
MD5
).
digestHex
(
sign
);
param
.
setSign
(
sign
);
Integer
orderEffectiveDuration
=
Constant
.
商户订单接单有效时长
;
ReceiveOrderSetting
setting
=
platformOrderSettingRepo
.
findTopByOrderByLatelyUpdateTime
();
if
(
setting
!=
null
)
{
orderEffectiveDuration
=
setting
.
getReceiveOrderEffectiveDuration
();
}
MerchantOrder
merchantOrder
=
param
.
convertToPo
(
merchant
.
getId
(),
orderEffectiveDuration
);
MerchantOrderPayInfo
payInfo
=
param
.
convertToPayInfoPo
(
merchantOrder
.
getId
());
merchantOrder
.
setPayInfoId
(
payInfo
.
getId
());
merchantOrderRepo
.
save
(
merchantOrder
);
merchantOrderPayInfoRepo
.
save
(
payInfo
);
return
MerchantOrderVO
.
convertFor
(
merchantOrder
);
}
/**
* 接单
*
* @param param
* @return
*/
@Lock
(
keys
=
"'receiveOrder_' + #orderId"
)
@Transactional
public
void
receiveOrder
(
@NotBlank
String
userAccountId
,
@NotBlank
String
orderId
)
{
List
<
GatheringCode
>
gatheringCodes
=
gatheringCodeRepo
.
findByUserAccountId
(
userAccountId
);
if
(
CollectionUtil
.
isEmpty
(
gatheringCodes
))
{
throw
new
BizException
(
BizError
.
未设置收款码无法接单
);
}
MerchantOrder
platformOrder
=
merchantOrderRepo
.
getOne
(
orderId
);
if
(
platformOrder
==
null
)
{
throw
new
BizException
(
BizError
.
商户订单不存在
);
}
if
(!
Constant
.
商户订单状态
_
等待接单
.
equals
(
platformOrder
.
getOrderState
()))
{
throw
new
BizException
(
BizError
.
订单已被接或已取消
);
}
String
gatheringCodeStorageId
=
getGatheringCodeStorageId
(
userAccountId
,
platformOrder
.
getGatheringChannelCode
(),
platformOrder
.
getGatheringAmount
());
if
(
StrUtil
.
isBlank
(
gatheringCodeStorageId
))
{
throw
new
BizException
(
BizError
.
无法接单找不到对应金额的收款码
);
}
// 校验用户是否达到接单上限,若达到上限,则不能接单
ReceiveOrderSetting
setting
=
platformOrderSettingRepo
.
findTopByOrderByLatelyUpdateTime
();
if
(
setting
!=
null
&&
setting
.
getReceiveOrderUpperLimit
()
!=
null
)
{
List
<
MyWaitConfirmOrderVO
>
waitConfirmOrders
=
findMyWaitConfirmOrder
(
userAccountId
);
if
(
waitConfirmOrders
.
size
()
>=
setting
.
getReceiveOrderUpperLimit
())
{
throw
new
BizException
(
BizError
.
已达到接单数量上限
);
}
}
UserAccount
userAccount
=
userAccountRepo
.
getOne
(
userAccountId
);
if
(
setting
!=
null
&&
setting
.
getCashDepositMinimumRequire
()
!=
null
)
{
if
(
userAccount
.
getCashDeposit
()
<
setting
.
getCashDepositMinimumRequire
())
{
throw
new
BizException
(
BizError
.
未达到接单保证金最低要求
);
}
}
Double
cashDeposit
=
NumberUtil
.
round
(
userAccount
.
getCashDeposit
()
-
platformOrder
.
getGatheringAmount
(),
4
)
.
doubleValue
();
if
(
cashDeposit
<
0
)
{
throw
new
BizException
(
BizError
.
保证金不足无法接单
);
}
userAccount
.
setCashDeposit
(
cashDeposit
);
userAccountRepo
.
save
(
userAccount
);
Integer
orderEffectiveDuration
=
Constant
.
商户订单支付有效时长
;
if
(
setting
!=
null
&&
setting
.
getOrderPayEffectiveDuration
()
!=
null
)
{
orderEffectiveDuration
=
setting
.
getOrderPayEffectiveDuration
();
}
platformOrder
.
updateReceived
(
userAccount
.
getId
(),
gatheringCodeStorageId
);
platformOrder
.
updateUsefulTime
(
DateUtil
.
offset
(
platformOrder
.
getReceivedTime
(),
DateField
.
MINUTE
,
orderEffectiveDuration
));
merchantOrderRepo
.
save
(
platformOrder
);
accountChangeLogRepo
.
save
(
AccountChangeLog
.
buildWithReceiveOrderDeduction
(
userAccount
,
platformOrder
));
}
@Transactional
(
readOnly
=
true
)
public
PageResult
<
MerchantOrderVO
>
findMerchantOrderByPage
(
MerchantOrderQueryCondParam
param
)
{
Specification
<
MerchantOrder
>
spec
=
new
Specification
<
MerchantOrder
>()
{
/**
*
*/
private
static
final
long
serialVersionUID
=
1L
;
public
Predicate
toPredicate
(
Root
<
MerchantOrder
>
root
,
CriteriaQuery
<?>
query
,
CriteriaBuilder
builder
)
{
List
<
Predicate
>
predicates
=
new
ArrayList
<
Predicate
>();
if
(
StrUtil
.
isNotBlank
(
param
.
getOrderNo
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"orderNo"
),
param
.
getOrderNo
()));
}
if
(
StrUtil
.
isNotBlank
(
param
.
getPlatformName
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
join
(
"merchant"
,
JoinType
.
INNER
).
get
(
"name"
),
param
.
getPlatformName
()));
}
if
(
StrUtil
.
isNotBlank
(
param
.
getGatheringChannelCode
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"gatheringChannelCode"
),
param
.
getGatheringChannelCode
()));
}
if
(
StrUtil
.
isNotBlank
(
param
.
getOrderState
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"orderState"
),
param
.
getOrderState
()));
}
if
(
StrUtil
.
isNotBlank
(
param
.
getReceiverUserName
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
join
(
"userAccount"
,
JoinType
.
INNER
).
get
(
"userName"
),
param
.
getReceiverUserName
()));
}
if
(
param
.
getSubmitStartTime
()
!=
null
)
{
predicates
.
add
(
builder
.
greaterThanOrEqualTo
(
root
.
get
(
"submitTime"
).
as
(
Date
.
class
),
DateUtil
.
beginOfDay
(
param
.
getSubmitStartTime
())));
}
if
(
param
.
getSubmitEndTime
()
!=
null
)
{
predicates
.
add
(
builder
.
lessThanOrEqualTo
(
root
.
get
(
"submitTime"
).
as
(
Date
.
class
),
DateUtil
.
endOfDay
(
param
.
getSubmitEndTime
())));
}
if
(
param
.
getReceiveOrderStartTime
()
!=
null
)
{
predicates
.
add
(
builder
.
greaterThanOrEqualTo
(
root
.
get
(
"receivedTime"
).
as
(
Date
.
class
),
DateUtil
.
beginOfDay
(
param
.
getReceiveOrderStartTime
())));
}
if
(
param
.
getReceiveOrderEndTime
()
!=
null
)
{
predicates
.
add
(
builder
.
lessThanOrEqualTo
(
root
.
get
(
"receivedTime"
).
as
(
Date
.
class
),
DateUtil
.
endOfDay
(
param
.
getReceiveOrderEndTime
())));
}
return
predicates
.
size
()
>
0
?
builder
.
and
(
predicates
.
toArray
(
new
Predicate
[
predicates
.
size
()]))
:
null
;
}
};
Page
<
MerchantOrder
>
result
=
merchantOrderRepo
.
findAll
(
spec
,
PageRequest
.
of
(
param
.
getPageNum
()
-
1
,
param
.
getPageSize
(),
Sort
.
by
(
Sort
.
Order
.
desc
(
"submitTime"
))));
PageResult
<
MerchantOrderVO
>
pageResult
=
new
PageResult
<>(
MerchantOrderVO
.
convertFor
(
result
.
getContent
()),
param
.
getPageNum
(),
param
.
getPageSize
(),
result
.
getTotalElements
());
return
pageResult
;
}
@Transactional
(
readOnly
=
true
)
public
PageResult
<
ReceiveOrderRecordVO
>
findMyReceiveOrderRecordByPage
(
MyReceiveOrderRecordQueryCondParam
param
)
{
Specification
<
MerchantOrder
>
spec
=
new
Specification
<
MerchantOrder
>()
{
/**
*
*/
private
static
final
long
serialVersionUID
=
1L
;
public
Predicate
toPredicate
(
Root
<
MerchantOrder
>
root
,
CriteriaQuery
<?>
query
,
CriteriaBuilder
builder
)
{
List
<
Predicate
>
predicates
=
new
ArrayList
<
Predicate
>();
if
(
StrUtil
.
isNotBlank
(
param
.
getGatheringChannelCode
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"gatheringChannelCode"
),
param
.
getGatheringChannelCode
()));
}
if
(
StrUtil
.
isNotBlank
(
param
.
getReceiverUserName
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
join
(
"receivedAccount"
,
JoinType
.
INNER
).
get
(
"userName"
),
param
.
getReceiverUserName
()));
}
if
(
param
.
getReceiveOrderTime
()
!=
null
)
{
predicates
.
add
(
builder
.
greaterThanOrEqualTo
(
root
.
get
(
"receivedTime"
).
as
(
Date
.
class
),
DateUtil
.
beginOfDay
(
param
.
getReceiveOrderTime
())));
predicates
.
add
(
builder
.
lessThanOrEqualTo
(
root
.
get
(
"receivedTime"
).
as
(
Date
.
class
),
DateUtil
.
endOfDay
(
param
.
getReceiveOrderTime
())));
}
return
predicates
.
size
()
>
0
?
builder
.
and
(
predicates
.
toArray
(
new
Predicate
[
predicates
.
size
()]))
:
null
;
}
};
Page
<
MerchantOrder
>
result
=
merchantOrderRepo
.
findAll
(
spec
,
PageRequest
.
of
(
param
.
getPageNum
()
-
1
,
param
.
getPageSize
(),
Sort
.
by
(
Sort
.
Order
.
desc
(
"submitTime"
))));
PageResult
<
ReceiveOrderRecordVO
>
pageResult
=
new
PageResult
<>(
ReceiveOrderRecordVO
.
convertFor
(
result
.
getContent
()),
param
.
getPageNum
(),
param
.
getPageSize
(),
result
.
getTotalElements
());
return
pageResult
;
}
/**
* 取消订单
*
* @param id
*/
@Transactional
public
void
cancelOrder
(
@NotBlank
String
id
)
{
MerchantOrder
platformOrder
=
merchantOrderRepo
.
getOne
(
id
);
if
(!
Constant
.
商户订单状态
_
等待接单
.
equals
(
platformOrder
.
getOrderState
()))
{
throw
new
BizException
(
BizError
.
只有等待接单状态的商户订单才能取消
);
}
platformOrder
.
setOrderState
(
Constant
.
商户订单状态
_
人工取消
);
platformOrder
.
setDealTime
(
new
Date
());
merchantOrderRepo
.
save
(
platformOrder
);
}
/**
* 商户取消订单
*
* @param id
*/
@Transactional
public
void
merchatCancelOrder
(
@NotBlank
String
merchantId
,
@NotBlank
String
id
)
{
MerchantOrder
platformOrder
=
merchantOrderRepo
.
getOne
(
id
);
if
(!
merchantId
.
equals
(
platformOrder
.
getMerchantId
()))
{
throw
new
BizException
(
BizError
.
无权取消订单
);
}
if
(!
Constant
.
商户订单状态
_
等待接单
.
equals
(
platformOrder
.
getOrderState
()))
{
throw
new
BizException
(
BizError
.
只有等待接单状态的商户订单才能取消
);
}
platformOrder
.
setOrderState
(
Constant
.
商户订单状态
_
商户取消订单
);
platformOrder
.
setDealTime
(
new
Date
());
merchantOrderRepo
.
save
(
platformOrder
);
}
@Transactional
public
void
orderTimeoutDeal
()
{
Date
now
=
new
Date
();
List
<
MerchantOrder
>
orders
=
merchantOrderRepo
.
findByOrderStateAndUsefulTimeLessThan
(
Constant
.
商户订单状态
_
等待接单
,
now
);
for
(
MerchantOrder
order
:
orders
)
{
order
.
setDealTime
(
now
);
order
.
setOrderState
(
Constant
.
商户订单状态
_
超时取消
);
merchantOrderRepo
.
save
(
order
);
}
}
@ParamValid
@Transactional
public
StartOrderSuccessVO
startOrder
(
StartOrderParam
param
)
{
Merchant
merchant
=
merchantRepo
.
findByMerchantNum
(
param
.
getMerchantNum
());
if
(
merchant
==
null
)
{
throw
new
BizException
(
BizError
.
商户未接入
);
}
boolean
unknownPayTypeFlag
=
true
;
List
<
DictItemVO
>
payTypes
=
DictHolder
.
findDictItem
(
"gatheringChannel"
);
for
(
DictItemVO
payType
:
payTypes
)
{
if
(
payType
.
getDictItemCode
().
equals
(
param
.
getPayType
()))
{
unknownPayTypeFlag
=
false
;
}
}
if
(
unknownPayTypeFlag
)
{
throw
new
BizException
(
BizError
.
不支持该支付类型
);
}
String
sign
=
param
.
getMerchantNum
()
+
param
.
getOrderNo
()
+
new
DecimalFormat
(
"###################.###########"
).
format
(
param
.
getAmount
())
+
param
.
getNotifyUrl
()
+
merchant
.
getSecretKey
();
sign
=
new
Digester
(
DigestAlgorithm
.
MD5
).
digestHex
(
sign
);
if
(!
sign
.
equals
(
param
.
getSign
()))
{
throw
new
BizException
(
BizError
.
签名不正确
);
}
Integer
orderEffectiveDuration
=
Constant
.
商户订单接单有效时长
;
ReceiveOrderSetting
setting
=
platformOrderSettingRepo
.
findTopByOrderByLatelyUpdateTime
();
if
(
setting
!=
null
)
{
orderEffectiveDuration
=
setting
.
getReceiveOrderEffectiveDuration
();
}
MerchantOrder
merchantOrder
=
param
.
convertToPo
(
merchant
.
getId
(),
orderEffectiveDuration
);
MerchantOrderPayInfo
payInfo
=
param
.
convertToPayInfoPo
(
merchantOrder
.
getId
());
merchantOrder
.
setPayInfoId
(
payInfo
.
getId
());
merchantOrderRepo
.
save
(
merchantOrder
);
merchantOrderPayInfoRepo
.
save
(
payInfo
);
return
StartOrderSuccessVO
.
convertFor
(
merchantOrder
.
getOrderNo
());
}
/**
* 支付成功异步通知
*
* @param merchantOrderId
*/
@Transactional
public
String
paySuccessAsynNotice
(
@NotBlank
String
merchantOrderId
)
{
MerchantOrderPayInfo
payInfo
=
merchantOrderPayInfoRepo
.
findByMerchantOrderId
(
merchantOrderId
);
if
(
Constant
.
商户订单支付通知状态
_
通知成功
.
equals
(
payInfo
.
getNoticeState
()))
{
log
.
warn
(
"商户订单支付已通知成功,无需重复通知;商户订单id为{}"
,
merchantOrderId
);
return
Constant
.
商户订单通知成功返回值
;
}
Merchant
merchant
=
merchantRepo
.
findByMerchantNum
(
payInfo
.
getMerchantNum
());
if
(
merchant
==
null
)
{
throw
new
BizException
(
BizError
.
商户未接入
);
}
String
sign
=
Constant
.
商户订单支付成功
+
payInfo
.
getMerchantNum
()
+
payInfo
.
getOrderNo
()
+
new
DecimalFormat
(
"###################.###########"
).
format
(
payInfo
.
getAmount
())
+
merchant
.
getSecretKey
();
sign
=
new
Digester
(
DigestAlgorithm
.
MD5
).
digestHex
(
sign
);
Map
<
String
,
Object
>
paramMap
=
new
HashMap
<>();
paramMap
.
put
(
"merchantNum"
,
payInfo
.
getMerchantNum
());
paramMap
.
put
(
"orderNo"
,
payInfo
.
getOrderNo
());
paramMap
.
put
(
"platformOrderNo"
,
payInfo
.
getMerchantOrder
().
getOrderNo
());
paramMap
.
put
(
"amount"
,
payInfo
.
getAmount
());
paramMap
.
put
(
"attch"
,
payInfo
.
getAttch
());
paramMap
.
put
(
"state"
,
Constant
.
商户订单支付成功
);
paramMap
.
put
(
"payTime"
,
DateUtil
.
format
(
payInfo
.
getMerchantOrder
().
getConfirmTime
(),
DatePattern
.
NORM_DATETIME_PATTERN
));
paramMap
.
put
(
"sign"
,
sign
);
String
result
=
"fail"
;
// 通知3次
for
(
int
i
=
0
;
i
<
3
;
i
++)
{
try
{
result
=
HttpUtil
.
get
(
payInfo
.
getNotifyUrl
(),
paramMap
,
2500
);
if
(
Constant
.
商户订单通知成功返回值
.
equals
(
result
))
{
break
;
}
}
catch
(
Exception
e
)
{
result
=
e
.
getMessage
();
log
.
error
(
MessageFormat
.
format
(
"商户订单支付成功异步通知地址请求异常,id为{0}"
,
merchantOrderId
),
e
);
}
}
payInfo
.
setNoticeState
(
Constant
.
商户订单通知成功返回值
.
equals
(
result
)
?
Constant
.
商户订单支付通知状态
_
通知成功
:
Constant
.
商户订单支付通知状态
_
通知失败
);
merchantOrderPayInfoRepo
.
save
(
payInfo
);
return
result
;
}
@Transactional
(
readOnly
=
true
)
public
PageResult
<
ReceiveOrderRecordVO
>
findLowerLevelAccountReceiveOrderRecordByPage
(
LowerLevelAccountReceiveOrderQueryCondParam
param
)
{
UserAccount
currentAccount
=
userAccountRepo
.
getOne
(
param
.
getCurrentAccountId
());
UserAccount
lowerLevelAccount
=
currentAccount
;
if
(
StrUtil
.
isNotBlank
(
param
.
getUserName
()))
{
lowerLevelAccount
=
userAccountRepo
.
findByUserName
(
param
.
getUserName
());
if
(
lowerLevelAccount
==
null
)
{
throw
new
BizException
(
BizError
.
用户名不存在
);
}
// 说明该用户名对应的账号不是当前账号的下级账号
if
(!
lowerLevelAccount
.
getAccountLevelPath
().
startsWith
(
currentAccount
.
getAccountLevelPath
()))
{
throw
new
BizException
(
BizError
.
不是上级账号无权查看该账号及下级的接单记录
);
}
}
String
lowerLevelAccountId
=
lowerLevelAccount
.
getId
();
String
lowerLevelAccountLevelPath
=
lowerLevelAccount
.
getAccountLevelPath
();
Specification
<
MerchantOrder
>
spec
=
new
Specification
<
MerchantOrder
>()
{
/**
*
*/
private
static
final
long
serialVersionUID
=
1L
;
public
Predicate
toPredicate
(
Root
<
MerchantOrder
>
root
,
CriteriaQuery
<?>
query
,
CriteriaBuilder
builder
)
{
List
<
Predicate
>
predicates
=
new
ArrayList
<
Predicate
>();
if
(
StrUtil
.
isNotBlank
(
param
.
getUserName
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"receivedAccountId"
),
lowerLevelAccountId
));
}
else
{
predicates
.
add
(
builder
.
like
(
root
.
join
(
"receivedAccount"
,
JoinType
.
INNER
).
get
(
"accountLevelPath"
),
lowerLevelAccountLevelPath
+
"%"
));
}
if
(
StrUtil
.
isNotBlank
(
param
.
getOrderState
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"orderState"
),
param
.
getOrderState
()));
}
if
(
StrUtil
.
isNotBlank
(
param
.
getGatheringChannelCode
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"gatheringChannelCode"
),
param
.
getGatheringChannelCode
()));
}
if
(
param
.
getStartTime
()
!=
null
)
{
predicates
.
add
(
builder
.
greaterThanOrEqualTo
(
root
.
get
(
"receivedTime"
).
as
(
Date
.
class
),
DateUtil
.
beginOfDay
(
param
.
getStartTime
())));
}
if
(
param
.
getEndTime
()
!=
null
)
{
predicates
.
add
(
builder
.
lessThanOrEqualTo
(
root
.
get
(
"receivedTime"
).
as
(
Date
.
class
),
DateUtil
.
endOfDay
(
param
.
getEndTime
())));
}
return
predicates
.
size
()
>
0
?
builder
.
and
(
predicates
.
toArray
(
new
Predicate
[
predicates
.
size
()]))
:
null
;
}
};
Page
<
MerchantOrder
>
result
=
merchantOrderRepo
.
findAll
(
spec
,
PageRequest
.
of
(
param
.
getPageNum
()
-
1
,
param
.
getPageSize
(),
Sort
.
by
(
Sort
.
Order
.
desc
(
"submitTime"
))));
PageResult
<
ReceiveOrderRecordVO
>
pageResult
=
new
PageResult
<>(
ReceiveOrderRecordVO
.
convertFor
(
result
.
getContent
()),
param
.
getPageNum
(),
param
.
getPageSize
(),
result
.
getTotalElements
());
return
pageResult
;
}
@Autowired
private
StringRedisTemplate
redisTemplate
;
@Autowired
private
MerchantOrderRepo
merchantOrderRepo
;
@Autowired
private
MerchantOrderPayInfoRepo
merchantOrderPayInfoRepo
;
@Autowired
private
MerchantRepo
merchantRepo
;
@Autowired
private
UserAccountRepo
userAccountRepo
;
@Autowired
private
GatheringCodeRepo
gatheringCodeRepo
;
@Autowired
private
AccountChangeLogRepo
accountChangeLogRepo
;
@Autowired
private
ReceiveOrderSettingRepo
platformOrderSettingRepo
;
@Autowired
private
OrderRebateRepo
orderRebateRepo
;
@Transactional
(
readOnly
=
true
)
public
MerchantOrderDetailsVO
findMerchantOrderDetailsById
(
@NotBlank
String
orderId
)
{
MerchantOrderDetailsVO
vo
=
MerchantOrderDetailsVO
.
convertFor
(
merchantOrderRepo
.
getOne
(
orderId
));
return
vo
;
}
/**
* 客服取消订单退款
*/
@Transactional
public
void
customerServiceCancelOrderRefund
(
@NotBlank
String
orderId
)
{
MerchantOrder
merchantOrder
=
merchantOrderRepo
.
getOne
(
orderId
);
if
(!(
Constant
.
商户订单状态
_
申诉中
.
equals
(
merchantOrder
.
getOrderState
())))
{
throw
new
BizException
(
BizError
.
只有申诉中的商户订单才能取消订单退款
);
}
UserAccount
userAccount
=
merchantOrder
.
getReceivedAccount
();
Double
cashDeposit
=
NumberUtil
.
round
(
userAccount
.
getCashDeposit
()
+
merchantOrder
.
getGatheringAmount
(),
4
)
.
doubleValue
();
userAccount
.
setCashDeposit
(
cashDeposit
);
userAccountRepo
.
save
(
userAccount
);
merchantOrder
.
customerCancelOrderRefund
();
merchantOrderRepo
.
save
(
merchantOrder
);
accountChangeLogRepo
.
save
(
AccountChangeLog
.
buildWithCustomerCancelOrderRefund
(
userAccount
,
merchantOrder
));
}
// @Transactional
// public void merchantConfirmToPaid(@NotBlank String secretKey, @NotBlank
// String orderId) {
// Merchant merchant = merchantRepo.findByMerchantNum(secretKey);
// if (merchant == null) {
// throw new BizException(BizError.商户未接入);
// }
// MerchantOrder order = merchantOrderRepo.findById(orderId).orElse(null);
// if (order == null) {
// log.error("商家订单不存在;secretKey:{},orderId:{}", secretKey, orderId);
// throw new BizException(BizError.商户订单不存在);
// }
// if (!order.getMerchantId().equals(merchant.getId())) {
// log.error("无权更新商家订单状态为商户已确认支付;secretKey:{},orderId:{}", secretKey, orderId);
// throw new BizException(BizError.无权更新商户订单状态为商户已确认支付);
// }
// if (!Constant.商户订单状态_已接单.equals(order.getOrderState())) {
// throw new BizException(BizError.订单状态为已接单才能转为平台已确认支付);
// }
// order.merchantConfirmToPaid();
// merchantOrderRepo.save(order);
// }
@Transactional
(
readOnly
=
true
)
public
OrderGatheringCodeVO
getOrderGatheringCode
(
@NotBlank
String
orderNo
)
{
MerchantOrder
order
=
merchantOrderRepo
.
findByOrderNo
(
orderNo
);
if
(
order
==
null
)
{
log
.
error
(
"商户订单不存在;orderNo:{}"
,
orderNo
);
throw
new
BizException
(
BizError
.
商户订单不存在
);
}
GatheringCode
gatheringCode
=
getGatheringCode
(
order
.
getReceivedAccountId
(),
order
.
getGatheringChannelCode
(),
order
.
getGatheringAmount
());
OrderGatheringCodeVO
vo
=
OrderGatheringCodeVO
.
convertFor
(
order
);
vo
.
setGatheringCodeStorageId
(
gatheringCode
.
getStorageId
());
vo
.
setGatheringCode
(
gatheringCode
);
return
vo
;
}
@Transactional
(
readOnly
=
true
)
public
String
getGatheringCodeStorageId
(
String
receivedAccountId
,
String
gatheringChannelCode
,
Double
gatheringAmount
)
{
ReceiveOrderSetting
merchantOrderSetting
=
platformOrderSettingRepo
.
findTopByOrderByLatelyUpdateTime
();
if
(
merchantOrderSetting
.
getUnfixedGatheringCodeReceiveOrder
())
{
GatheringCode
gatheringCode
=
gatheringCodeRepo
.
findTopByUserAccountIdAndGatheringChannelCodeAndFixedGatheringAmountIsFalse
(
receivedAccountId
,
gatheringChannelCode
);
if
(
gatheringCode
!=
null
)
{
return
gatheringCode
.
getStorageId
();
}
}
else
{
GatheringCode
gatheringCode
=
gatheringCodeRepo
.
findTopByUserAccountIdAndGatheringChannelCodeAndGatheringAmount
(
receivedAccountId
,
gatheringChannelCode
,
gatheringAmount
);
if
(
gatheringCode
!=
null
)
{
return
gatheringCode
.
getStorageId
();
}
}
return
null
;
}
@Transactional
(
readOnly
=
true
)
public
GatheringCode
getGatheringCode
(
String
receivedAccountId
,
String
gatheringChannelCode
,
Double
gatheringAmount
)
{
ReceiveOrderSetting
merchantOrderSetting
=
platformOrderSettingRepo
.
findTopByOrderByLatelyUpdateTime
();
if
(
merchantOrderSetting
.
getUnfixedGatheringCodeReceiveOrder
())
{
GatheringCode
gatheringCode
=
gatheringCodeRepo
.
findTopByUserAccountIdAndGatheringChannelCodeAndFixedGatheringAmountIsFalse
(
receivedAccountId
,
gatheringChannelCode
);
if
(
gatheringCode
!=
null
)
{
return
gatheringCode
;
}
}
else
{
GatheringCode
gatheringCode
=
gatheringCodeRepo
.
findTopByUserAccountIdAndGatheringChannelCodeAndGatheringAmount
(
receivedAccountId
,
gatheringChannelCode
,
gatheringAmount
);
if
(
gatheringCode
!=
null
)
{
return
gatheringCode
;
}
}
return
null
;
}
@Transactional
public
void
userConfirmToPaid
(
@NotBlank
String
userAccountId
,
@NotBlank
String
orderId
)
{
MerchantOrder
platformOrder
=
merchantOrderRepo
.
findByIdAndReceivedAccountId
(
orderId
,
userAccountId
);
if
(
platformOrder
==
null
)
{
throw
new
BizException
(
BizError
.
商户订单不存在
);
}
if
(!(
Constant
.
商户订单状态
_
已接单
.
equals
(
platformOrder
.
getOrderState
())
||
Constant
.
商户订单状态
_
商户已确认支付
.
equals
(
platformOrder
.
getOrderState
())))
{
throw
new
BizException
(
BizError
.
订单状态为已接单或平台已确认支付才能转为确认已支付
);
}
platformOrder
.
confirmToPaid
(
null
);
merchantOrderRepo
.
save
(
platformOrder
);
receiveOrderBountySettlement
(
platformOrder
);
}
/**
* 客服确认已支付
*/
@Transactional
public
void
customerServiceConfirmToPaid
(
@NotBlank
String
orderId
,
String
note
)
{
MerchantOrder
platformOrder
=
merchantOrderRepo
.
findById
(
orderId
).
orElse
(
null
);
if
(
platformOrder
==
null
)
{
throw
new
BizException
(
BizError
.
商户订单不存在
);
}
if
(!
Constant
.
商户订单状态
_
申诉中
.
equals
(
platformOrder
.
getOrderState
()))
{
throw
new
BizException
(
BizError
.
订单状态为申述中才能转为确认已支付
);
}
platformOrder
.
confirmToPaid
(
note
);
merchantOrderRepo
.
save
(
platformOrder
);
receiveOrderBountySettlement
(
platformOrder
);
}
/**
* 接单奖励金结算
*/
@Transactional
public
void
receiveOrderBountySettlement
(
MerchantOrder
merchantOrder
)
{
UserAccount
userAccount
=
merchantOrder
.
getReceivedAccount
();
double
bounty
=
NumberUtil
.
round
(
merchantOrder
.
getGatheringAmount
()
*
userAccount
.
getRebate
()
*
0.01
,
4
)
.
doubleValue
();
merchantOrder
.
updateBounty
(
bounty
);
merchantOrderRepo
.
save
(
merchantOrder
);
userAccount
.
setCashDeposit
(
NumberUtil
.
round
(
userAccount
.
getCashDeposit
()
+
bounty
,
4
).
doubleValue
());
userAccountRepo
.
save
(
userAccount
);
accountChangeLogRepo
.
save
(
AccountChangeLog
.
buildWithReceiveOrderBounty
(
userAccount
,
bounty
,
userAccount
.
getRebate
()));
generateOrderRebate
(
merchantOrder
);
ThreadPoolUtils
.
getPaidMerchantOrderPool
().
schedule
(()
->
{
redisTemplate
.
opsForList
().
leftPush
(
Constant
.
商户订单
ID
,
merchantOrder
.
getId
());
},
1
,
TimeUnit
.
SECONDS
);
}
/**
* 生成订单返点
*/
public
void
generateOrderRebate
(
MerchantOrder
merchantOrder
)
{
UserAccount
userAccount
=
merchantOrder
.
getReceivedAccount
();
UserAccount
superior
=
merchantOrder
.
getReceivedAccount
().
getInviter
();
while
(
superior
!=
null
)
{
double
rebate
=
NumberUtil
.
round
(
superior
.
getRebate
()
-
userAccount
.
getRebate
(),
4
).
doubleValue
();
if
(
rebate
<
0
)
{
log
.
error
(
"订单返点异常,下级账号的返点不能大于上级账号;下级账号id:{},上级账号id:{}"
,
userAccount
.
getId
(),
superior
.
getId
());
break
;
}
double
rebateAmount
=
NumberUtil
.
round
(
merchantOrder
.
getGatheringAmount
()
*
rebate
*
0.01
,
4
).
doubleValue
();
OrderRebate
orderRebate
=
OrderRebate
.
build
(
rebate
,
rebateAmount
,
merchantOrder
.
getId
(),
superior
.
getId
());
orderRebateRepo
.
save
(
orderRebate
);
userAccount
=
superior
;
superior
=
superior
.
getInviter
();
}
}
@Transactional
(
readOnly
=
true
)
public
void
orderRebateAutoSettlement
()
{
List
<
OrderRebate
>
orderRebates
=
orderRebateRepo
.
findBySettlementTimeIsNull
();
for
(
OrderRebate
orderRebate
:
orderRebates
)
{
redisTemplate
.
opsForList
().
leftPush
(
Constant
.
订单返点
ID
,
orderRebate
.
getId
());
}
}
/**
* 通知指定的订单进行返点结算
*/
@Transactional
(
readOnly
=
true
)
public
void
noticeOrderRebateSettlement
(
@NotBlank
String
orderId
)
{
List
<
OrderRebate
>
orderRebates
=
orderRebateRepo
.
findByMerchantOrderId
(
orderId
);
for
(
OrderRebate
orderRebate
:
orderRebates
)
{
redisTemplate
.
opsForList
().
leftPush
(
Constant
.
订单返点
ID
,
orderRebate
.
getId
());
}
}
/**
* 订单返点结算
*/
@Transactional
public
void
orderRebateSettlement
(
@NotBlank
String
orderRebateId
)
{
OrderRebate
orderRebate
=
orderRebateRepo
.
getOne
(
orderRebateId
);
if
(
orderRebate
.
getSettlementTime
()
!=
null
)
{
log
.
warn
(
"当前的订单返点记录已结算,无法重复结算;id:{}"
,
orderRebateId
);
return
;
}
orderRebate
.
settlement
();
orderRebateRepo
.
save
(
orderRebate
);
UserAccount
userAccount
=
orderRebate
.
getRebateAccount
();
double
cashDeposit
=
userAccount
.
getCashDeposit
()
+
orderRebate
.
getRebateAmount
();
userAccount
.
setCashDeposit
(
NumberUtil
.
round
(
cashDeposit
,
4
).
doubleValue
());
userAccountRepo
.
save
(
userAccount
);
accountChangeLogRepo
.
save
(
AccountChangeLog
.
buildWithOrderRebate
(
userAccount
,
orderRebate
));
}
@Transactional
(
readOnly
=
true
)
public
List
<
MyWaitConfirmOrderVO
>
findMyWaitConfirmOrder
(
@NotBlank
String
userAccountId
)
{
return
MyWaitConfirmOrderVO
.
convertFor
(
merchantOrderRepo
.
findByOrderStateInAndReceivedAccountIdOrderBySubmitTimeDesc
(
Arrays
.
asList
(
Constant
.
商户订单状态
_
已接单
,
Constant
.
商户订单状态
_
商户已确认支付
),
userAccountId
));
}
@Transactional
(
readOnly
=
true
)
public
List
<
MyWaitReceivingOrderVO
>
findMyWaitReceivingOrder
(
@NotBlank
String
userAccountId
)
{
UserAccount
userAccount
=
userAccountRepo
.
getOne
(
userAccountId
);
List
<
GatheringCode
>
gatheringCodes
=
gatheringCodeRepo
.
findByUserAccountId
(
userAccountId
);
if
(
CollectionUtil
.
isEmpty
(
gatheringCodes
))
{
throw
new
BizException
(
BizError
.
未设置收款码无法接单
);
}
ReceiveOrderSetting
merchantOrderSetting
=
platformOrderSettingRepo
.
findTopByOrderByLatelyUpdateTime
();
if
(
merchantOrderSetting
.
getUnfixedGatheringCodeReceiveOrder
())
{
Map
<
String
,
String
>
gatheringChannelCodeMap
=
new
HashMap
<>();
for
(
GatheringCode
gatheringCode
:
gatheringCodes
)
{
gatheringChannelCodeMap
.
put
(
gatheringCode
.
getGatheringChannelCode
(),
gatheringCode
.
getGatheringChannelCode
());
}
List
<
MerchantOrder
>
waitReceivingOrders
=
merchantOrderRepo
.
findTop10ByOrderStateAndGatheringAmountIsLessThanEqualAndGatheringChannelCodeInOrderBySubmitTimeDesc
(
Constant
.
商户订单状态
_
等待接单
,
userAccount
.
getCashDeposit
(),
new
ArrayList
<>(
gatheringChannelCodeMap
.
keySet
()));
return
MyWaitReceivingOrderVO
.
convertFor
(
waitReceivingOrders
);
}
Map
<
String
,
List
<
Double
>>
gatheringChannelCodeMap
=
new
HashMap
<>();
for
(
GatheringCode
gatheringCode
:
gatheringCodes
)
{
if
(
gatheringChannelCodeMap
.
get
(
gatheringCode
.
getGatheringChannelCode
())
==
null
)
{
gatheringChannelCodeMap
.
put
(
gatheringCode
.
getGatheringChannelCode
(),
new
ArrayList
<>());
}
if
(
userAccount
.
getCashDeposit
()
<
gatheringCode
.
getGatheringAmount
())
{
continue
;
}
gatheringChannelCodeMap
.
get
(
gatheringCode
.
getGatheringChannelCode
())
.
add
(
gatheringCode
.
getGatheringAmount
());
}
List
<
MerchantOrder
>
waitReceivingOrders
=
new
ArrayList
<>();
for
(
Entry
<
String
,
List
<
Double
>>
entry
:
gatheringChannelCodeMap
.
entrySet
())
{
if
(
CollectionUtil
.
isEmpty
(
entry
.
getValue
()))
{
continue
;
}
List
<
MerchantOrder
>
tmpOrders
=
merchantOrderRepo
.
findTop10ByOrderStateAndGatheringAmountInAndGatheringChannelCodeOrderBySubmitTimeDesc
(
Constant
.
商户订单状态
_
等待接单
,
entry
.
getValue
(),
entry
.
getKey
());
waitReceivingOrders
.
addAll
(
tmpOrders
);
}
Collections
.
sort
(
waitReceivingOrders
,
new
Comparator
<
MerchantOrder
>()
{
@Override
public
int
compare
(
MerchantOrder
o1
,
MerchantOrder
o2
)
{
return
o1
.
getSubmitTime
().
before
(
o2
.
getSubmitTime
())
?
1
:
-
1
;
}
});
if
(
waitReceivingOrders
.
isEmpty
())
{
return
MyWaitReceivingOrderVO
.
convertFor
(
waitReceivingOrders
);
}
return
MyWaitReceivingOrderVO
.
convertFor
(
waitReceivingOrders
.
subList
(
0
,
waitReceivingOrders
.
size
()
>=
10
?
10
:
waitReceivingOrders
.
size
()));
}
@ParamValid
@Transactional
public
MerchantOrderVO
manualStartOrder
(
ManualStartOrderParam
param
)
{
Merchant
merchant
=
merchantRepo
.
findByMerchantNum
(
param
.
getMerchantNum
());
if
(
merchant
==
null
)
{
throw
new
BizException
(
BizError
.
商户未接入
);
}
String
sign
=
param
.
getMerchantNum
()
+
param
.
getOrderNo
()
+
new
DecimalFormat
(
"###################.###########"
).
format
(
param
.
getGatheringAmount
())
+
param
.
getNotifyUrl
()
+
merchant
.
getSecretKey
();
sign
=
new
Digester
(
DigestAlgorithm
.
MD5
).
digestHex
(
sign
);
param
.
setSign
(
sign
);
Integer
orderEffectiveDuration
=
Constant
.
商户订单接单有效时长
;
ReceiveOrderSetting
setting
=
platformOrderSettingRepo
.
findTopByOrderByLatelyUpdateTime
();
if
(
setting
!=
null
)
{
orderEffectiveDuration
=
setting
.
getReceiveOrderEffectiveDuration
();
}
MerchantOrder
merchantOrder
=
param
.
convertToPo
(
merchant
.
getId
(),
orderEffectiveDuration
);
MerchantOrderPayInfo
payInfo
=
param
.
convertToPayInfoPo
(
merchantOrder
.
getId
());
merchantOrder
.
setPayInfoId
(
payInfo
.
getId
());
merchantOrderRepo
.
save
(
merchantOrder
);
merchantOrderPayInfoRepo
.
save
(
payInfo
);
//todo this is temporary code 这里要匹配有资源的用户接单
List
<
UserAccount
>
userAccounts
=
userAccountRepo
.
findAllByAccountType
(
Constant
.
账号类型
_
会员
);
;
this
.
receiveOrder
(
userAccounts
.
get
(
RandomUtil
.
randomInt
(
0
,
userAccounts
.
size
())).
getId
(),
merchantOrder
.
getId
());
return
MerchantOrderVO
.
convertFor
(
merchantOrder
);
}
/**
* 接单
*/
@Lock
(
keys
=
"'receiveOrder_' + #orderId"
)
@Transactional
public
void
receiveOrder
(
@NotBlank
String
userAccountId
,
@NotBlank
String
orderId
)
{
List
<
GatheringCode
>
gatheringCodes
=
gatheringCodeRepo
.
findByUserAccountId
(
userAccountId
);
if
(
CollectionUtil
.
isEmpty
(
gatheringCodes
))
{
throw
new
BizException
(
BizError
.
未设置收款码无法接单
);
}
MerchantOrder
platformOrder
=
merchantOrderRepo
.
getOne
(
orderId
);
if
(
platformOrder
==
null
)
{
throw
new
BizException
(
BizError
.
商户订单不存在
);
}
if
(!
Constant
.
商户订单状态
_
等待接单
.
equals
(
platformOrder
.
getOrderState
()))
{
throw
new
BizException
(
BizError
.
订单已被接或已取消
);
}
String
gatheringCodeStorageId
=
getGatheringCodeStorageId
(
userAccountId
,
platformOrder
.
getGatheringChannelCode
(),
platformOrder
.
getGatheringAmount
());
//支付宝和微信才有收款码
if
(
StrUtil
.
isBlank
(
gatheringCodeStorageId
)
&&
!
platformOrder
.
getGatheringChannelCode
().
equals
(
Constant
.
支付渠道
_
银行卡
)
)
{
throw
new
BizException
(
BizError
.
无法接单找不到对应金额的收款码
);
}
// 校验用户是否达到接单上限,若达到上限,则不能接单
ReceiveOrderSetting
setting
=
platformOrderSettingRepo
.
findTopByOrderByLatelyUpdateTime
();
if
(
setting
!=
null
&&
setting
.
getReceiveOrderUpperLimit
()
!=
null
)
{
List
<
MyWaitConfirmOrderVO
>
waitConfirmOrders
=
findMyWaitConfirmOrder
(
userAccountId
);
if
(
waitConfirmOrders
.
size
()
>=
setting
.
getReceiveOrderUpperLimit
())
{
throw
new
BizException
(
BizError
.
已达到接单数量上限
);
}
}
UserAccount
userAccount
=
userAccountRepo
.
getOne
(
userAccountId
);
if
(
setting
!=
null
&&
setting
.
getCashDepositMinimumRequire
()
!=
null
)
{
if
(
userAccount
.
getCashDeposit
()
<
setting
.
getCashDepositMinimumRequire
())
{
throw
new
BizException
(
BizError
.
未达到接单保证金最低要求
);
}
}
Double
cashDeposit
=
NumberUtil
.
round
(
userAccount
.
getCashDeposit
()
-
platformOrder
.
getGatheringAmount
(),
4
)
.
doubleValue
();
if
(
cashDeposit
<
0
)
{
throw
new
BizException
(
BizError
.
保证金不足无法接单
);
}
userAccount
.
setCashDeposit
(
cashDeposit
);
userAccountRepo
.
save
(
userAccount
);
Integer
orderEffectiveDuration
=
Constant
.
商户订单支付有效时长
;
if
(
setting
!=
null
&&
setting
.
getOrderPayEffectiveDuration
()
!=
null
)
{
orderEffectiveDuration
=
setting
.
getOrderPayEffectiveDuration
();
}
platformOrder
.
updateReceived
(
userAccount
.
getId
(),
gatheringCodeStorageId
);
platformOrder
.
updateUsefulTime
(
DateUtil
.
offset
(
platformOrder
.
getReceivedTime
(),
DateField
.
MINUTE
,
orderEffectiveDuration
));
merchantOrderRepo
.
save
(
platformOrder
);
accountChangeLogRepo
.
save
(
AccountChangeLog
.
buildWithReceiveOrderDeduction
(
userAccount
,
platformOrder
));
}
@Transactional
(
readOnly
=
true
)
public
PageResult
<
MerchantOrderVO
>
findMerchantOrderByPage
(
MerchantOrderQueryCondParam
param
)
{
Specification
<
MerchantOrder
>
spec
=
new
Specification
<
MerchantOrder
>()
{
/**
*
*/
private
static
final
long
serialVersionUID
=
1L
;
public
Predicate
toPredicate
(
Root
<
MerchantOrder
>
root
,
CriteriaQuery
<?>
query
,
CriteriaBuilder
builder
)
{
List
<
Predicate
>
predicates
=
new
ArrayList
<
Predicate
>();
if
(
StrUtil
.
isNotBlank
(
param
.
getOrderNo
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"orderNo"
),
param
.
getOrderNo
()));
}
if
(
StrUtil
.
isNotBlank
(
param
.
getPlatformName
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
join
(
"merchant"
,
JoinType
.
INNER
).
get
(
"name"
),
param
.
getPlatformName
()));
}
if
(
StrUtil
.
isNotBlank
(
param
.
getGatheringChannelCode
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"gatheringChannelCode"
),
param
.
getGatheringChannelCode
()));
}
if
(
StrUtil
.
isNotBlank
(
param
.
getOrderState
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"orderState"
),
param
.
getOrderState
()));
}
if
(
StrUtil
.
isNotBlank
(
param
.
getReceiverUserName
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
join
(
"userAccount"
,
JoinType
.
INNER
).
get
(
"userName"
),
param
.
getReceiverUserName
()));
}
if
(
param
.
getSubmitStartTime
()
!=
null
)
{
predicates
.
add
(
builder
.
greaterThanOrEqualTo
(
root
.
get
(
"submitTime"
).
as
(
Date
.
class
),
DateUtil
.
beginOfDay
(
param
.
getSubmitStartTime
())));
}
if
(
param
.
getSubmitEndTime
()
!=
null
)
{
predicates
.
add
(
builder
.
lessThanOrEqualTo
(
root
.
get
(
"submitTime"
).
as
(
Date
.
class
),
DateUtil
.
endOfDay
(
param
.
getSubmitEndTime
())));
}
if
(
param
.
getReceiveOrderStartTime
()
!=
null
)
{
predicates
.
add
(
builder
.
greaterThanOrEqualTo
(
root
.
get
(
"receivedTime"
).
as
(
Date
.
class
),
DateUtil
.
beginOfDay
(
param
.
getReceiveOrderStartTime
())));
}
if
(
param
.
getReceiveOrderEndTime
()
!=
null
)
{
predicates
.
add
(
builder
.
lessThanOrEqualTo
(
root
.
get
(
"receivedTime"
).
as
(
Date
.
class
),
DateUtil
.
endOfDay
(
param
.
getReceiveOrderEndTime
())));
}
return
predicates
.
size
()
>
0
?
builder
.
and
(
predicates
.
toArray
(
new
Predicate
[
predicates
.
size
()]))
:
null
;
}
};
Page
<
MerchantOrder
>
result
=
merchantOrderRepo
.
findAll
(
spec
,
PageRequest
.
of
(
param
.
getPageNum
()
-
1
,
param
.
getPageSize
(),
Sort
.
by
(
Sort
.
Order
.
desc
(
"submitTime"
))));
PageResult
<
MerchantOrderVO
>
pageResult
=
new
PageResult
<>(
MerchantOrderVO
.
convertFor
(
result
.
getContent
()),
param
.
getPageNum
(),
param
.
getPageSize
(),
result
.
getTotalElements
());
return
pageResult
;
}
@Transactional
(
readOnly
=
true
)
public
PageResult
<
ReceiveOrderRecordVO
>
findMyReceiveOrderRecordByPage
(
MyReceiveOrderRecordQueryCondParam
param
)
{
Specification
<
MerchantOrder
>
spec
=
new
Specification
<
MerchantOrder
>()
{
/**
*
*/
private
static
final
long
serialVersionUID
=
1L
;
public
Predicate
toPredicate
(
Root
<
MerchantOrder
>
root
,
CriteriaQuery
<?>
query
,
CriteriaBuilder
builder
)
{
List
<
Predicate
>
predicates
=
new
ArrayList
<
Predicate
>();
if
(
StrUtil
.
isNotBlank
(
param
.
getGatheringChannelCode
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"gatheringChannelCode"
),
param
.
getGatheringChannelCode
()));
}
if
(
StrUtil
.
isNotBlank
(
param
.
getReceiverUserName
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
join
(
"receivedAccount"
,
JoinType
.
INNER
).
get
(
"userName"
),
param
.
getReceiverUserName
()));
}
if
(
param
.
getReceiveOrderTime
()
!=
null
)
{
predicates
.
add
(
builder
.
greaterThanOrEqualTo
(
root
.
get
(
"receivedTime"
).
as
(
Date
.
class
),
DateUtil
.
beginOfDay
(
param
.
getReceiveOrderTime
())));
predicates
.
add
(
builder
.
lessThanOrEqualTo
(
root
.
get
(
"receivedTime"
).
as
(
Date
.
class
),
DateUtil
.
endOfDay
(
param
.
getReceiveOrderTime
())));
}
return
predicates
.
size
()
>
0
?
builder
.
and
(
predicates
.
toArray
(
new
Predicate
[
predicates
.
size
()]))
:
null
;
}
};
Page
<
MerchantOrder
>
result
=
merchantOrderRepo
.
findAll
(
spec
,
PageRequest
.
of
(
param
.
getPageNum
()
-
1
,
param
.
getPageSize
(),
Sort
.
by
(
Sort
.
Order
.
desc
(
"submitTime"
))));
PageResult
<
ReceiveOrderRecordVO
>
pageResult
=
new
PageResult
<>(
ReceiveOrderRecordVO
.
convertFor
(
result
.
getContent
()),
param
.
getPageNum
(),
param
.
getPageSize
(),
result
.
getTotalElements
());
return
pageResult
;
}
/**
* 取消订单
*/
@Transactional
public
void
cancelOrder
(
@NotBlank
String
id
)
{
MerchantOrder
platformOrder
=
merchantOrderRepo
.
getOne
(
id
);
if
(!
Constant
.
商户订单状态
_
等待接单
.
equals
(
platformOrder
.
getOrderState
()))
{
throw
new
BizException
(
BizError
.
只有等待接单状态的商户订单才能取消
);
}
platformOrder
.
setOrderState
(
Constant
.
商户订单状态
_
人工取消
);
platformOrder
.
setDealTime
(
new
Date
());
merchantOrderRepo
.
save
(
platformOrder
);
}
/**
* 商户取消订单
*/
@Transactional
public
void
merchatCancelOrder
(
@NotBlank
String
merchantId
,
@NotBlank
String
id
)
{
MerchantOrder
platformOrder
=
merchantOrderRepo
.
getOne
(
id
);
if
(!
merchantId
.
equals
(
platformOrder
.
getMerchantId
()))
{
throw
new
BizException
(
BizError
.
无权取消订单
);
}
if
(!
Constant
.
商户订单状态
_
等待接单
.
equals
(
platformOrder
.
getOrderState
()))
{
throw
new
BizException
(
BizError
.
只有等待接单状态的商户订单才能取消
);
}
platformOrder
.
setOrderState
(
Constant
.
商户订单状态
_
商户取消订单
);
platformOrder
.
setDealTime
(
new
Date
());
merchantOrderRepo
.
save
(
platformOrder
);
}
@Transactional
public
void
orderTimeoutDeal
()
{
Date
now
=
new
Date
();
List
<
MerchantOrder
>
orders
=
merchantOrderRepo
.
findByOrderStateAndUsefulTimeLessThan
(
Constant
.
商户订单状态
_
等待接单
,
now
);
for
(
MerchantOrder
order
:
orders
)
{
order
.
setDealTime
(
now
);
order
.
setOrderState
(
Constant
.
商户订单状态
_
超时取消
);
merchantOrderRepo
.
save
(
order
);
}
}
@ParamValid
@Transactional
public
StartOrderSuccessVO
startOrder
(
StartOrderParam
param
)
{
Merchant
merchant
=
merchantRepo
.
findByMerchantNum
(
param
.
getMerchantNum
());
if
(
merchant
==
null
)
{
throw
new
BizException
(
BizError
.
商户未接入
);
}
boolean
unknownPayTypeFlag
=
true
;
List
<
DictItemVO
>
payTypes
=
DictHolder
.
findDictItem
(
"gatheringChannel"
);
for
(
DictItemVO
payType
:
payTypes
)
{
if
(
payType
.
getDictItemCode
().
equals
(
param
.
getPayType
()))
{
unknownPayTypeFlag
=
false
;
}
}
if
(
unknownPayTypeFlag
)
{
throw
new
BizException
(
BizError
.
不支持该支付类型
);
}
String
sign
=
param
.
getMerchantNum
()
+
param
.
getOrderNo
()
+
new
DecimalFormat
(
"###################.###########"
).
format
(
param
.
getAmount
())
+
param
.
getNotifyUrl
()
+
merchant
.
getSecretKey
();
sign
=
new
Digester
(
DigestAlgorithm
.
MD5
).
digestHex
(
sign
);
if
(!
sign
.
equals
(
param
.
getSign
()))
{
throw
new
BizException
(
BizError
.
签名不正确
);
}
Integer
orderEffectiveDuration
=
Constant
.
商户订单接单有效时长
;
ReceiveOrderSetting
setting
=
platformOrderSettingRepo
.
findTopByOrderByLatelyUpdateTime
();
if
(
setting
!=
null
)
{
orderEffectiveDuration
=
setting
.
getReceiveOrderEffectiveDuration
();
}
MerchantOrder
merchantOrder
=
param
.
convertToPo
(
merchant
.
getId
(),
orderEffectiveDuration
);
MerchantOrderPayInfo
payInfo
=
param
.
convertToPayInfoPo
(
merchantOrder
.
getId
());
merchantOrder
.
setPayInfoId
(
payInfo
.
getId
());
merchantOrderRepo
.
save
(
merchantOrder
);
merchantOrderPayInfoRepo
.
save
(
payInfo
);
//todo this is temporary code 这里要匹配有资源的用户接单
List
<
UserAccount
>
userAccounts
=
userAccountRepo
.
findAllByAccountType
(
Constant
.
账号类型
_
会员
);
;
this
.
receiveOrder
(
userAccounts
.
get
(
RandomUtil
.
randomInt
(
0
,
userAccounts
.
size
())).
getId
(),
merchantOrder
.
getId
());
return
StartOrderSuccessVO
.
convertFor
(
merchantOrder
.
getOrderNo
());
}
/**
* 支付成功异步通知
*/
@Transactional
public
String
paySuccessAsynNotice
(
@NotBlank
String
merchantOrderId
)
{
MerchantOrderPayInfo
payInfo
=
merchantOrderPayInfoRepo
.
findByMerchantOrderId
(
merchantOrderId
);
if
(
Constant
.
商户订单支付通知状态
_
通知成功
.
equals
(
payInfo
.
getNoticeState
()))
{
log
.
warn
(
"商户订单支付已通知成功,无需重复通知;商户订单id为{}"
,
merchantOrderId
);
return
Constant
.
商户订单通知成功返回值
;
}
Merchant
merchant
=
merchantRepo
.
findByMerchantNum
(
payInfo
.
getMerchantNum
());
if
(
merchant
==
null
)
{
throw
new
BizException
(
BizError
.
商户未接入
);
}
String
sign
=
Constant
.
商户订单支付成功
+
payInfo
.
getMerchantNum
()
+
payInfo
.
getOrderNo
()
+
new
DecimalFormat
(
"###################.###########"
).
format
(
payInfo
.
getAmount
())
+
merchant
.
getSecretKey
();
sign
=
new
Digester
(
DigestAlgorithm
.
MD5
).
digestHex
(
sign
);
Map
<
String
,
Object
>
paramMap
=
new
HashMap
<>();
paramMap
.
put
(
"merchantNum"
,
payInfo
.
getMerchantNum
());
paramMap
.
put
(
"orderNo"
,
payInfo
.
getOrderNo
());
paramMap
.
put
(
"platformOrderNo"
,
payInfo
.
getMerchantOrder
().
getOrderNo
());
paramMap
.
put
(
"amount"
,
payInfo
.
getAmount
());
paramMap
.
put
(
"attch"
,
payInfo
.
getAttch
());
paramMap
.
put
(
"state"
,
Constant
.
商户订单支付成功
);
paramMap
.
put
(
"payTime"
,
DateUtil
.
format
(
payInfo
.
getMerchantOrder
().
getConfirmTime
(),
DatePattern
.
NORM_DATETIME_PATTERN
));
paramMap
.
put
(
"sign"
,
sign
);
String
result
=
"fail"
;
// 通知3次
for
(
int
i
=
0
;
i
<
3
;
i
++)
{
try
{
result
=
HttpUtil
.
get
(
payInfo
.
getNotifyUrl
(),
paramMap
,
2500
);
if
(
Constant
.
商户订单通知成功返回值
.
equals
(
result
))
{
break
;
}
}
catch
(
Exception
e
)
{
result
=
e
.
getMessage
();
log
.
error
(
MessageFormat
.
format
(
"商户订单支付成功异步通知地址请求异常,id为{0}"
,
merchantOrderId
),
e
);
}
}
payInfo
.
setNoticeState
(
Constant
.
商户订单通知成功返回值
.
equals
(
result
)
?
Constant
.
商户订单支付通知状态
_
通知成功
:
Constant
.
商户订单支付通知状态
_
通知失败
);
merchantOrderPayInfoRepo
.
save
(
payInfo
);
return
result
;
}
@Transactional
(
readOnly
=
true
)
public
PageResult
<
ReceiveOrderRecordVO
>
findLowerLevelAccountReceiveOrderRecordByPage
(
LowerLevelAccountReceiveOrderQueryCondParam
param
)
{
UserAccount
currentAccount
=
userAccountRepo
.
getOne
(
param
.
getCurrentAccountId
());
UserAccount
lowerLevelAccount
=
currentAccount
;
if
(
StrUtil
.
isNotBlank
(
param
.
getUserName
()))
{
lowerLevelAccount
=
userAccountRepo
.
findByUserName
(
param
.
getUserName
());
if
(
lowerLevelAccount
==
null
)
{
throw
new
BizException
(
BizError
.
用户名不存在
);
}
// 说明该用户名对应的账号不是当前账号的下级账号
if
(!
lowerLevelAccount
.
getAccountLevelPath
().
startsWith
(
currentAccount
.
getAccountLevelPath
()))
{
throw
new
BizException
(
BizError
.
不是上级账号无权查看该账号及下级的接单记录
);
}
}
String
lowerLevelAccountId
=
lowerLevelAccount
.
getId
();
String
lowerLevelAccountLevelPath
=
lowerLevelAccount
.
getAccountLevelPath
();
Specification
<
MerchantOrder
>
spec
=
new
Specification
<
MerchantOrder
>()
{
/**
*
*/
private
static
final
long
serialVersionUID
=
1L
;
public
Predicate
toPredicate
(
Root
<
MerchantOrder
>
root
,
CriteriaQuery
<?>
query
,
CriteriaBuilder
builder
)
{
List
<
Predicate
>
predicates
=
new
ArrayList
<
Predicate
>();
if
(
StrUtil
.
isNotBlank
(
param
.
getUserName
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"receivedAccountId"
),
lowerLevelAccountId
));
}
else
{
predicates
.
add
(
builder
.
like
(
root
.
join
(
"receivedAccount"
,
JoinType
.
INNER
).
get
(
"accountLevelPath"
),
lowerLevelAccountLevelPath
+
"%"
));
}
if
(
StrUtil
.
isNotBlank
(
param
.
getOrderState
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"orderState"
),
param
.
getOrderState
()));
}
if
(
StrUtil
.
isNotBlank
(
param
.
getGatheringChannelCode
()))
{
predicates
.
add
(
builder
.
equal
(
root
.
get
(
"gatheringChannelCode"
),
param
.
getGatheringChannelCode
()));
}
if
(
param
.
getStartTime
()
!=
null
)
{
predicates
.
add
(
builder
.
greaterThanOrEqualTo
(
root
.
get
(
"receivedTime"
).
as
(
Date
.
class
),
DateUtil
.
beginOfDay
(
param
.
getStartTime
())));
}
if
(
param
.
getEndTime
()
!=
null
)
{
predicates
.
add
(
builder
.
lessThanOrEqualTo
(
root
.
get
(
"receivedTime"
).
as
(
Date
.
class
),
DateUtil
.
endOfDay
(
param
.
getEndTime
())));
}
return
predicates
.
size
()
>
0
?
builder
.
and
(
predicates
.
toArray
(
new
Predicate
[
predicates
.
size
()]))
:
null
;
}
};
Page
<
MerchantOrder
>
result
=
merchantOrderRepo
.
findAll
(
spec
,
PageRequest
.
of
(
param
.
getPageNum
()
-
1
,
param
.
getPageSize
(),
Sort
.
by
(
Sort
.
Order
.
desc
(
"submitTime"
))));
PageResult
<
ReceiveOrderRecordVO
>
pageResult
=
new
PageResult
<>(
ReceiveOrderRecordVO
.
convertFor
(
result
.
getContent
()),
param
.
getPageNum
(),
param
.
getPageSize
(),
result
.
getTotalElements
());
return
pageResult
;
}
}
runscore-api/src/main/java/me/zohar/runscore/merchant/vo/OrderGatheringCodeVO.java
浏览文件 @
d3b8a353
...
...
@@ -8,6 +8,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
import
lombok.Data
;
import
me.zohar.runscore.dictconfig.DictHolder
;
import
me.zohar.runscore.gatheringcode.domain.GatheringCode
;
import
me.zohar.runscore.merchant.domain.MerchantOrder
;
@Data
...
...
@@ -58,6 +59,8 @@ public class OrderGatheringCodeVO {
private
String
gatheringCodeUrl
;
private
GatheringCode
gatheringCode
;
/**
* 同步通知地址
*/
...
...
@@ -75,4 +78,5 @@ public class OrderGatheringCodeVO {
return
vo
;
}
}
runscore-api/src/main/java/me/zohar/runscore/useraccount/domain/LoginLog.java
浏览文件 @
d3b8a353
...
...
@@ -32,6 +32,7 @@ public class LoginLog {
/**
* 登录系统
*/
@Column
(
name
=
"`system`"
)
private
String
system
;
/**
...
...
runscore-api/src/main/java/me/zohar/runscore/useraccount/repo/UserAccountRepo.java
浏览文件 @
d3b8a353
...
...
@@ -3,6 +3,8 @@ package me.zohar.runscore.useraccount.repo;
import
org.springframework.data.jpa.repository.JpaRepository
;
import
org.springframework.data.jpa.repository.JpaSpecificationExecutor
;
import
java.util.List
;
import
me.zohar.runscore.useraccount.domain.UserAccount
;
...
...
@@ -12,4 +14,5 @@ public interface UserAccountRepo extends JpaRepository<UserAccount, String>, Jpa
Long
countByInviterId
(
String
inviterId
);
List
<
UserAccount
>
findAllByAccountType
(
String
accountType
);
}
runscore-merchant/src/main/java/me/zohar/runscore/api/controller/ApiController.java
浏览文件 @
d3b8a353
...
...
@@ -19,6 +19,11 @@ public class ApiController {
@Autowired
private
MerchantOrderService
platformOrderService
;
/**
* 商户开单,获取支付方式,返回银行卡信息或者支付码信息
* @param param
* @return
*/
@PostMapping
(
"/startOrder"
)
@ResponseBody
public
Result
startOrder
(
StartOrderParam
param
)
{
...
...
runscore-merchant/src/test/java/me/zohar/runscore/test/StartOrderTest.java
浏览文件 @
d3b8a353
...
...
@@ -15,11 +15,12 @@ public class StartOrderTest {
*/
public
static
void
main
(
String
[]
args
)
{
String
merchantNum
=
"
201906
001"
;
String
merchantOrderNo
=
"20190629023134U93628387
7
"
;
String
merchantNum
=
"
1
001"
;
String
merchantOrderNo
=
"20190629023134U93628387
8
"
;
Double
amount
=
100
d
;
String
notifyUrl
=
"http://xhbc10.com/index.php/Pay/Paynotify"
;
String
secretKey
=
"l54x9426o68962464"
;
//String returnUrl = "http://xhbc10.com/index.php/Pay/Paynotify";
String
secretKey
=
"456"
;
String
sign
=
merchantNum
+
merchantOrderNo
+
new
DecimalFormat
(
"###################.###########"
).
format
(
amount
)
+
notifyUrl
+
secretKey
;
System
.
out
.
println
(
sign
);
...
...
runscore-mobile/src/main/resources/static/js/gathering-code.js
浏览文件 @
d3b8a353
...
...
@@ -135,9 +135,12 @@ var gatheringCodeVM = new Vue({
that
.
editGatheringCode
=
{
gatheringChannelCode
:
''
,
state
:
''
,
fixedGatheringAmount
:
tru
e
,
fixedGatheringAmount
:
fals
e
,
gatheringAmount
:
''
,
payee
:
''
payee
:
''
,
bankAddress
:
''
,
bankCode
:
''
,
bankUsername
:
''
};
that
.
showEditGatheringCodePageInner
();
that
.
initFileUploadWidget
();
...
...
@@ -187,24 +190,24 @@ var gatheringCodeVM = new Vue({
});
return
;
}
if
(
editGatheringCode
.
fixedGatheringAmount
==
null
)
{
layer
.
alert
(
'请选择是否固定收款金额'
,
{
title
:
'提示'
,
icon
:
7
,
time
:
3000
});
return
;
}
if
(
editGatheringCode
.
fixedGatheringAmount
)
{
if
(
editGatheringCode
.
gatheringAmount
==
null
||
editGatheringCode
.
gatheringAmount
==
''
)
{
layer
.
alert
(
'请输入收款金额'
,
{
title
:
'提示'
,
icon
:
7
,
time
:
3000
});
return
;
}
}
//
if (editGatheringCode.fixedGatheringAmount == null) {
//
layer.alert('请选择是否固定收款金额', {
//
title : '提示',
//
icon : 7,
//
time : 3000
//
});
//
return;
//
}
//
if (editGatheringCode.fixedGatheringAmount) {
//
if (editGatheringCode.gatheringAmount == null || editGatheringCode.gatheringAmount == '') {
//
layer.alert('请输入收款金额', {
//
title : '提示',
//
icon : 7,
//
time : 3000
//
});
//
return;
//
}
//
}
if
(
editGatheringCode
.
payee
==
null
||
editGatheringCode
.
payee
==
''
)
{
layer
.
alert
(
'请选择收款人'
,
{
title
:
'提示'
,
...
...
@@ -214,11 +217,12 @@ var gatheringCodeVM = new Vue({
return
;
}
if
(
$
(
'.gathering-code-pic'
).
fileinput
(
'getPreview'
).
content
.
length
!=
0
)
{
if
(
$
(
'.gathering-code-pic'
).
fileinput
(
'getPreview'
).
content
.
length
!=
0
||
editGatheringCode
.
gatheringChannelCode
==
'bankcard'
)
{
that
.
addOrUpdateGatheringCodeInner
();
}
else
{
var
filesCount
=
$
(
'.gathering-code-pic'
).
fileinput
(
'getFilesCount'
);
if
(
filesCount
==
0
)
{
console
.
log
(
editGatheringCode
.
gatheringChannelCode
);
if
(
filesCount
==
0
)
{
layer
.
alert
(
'请选择要上传的图片'
,
{
title
:
'提示'
,
icon
:
7
,
...
...
runscore-mobile/src/main/resources/templates/gathering-code.html
浏览文件 @
d3b8a353
<!DOCTYPE html>
<html
xmlns:th=
"http://www.thymeleaf.org"
>
<head>
<meta
charset=
"utf-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1, shrink-to-fit=no"
>
<title>
收款码
</title>
<link
rel=
"stylesheet"
href=
"https://cdn.staticfile.org/twitter-bootstrap/4.1.0/css/bootstrap.min.css"
>
<script
src=
"https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"
></script>
<script
src=
"https://cdn.staticfile.org/popper.js/1.12.5/umd/popper.min.js"
></script>
<script
src=
"https://cdn.staticfile.org/twitter-bootstrap/4.1.0/js/bootstrap.min.js"
></script>
<script
src=
"https://cdn.staticfile.org/vue/2.4.2/vue.min.js"
></script>
<script
src=
"https://cdn.staticfile.org/vue-resource/1.5.1/vue-resource.min.js"
></script>
<script
src=
"https://cdn.bootcss.com/dayjs/1.7.8/dayjs.min.js"
></script>
<script
src=
"https://cdn.bootcss.com/layer/2.3/layer.js"
></script>
<link
href=
"https://cdnjs.cloudflare.com/ajax/libs/bootstrap-fileinput/5.0.1/css/fileinput.min.css"
media=
"all"
rel=
"stylesheet"
type=
"text/css"
/>
<script
src=
"https://cdnjs.cloudflare.com/ajax/libs/bootstrap-fileinput/5.0.1/js/fileinput.min.js"
></script>
<script
src=
"/js/common/common.js"
></script>
<link
href=
"/css/common/common.css"
rel=
"stylesheet"
type=
"text/css"
>
<style
type=
"text/css"
>
.account-change
{
background-color
:
#fff
;
;
margin-bottom
:
1rem
;
display
:
flex
;
padding-left
:
1.2rem
;
padding-right
:
0.2rem
;
padding-top
:
0.5rem
;
padding-bottom
:
0.5rem
;
}
<meta
charset=
"utf-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1, shrink-to-fit=no"
>
<title>
收款码
</title>
<link
rel=
"stylesheet"
href=
"https://cdn.staticfile.org/twitter-bootstrap/4.1.0/css/bootstrap.min.css"
>
<script
src=
"https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"
></script>
<script
src=
"https://cdn.staticfile.org/popper.js/1.12.5/umd/popper.min.js"
></script>
<script
src=
"https://cdn.staticfile.org/twitter-bootstrap/4.1.0/js/bootstrap.min.js"
></script>
<script
src=
"https://cdn.staticfile.org/vue/2.4.2/vue.min.js"
></script>
<script
src=
"https://cdn.staticfile.org/vue-resource/1.5.1/vue-resource.min.js"
></script>
<script
src=
"https://cdn.bootcss.com/dayjs/1.7.8/dayjs.min.js"
></script>
<script
src=
"https://cdn.bootcss.com/layer/2.3/layer.js"
></script>
<link
href=
"https://cdnjs.cloudflare.com/ajax/libs/bootstrap-fileinput/5.0.1/css/fileinput.min.css"
media=
"all"
rel=
"stylesheet"
type=
"text/css"
/>
<script
src=
"https://cdnjs.cloudflare.com/ajax/libs/bootstrap-fileinput/5.0.1/js/fileinput.min.js"
></script>
<script
src=
"/js/common/common.js"
></script>
<link
href=
"/css/common/common.css"
rel=
"stylesheet"
type=
"text/css"
>
<style
type=
"text/css"
>
.account-change
{
background-color
:
#fff
;
;
margin-bottom
:
1rem
;
display
:
flex
;
padding-left
:
1.2rem
;
padding-right
:
0.2rem
;
padding-top
:
0.5rem
;
padding-bottom
:
0.5rem
;
}
.account-change-body
{
flex
:
4
;
display
:
flex
;
flex-direction
:
column
;
}
.account-change-body
{
flex
:
4
;
display
:
flex
;
flex-direction
:
column
;
}
.account-change-body-item
label
{
text-align
:
end
;
width
:
5rem
;
margin-right
:
0.5rem
;
}
.account-change-body-item
label
{
text-align
:
end
;
width
:
5rem
;
margin-right
:
0.5rem
;
}
.account-change-body-item
span
{
min-width
:
5rem
;
display
:
inline-block
;
}
.account-change-body-item
span
{
min-width
:
5rem
;
display
:
inline-block
;
}
.account-change-body-item
button
{
width
:
48%
;
}
.account-change-body-item
button
{
width
:
48%
;
}
.account-change-body-item-highlight
{
color
:
#c3606a
;
}
.account-change-body-item-highlight
{
color
:
#c3606a
;
}
.page-link
{
color
:
#007bff
!important
}
</style>
.page-link
{
color
:
#007bff
!important
}
</style>
</head>
<body>
<div
th:replace=
"common/header::html"
></div>
<div
id=
"gatheringCode"
v-cloak
>
<div
class=
"page-body"
style=
"background: #f2f2f2;"
v-show=
"showGatheringCodeFlag"
>
<button
type=
"button"
style=
"margin-top: 0.3rem;"
class=
"btn btn-outline-info btn-lg btn-block"
v-on:click=
"showEditGatheringCodePage()"
>
添加收款码
</button>
<div
class=
"input-group"
style=
"padding-bottom: 0.3rem; padding-top: 0.3rem;"
>
<select
class=
"form-control"
style=
"width: 2.2rem; height: 3rem;"
v-model=
"state"
>
<option
value=
""
>
状态
</option>
<option
v-for=
"dictItem in gatheringCodeStateDictItems"
:value=
"dictItem.dictItemCode"
>
{{dictItem.dictItemName}}
</option>
</select>
<select
class=
"form-control"
style=
"width: 6.2rem; height: 3rem;"
v-model=
"gatheringChannelCode"
>
<option
value=
""
>
收款渠道
</option>
<option
v-for=
"dictItem in gatheringChannelDictItems"
:value=
"dictItem.dictItemCode"
>
{{dictItem.dictItemName}}
</option>
</select>
<div
class=
"input-group-append"
>
<button
class=
"btn btn-outline-secondary"
type=
"button"
v-on:click=
"query"
>
查询
</button>
</div>
</div>
<div
class=
"account-change"
v-for=
"gatheringCode in gatheringCodes"
>
<div
class=
"account-change-body"
>
<div
class=
"account-change-body-item"
>
<label>
创建时间:
</label>
<span>
{{gatheringCode.createTime}}
</span>
</div>
<div
class=
"account-change-body-item"
>
<label>
收款渠道:
</label>
<span>
{{gatheringCode.gatheringChannelName}}
</span><label>
状态:
</label>
<span>
{{gatheringCode.stateName}}
</span>
</div>
<div
class=
"account-change-body-item"
>
<label>
收款金额:
</label>
<span
class=
"account-change-body-item-highlight"
>
{{gatheringCode.fixedGatheringAmount ? gatheringCode.gatheringAmount + '元' : '不固定'}}
</span><label>
收款人:
</label>
<span
class=
"account-change-body-item-highlight"
>
{{gatheringCode.payee}}
</span>
</div>
<div
class=
"account-change-body-item"
>
<button
class=
"btn btn-outline-info btn-lg"
type=
"button"
v-on:click=
"showEditGatheringCodePage(gatheringCode.id)"
>
编辑
</button>
<button
class=
"btn btn-outline-danger btn-lg"
type=
"button"
v-on:click=
"delGatheringCode(gatheringCode.id)"
>
删除
</button>
</div>
</div>
</div>
<div
style=
"padding-left: 13rem;"
>
<ul
class=
"pagination"
>
<li
class=
"page-item"
><a
class=
"page-link"
v-show=
"totalPage > 0 && pageNum > 1"
v-on:click=
"prePage"
>
上一页
</a></li>
<li
class=
"page-item"
><a
class=
"page-link"
v-show=
"pageNum >= 1 && pageNum < totalPage"
v-on:click=
"nextPage"
>
下一页
</a></li>
</ul>
</div>
</div>
<div
class=
"page-body"
style=
"background: #f2f2f2;"
v-show=
"showEditGatheringCodeFlag"
>
<div
class=
"container"
style=
"margin-top: 1.3rem;"
>
<form>
<div
class=
"form-group"
>
<label>
收款渠道:
</label>
<select
class=
"form-control"
v-model=
"editGatheringCode.gatheringChannelCode"
@
change=
"alert(editGatheringCode.gatheringChannelCode);"
>
<option
value=
""
>
请选择
</option>
<option
v-for=
"dictItem in gatheringChannelDictItems"
:value=
"dictItem.dictItemCode"
>
{{dictItem.dictItemName}}
</option>
</select>
</div>
<div
class=
"form-group"
>
<label>
状态:
</label>
<select
class=
"form-control"
v-model=
"editGatheringCode.state"
>
<option
value=
""
>
请选择
</option>
<option
v-for=
"dictItem in gatheringCodeStateDictItems"
:value=
"dictItem.dictItemCode"
>
{{dictItem.dictItemName}}
</option>
</select>
</div>
<!--<div class="form-group">
<label> <span> 收款金额:</span>
<div class="custom-control custom-checkbox custom-control-inline" v-on:click="switchGatheringAmountMode">
<input type="checkbox" id="fixedGatheringAmount" class="custom-control-input" v-model="editGatheringCode.fixedGatheringAmount">
<label for="fixedGatheringAmount" class="custom-control-label">固定收款金额</label>
</div>
</label> <input type="number" class="form-control" v-model="editGatheringCode.gatheringAmount" :disabled="!editGatheringCode.fixedGatheringAmount">
</div>-->
<div
class=
"form-group"
>
<label>
收款人:
</label>
<input
type=
"text"
class=
"form-control"
v-model=
"editGatheringCode.payee"
>
</div>
<div
class=
"form-group"
v-show=
"editGatheringCode.gatheringChannelCode == 'bankcard'"
style=
""
><label>
银行卡号:
</label>
<input
type=
"text"
class=
"form-control"
>
<label>
开户行:
</label>
<input
type=
"text"
class=
"form-control"
>
<label>
卡姓名:
</label>
<input
type=
"text"
class=
"form-control"
></div>
<div
class=
"form-group"
v-show=
"editGatheringCode.gatheringChannelCode != 'bankcard'"
>
<label>
收款码:
</label>
<input
type=
"file"
class=
"form-control gathering-code-pic"
>
</div>
<button
type=
"button"
class=
"btn btn-danger btn-lg btn-block"
v-on:click=
"addOrUpdateGatheringCode"
>
保存
</button>
<button
type=
"button"
class=
"btn btn-light btn-lg btn-block"
v-on:click=
"hideEditGatheringCodePage"
>
返回
</button>
</form>
<div
th:replace=
"common/header::html"
></div>
<div
id=
"gatheringCode"
v-cloak
>
<div
class=
"page-body"
style=
"background: #f2f2f2;"
v-show=
"showGatheringCodeFlag"
>
<button
type=
"button"
style=
"margin-top: 0.3rem;"
class=
"btn btn-outline-info btn-lg btn-block"
v-on:click=
"showEditGatheringCodePage()"
>
添加收款码
</button>
<div
class=
"input-group"
style=
"padding-bottom: 0.3rem; padding-top: 0.3rem;"
>
<select
class=
"form-control"
style=
"width: 2.2rem; height: 3rem;"
v-model=
"state"
>
<option
value=
""
>
状态
</option>
<option
v-for=
"dictItem in gatheringCodeStateDictItems"
:value=
"dictItem.dictItemCode"
>
{{dictItem.dictItemName}}
</option>
</select>
<select
class=
"form-control"
style=
"width: 6.2rem; height: 3rem;"
v-model=
"gatheringChannelCode"
>
<option
value=
""
>
收款渠道
</option>
<option
v-for=
"dictItem in gatheringChannelDictItems"
:value=
"dictItem.dictItemCode"
>
{{dictItem.dictItemName}}
</option>
</select>
<div
class=
"input-group-append"
>
<button
class=
"btn btn-outline-secondary"
type=
"button"
v-on:click=
"query"
>
查询
</button>
</div>
</div>
<div
class=
"account-change"
v-for=
"gatheringCode in gatheringCodes"
>
<div
class=
"account-change-body"
>
<div
class=
"account-change-body-item"
>
<label>
创建时间:
</label>
<span>
{{gatheringCode.createTime}}
</span>
</div>
<div
class=
"account-change-body-item"
>
<label>
收款渠道:
</label>
<span>
{{gatheringCode.gatheringChannelName}}
</span><label>
状态:
</label>
<span>
{{gatheringCode.stateName}}
</span>
</div>
<div
class=
"account-change-body-item"
>
<label>
收款金额:
</label>
<span
class=
"account-change-body-item-highlight"
>
{{gatheringCode.fixedGatheringAmount ? gatheringCode.gatheringAmount + '元' : '不固定'}}
</span><label>
收款人:
</label>
<span
class=
"account-change-body-item-highlight"
>
{{gatheringCode.payee}}
</span>
</div>
<div
class=
"account-change-body-item"
>
<button
class=
"btn btn-outline-info btn-lg"
type=
"button"
v-on:click=
"showEditGatheringCodePage(gatheringCode.id)"
>
编辑
</button>
<button
class=
"btn btn-outline-danger btn-lg"
type=
"button"
v-on:click=
"delGatheringCode(gatheringCode.id)"
>
删除
</button>
</div>
</div>
</div>
<div
style=
"padding-left: 13rem;"
>
<ul
class=
"pagination"
>
<li
class=
"page-item"
><a
class=
"page-link"
v-show=
"totalPage > 0 && pageNum > 1"
v-on:click=
"prePage"
>
上一页
</a></li>
<li
class=
"page-item"
><a
class=
"page-link"
v-show=
"pageNum >= 1 && pageNum < totalPage"
v-on:click=
"nextPage"
>
下一页
</a></li>
</ul>
</div>
</div>
<div
class=
"page-body"
style=
"background: #f2f2f2;"
v-show=
"showEditGatheringCodeFlag"
>
<div
class=
"container"
style=
"margin-top: 1.3rem;"
>
<form>
<div
class=
"form-group"
>
<label>
收款渠道:
</label>
<select
class=
"form-control"
v-model=
"editGatheringCode.gatheringChannelCode"
>
<option
value=
""
>
请选择
</option>
<option
v-for=
"dictItem in gatheringChannelDictItems"
:value=
"dictItem.dictItemCode"
>
{{dictItem.dictItemName}}
</option>
</select>
</div>
<div
class=
"form-group"
>
<label>
状态:
</label>
<select
class=
"form-control"
v-model=
"editGatheringCode.state"
>
<option
value=
""
>
请选择
</option>
<option
v-for=
"dictItem in gatheringCodeStateDictItems"
:value=
"dictItem.dictItemCode"
>
{{dictItem.dictItemName}}
</option>
</select>
</div>
<!--<div class="form-group">
<label> <span> 收款金额:</span>
<div class="custom-control custom-checkbox custom-control-inline" v-on:click="switchGatheringAmountMode">
<input type="checkbox" id="fixedGatheringAmount" class="custom-control-input" v-model="editGatheringCode.fixedGatheringAmount">
<label for="fixedGatheringAmount" class="custom-control-label">固定收款金额</label>
</div>
</label> <input type="number" class="form-control" v-model="editGatheringCode.gatheringAmount" :disabled="!editGatheringCode.fixedGatheringAmount">
</div>-->
<div
class=
"form-group"
>
<label>
收款人:
</label>
<input
type=
"text"
class=
"form-control"
v-model=
"editGatheringCode.payee"
>
</div>
<div
class=
"form-group"
v-show=
"editGatheringCode.gatheringChannelCode == 'bankcard'"
style=
""
>
<label>
银行卡号:
</label>
<input
type=
"text"
class=
"form-control"
v-model=
"editGatheringCode.bankCode"
>
<label>
开户行:
</label>
<input
type=
"text"
class=
"form-control"
v-model=
"editGatheringCode.bankAddress"
>
<label>
卡户主:
</label>
<input
type=
"text"
class=
"form-control"
v-model=
"editGatheringCode.bankUsername"
>
</div>
<div
class=
"form-group"
v-show=
"editGatheringCode.gatheringChannelCode != 'bankcard'"
>
<label>
收款码:
</label>
<input
type=
"file"
class=
"form-control gathering-code-pic"
>
</div>
<button
type=
"button"
class=
"btn btn-danger btn-lg btn-block"
v-on:click=
"addOrUpdateGatheringCode"
>
保存
</button>
<button
type=
"button"
class=
"btn btn-light btn-lg btn-block"
v-on:click=
"hideEditGatheringCodePage"
>
返回
</button>
</form>
</div>
</div>
</div>
<div
th:replace=
"common/footer::html"
></div>
<script
type=
"text/javascript"
src=
"/js/gathering-code.js"
></script>
</div>
</div>
</div>
<div
th:replace=
"common/footer::html"
></div>
<script
type=
"text/javascript"
src=
"/js/gathering-code.js"
></script>
</body>
</html>
\ No newline at end of file
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论