Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
P
promotion-service
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
promotion
promotion-service
Commits
71bacad6
提交
71bacad6
authored
4月 15, 2024
作者:
李秋林
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
腾讯云-短信服务工具类添加;促销员注册手机验证码进行验证
上级
b1f6c41d
隐藏空白字符变更
内嵌
并排
正在显示
12 个修改的文件
包含
351 行增加
和
57 行删除
+351
-57
pom.xml
pom.xml
+11
-0
RedisKeys.java
...java/com/wangxiaolu/promotion/common/redis/RedisKeys.java
+20
-0
RedisCache.java
...wangxiaolu/promotion/common/redis/service/RedisCache.java
+82
-0
ConsoleDataQueryController.java
...ion/controller/tengxunyun/ConsoleDataQueryController.java
+20
-0
TencentCoreController.java
...romotion/controller/tengxunyun/TencentCoreController.java
+44
-0
WeChatUserCoreController.java
...promotion/controller/wechat/WeChatUserCoreController.java
+12
-3
RCode.java
.../java/com/wangxiaolu/promotion/result/basedata/RCode.java
+8
-1
TencentCoreService.java
...wangxiaolu/promotion/service/user/TencentCoreService.java
+14
-0
TencentCoreServiceImpl.java
...u/promotion/service/user/impl/TencentCoreServiceImpl.java
+54
-0
TencentUtils.java
...ain/java/com/wangxiaolu/promotion/utils/TencentUtils.java
+49
-51
application.yml
src/main/resources/application.yml
+3
-2
TencentCoreControllerTest.java
...tion/controller/tengxunyun/TencentCoreControllerTest.java
+34
-0
没有找到文件。
pom.xml
浏览文件 @
71bacad6
...
...
@@ -144,6 +144,17 @@
<version>
3.1.998
</version>
</dependency>
<dependency>
<groupId>
org.springframework.boot
</groupId>
<artifactId>
spring-boot-starter-data-redis
</artifactId>
<version>
2.4.2
</version>
</dependency>
<dependency>
<groupId>
redis.clients
</groupId>
<artifactId>
jedis
</artifactId>
</dependency>
</dependencies>
<build>
...
...
src/main/java/com/wangxiaolu/promotion/common/redis/RedisKeys.java
0 → 100644
浏览文件 @
71bacad6
package
com
.
wangxiaolu
.
promotion
.
common
.
redis
;
import
lombok.AllArgsConstructor
;
import
lombok.Getter
;
public
interface
RedisKeys
{
@AllArgsConstructor
@Getter
enum
UserKeys
{
/**
* 用户接收手机验证码
*/
PHONE_VER_CODE
(
"user:phone_"
),
;
String
key
;
}
}
src/main/java/com/wangxiaolu/promotion/common/redis/service/RedisCache.java
0 → 100644
浏览文件 @
71bacad6
package
com
.
wangxiaolu
.
promotion
.
common
.
redis
.
service
;
import
com.alibaba.fastjson.JSONObject
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.data.redis.core.RedisTemplate
;
import
org.springframework.data.redis.core.ValueOperations
;
import
org.springframework.stereotype.Component
;
import
java.util.concurrent.TimeUnit
;
@Component
public
class
RedisCache
{
@Autowired
RedisTemplate
<
String
,
String
>
redisTemplate
;
/**
* 保存一个值
*/
public
void
add
(
String
key
,
String
val
)
{
ValueOperations
<
String
,
String
>
vo
=
redisTemplate
.
opsForValue
();
vo
.
set
(
key
,
val
);
}
/**
* 保存一个值,设置过期时间(分钟)
*/
public
void
addToMinute
(
String
key
,
String
val
,
long
time
)
{
ValueOperations
<
String
,
String
>
vo
=
redisTemplate
.
opsForValue
();
vo
.
set
(
key
,
val
,
time
,
TimeUnit
.
MINUTES
);
}
/**
* 保存一个值,并将val json化
*/
public
void
addToJson
(
String
key
,
Object
val
)
{
ValueOperations
<
String
,
String
>
vo
=
redisTemplate
.
opsForValue
();
vo
.
set
(
key
,
valToJson
(
val
));
}
/**
* 保存一个值,并将val json化,设置过期时间(分钟)
*/
public
void
addToJsonToMinute
(
String
key
,
Object
val
,
long
time
)
{
ValueOperations
<
String
,
String
>
vo
=
redisTemplate
.
opsForValue
();
vo
.
set
(
key
,
valToJson
(
val
),
time
,
TimeUnit
.
MINUTES
);
}
/**
* 获取一个值
*/
public
String
get
(
String
key
)
{
ValueOperations
<
String
,
String
>
vo
=
redisTemplate
.
opsForValue
();
return
vo
.
get
(
key
);
}
/**
* 获取一个值,并将val json化
*/
public
JSONObject
getToJson
(
String
key
)
{
ValueOperations
<
String
,
String
>
vo
=
redisTemplate
.
opsForValue
();
String
val
=
vo
.
get
(
key
);
return
JSONObject
.
parseObject
(
val
);
}
/**
* 获取一个值,并将val json化
*/
// public JSONObject getUserJsonInfo(String authorization) {
// JSONObject userDetail = getToJson(RedisKeys.UserKeys.TOKEN.getKey() + authorization);
// return userDetail;
// }
public
void
removeKey
(
String
key
)
{
redisTemplate
.
delete
(
key
);
}
private
String
valToJson
(
Object
o
)
{
return
JSONObject
.
toJSONString
(
o
);
}
}
src/main/java/com/wangxiaolu/promotion/controller/tengxunyun/ConsoleDataQueryController.java
0 → 100644
浏览文件 @
71bacad6
package
com
.
wangxiaolu
.
promotion
.
controller
.
tengxunyun
;
import
lombok.extern.slf4j.Slf4j
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
/**
* @author : liqiulin
* @date : 2024-04-15 14
* @describe : 验证、控制相关数据接口
*/
@Slf4j
@RestController
@RequestMapping
(
"/console/query"
)
public
class
ConsoleDataQueryController
{
/**
* 查询验证码
*/
}
src/main/java/com/wangxiaolu/promotion/controller/tengxunyun/TencentCoreController.java
0 → 100644
浏览文件 @
71bacad6
package
com
.
wangxiaolu
.
promotion
.
controller
.
tengxunyun
;
import
com.wangxiaolu.promotion.exception.ParamException
;
import
com.wangxiaolu.promotion.result.basedata.RCode
;
import
com.wangxiaolu.promotion.service.user.TencentCoreService
;
import
com.wangxiaolu.promotion.utils.DataUtils
;
import
lombok.extern.slf4j.Slf4j
;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.web.bind.annotation.PostMapping
;
import
org.springframework.web.bind.annotation.RequestBody
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
java.util.Map
;
/**
* @author : liqiulin
* @date : 2024-04-15 11
* @describe : 对接腾讯云接口
* 如:腾讯云短信
*/
@Slf4j
@RestController
@RequestMapping
(
"/tencent/core"
)
public
class
TencentCoreController
{
@Autowired
TencentCoreService
tencentCoreService
;
/**
* 腾讯云短信
*/
@PostMapping
(
"/send/sms"
)
public
void
sendSms
(
@RequestBody
Map
<
String
,
String
>
phoneInfo
)
{
String
phone
=
phoneInfo
.
get
(
"phone"
);
if
(
StringUtils
.
isEmpty
(
phone
)
||
!
DataUtils
.
phonePattern
(
phone
))
{
throw
new
ParamException
(
RCode
.
PHONE_PARAM_ERROR
,
null
);
}
tencentCoreService
.
sendSmsPhoneVerCOde
(
phone
);
}
}
src/main/java/com/wangxiaolu/promotion/controller/wechat/WeChatUserCoreController.java
浏览文件 @
71bacad6
package
com
.
wangxiaolu
.
promotion
.
controller
.
wechat
;
import
com.alibaba.fastjson.JSONObject
;
import
com.wangxiaolu.promotion.common.redis.RedisKeys
;
import
com.wangxiaolu.promotion.common.redis.service.RedisCache
;
import
com.wangxiaolu.promotion.exception.ParamException
;
import
com.wangxiaolu.promotion.pojo.user.dto.WxTemporaryInfoDto
;
import
com.wangxiaolu.promotion.pojo.user.vo.WxJsUserInfoVo
;
...
...
@@ -8,6 +10,7 @@ import com.wangxiaolu.promotion.result.basedata.RCode;
import
com.wangxiaolu.promotion.service.wechat.WeChatUserCoreService
;
import
com.wangxiaolu.promotion.utils.DataUtils
;
import
lombok.extern.slf4j.Slf4j
;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.beans.BeanUtils
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.validation.annotation.Validated
;
...
...
@@ -28,22 +31,28 @@ public class WeChatUserCoreController {
@Autowired
WeChatUserCoreService
weChatUserCoreService
;
@Autowired
RedisCache
redisCache
;
/**
* 促销员注册信息
*/
@PostMapping
(
"/temporary/enroll"
)
public
boolean
enrollUserInfo
(
@RequestBody
@Validated
WxJsUserInfoVo
wxJsUserInfoVo
)
{
log
.
info
(
"微信-促销员注册:{}"
,
JSONObject
.
toJSONString
(
wxJsUserInfoVo
));
// 人员信息校验
boolean
isIden
=
DataUtils
.
idenCardPattern
(
wxJsUserInfoVo
.
getIdenNumber
());
boolean
isPhone
=
DataUtils
.
phonePattern
(
wxJsUserInfoVo
.
getPhone
());
if
(!
isIden
||
!
isPhone
)
{
throw
new
ParamException
(
RCode
.
ENROLL_PARAM_ERROR
,
null
);
}
// todo 手机号校验 wxJsUserInfoVo.getPhoneCode()
log
.
info
(
"微信-促销员注册:{}"
,
JSONObject
.
toJSONString
(
wxJsUserInfoVo
));
// 手机号-验证码 校验
String
redisKey
=
RedisKeys
.
UserKeys
.
PHONE_VER_CODE
.
getKey
()
+
wxJsUserInfoVo
.
getPhone
();
String
phoneCodeOld
=
redisCache
.
get
(
redisKey
);
if
(
StringUtils
.
isBlank
(
phoneCodeOld
)
||
!
phoneCodeOld
.
equals
(
wxJsUserInfoVo
.
getPhoneCode
())){
throw
new
ParamException
(
RCode
.
TENCENT_SMS_PHONE_CODE_ERROR
,
null
);
}
WxTemporaryInfoDto
temporaryDto
=
new
WxTemporaryInfoDto
();
BeanUtils
.
copyProperties
(
wxJsUserInfoVo
,
temporaryDto
);
...
...
src/main/java/com/wangxiaolu/promotion/result/basedata/RCode.java
浏览文件 @
71bacad6
...
...
@@ -30,7 +30,14 @@ public enum RCode implements StatusCode {
*/
LOGIN_PARAM_ERROR
(
3000
,
"登录信息错误"
),
ENROLL_PARAM_ERROR
(
3001
,
"注册信息错误"
),
PHONE_PARAM_ERROR
(
3002
,
"注册信息错误"
),
PHONE_PARAM_ERROR
(
3002
,
"手机号异常"
),
/**
* 腾讯云
* 3500+
*/
TENCENT_SMS_ERROR
(
3500
,
"短信发送失败"
),
TENCENT_SMS_REPETITION
(
3501
,
"短信已发送"
),
TENCENT_SMS_PHONE_CODE_ERROR
(
3502
,
"验证码错误"
),
;
...
...
src/main/java/com/wangxiaolu/promotion/service/user/TencentCoreService.java
0 → 100644
浏览文件 @
71bacad6
package
com
.
wangxiaolu
.
promotion
.
service
.
user
;
/**
* @author : liqiulin
* @date : 2024-04-15 15
* @describe :
*/
public
interface
TencentCoreService
{
/**
* 发送手机号验证码
* @param phone 手机号
*/
void
sendSmsPhoneVerCOde
(
String
phone
);
}
src/main/java/com/wangxiaolu/promotion/service/user/impl/TencentCoreServiceImpl.java
0 → 100644
浏览文件 @
71bacad6
package
com
.
wangxiaolu
.
promotion
.
service
.
user
.
impl
;
import
com.wangxiaolu.promotion.common.redis.RedisKeys
;
import
com.wangxiaolu.promotion.common.redis.service.RedisCache
;
import
com.wangxiaolu.promotion.exception.ParamException
;
import
com.wangxiaolu.promotion.result.basedata.RCode
;
import
com.wangxiaolu.promotion.service.user.TencentCoreService
;
import
com.wangxiaolu.promotion.utils.DataUtils
;
import
com.wangxiaolu.promotion.utils.TencentUtils
;
import
lombok.extern.slf4j.Slf4j
;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.stereotype.Service
;
/**
* @author : liqiulin
* @date : 2024-04-15 15
* @describe :
*/
@Slf4j
@Service
public
class
TencentCoreServiceImpl
implements
TencentCoreService
{
@Value
(
"${tengxunyun.sms.overdue_long}"
)
private
long
overdueLong
;
@Autowired
TencentUtils
tencentUtils
;
@Autowired
RedisCache
redisCache
;
@Override
public
void
sendSmsPhoneVerCOde
(
String
phone
)
{
String
redisKey
=
RedisKeys
.
UserKeys
.
PHONE_VER_CODE
.
getKey
()
+
phone
;
// 判断是否已获取
String
verCodeOld
=
redisCache
.
get
(
redisKey
);
if
(
StringUtils
.
isNotBlank
(
verCodeOld
)){
throw
new
ParamException
(
RCode
.
TENCENT_SMS_REPETITION
,
null
);
}
// 生成6位验证码
String
phoneVerCode
=
DataUtils
.
phoneVerCode
();
log
.
info
(
"腾讯云短信,发送验证码:{}-{}"
,
phone
,
phoneVerCode
);
redisCache
.
addToMinute
(
redisKey
,
phoneVerCode
,
overdueLong
);
// boolean succss = tencentUtils.sendSmsPhoneVerCOde(phone, phoneVerCode);
boolean
succss
=
true
;
if
(!
succss
)
{
redisCache
.
removeKey
(
redisKey
);
log
.
info
(
"腾讯云短信发送失败:删除验证码:{}-{}"
,
phone
,
phoneVerCode
);
}
}
}
src/main/java/com/wangxiaolu/promotion/
controller/tengxunyun/TengXunYunController
.java
→
src/main/java/com/wangxiaolu/promotion/
utils/TencentUtils
.java
浏览文件 @
71bacad6
package
com
.
wangxiaolu
.
promotion
.
controller
.
tengxunyun
;
package
com
.
wangxiaolu
.
promotion
.
utils
;
import
com.tencentcloudapi.common.Credential
;
import
com.tencentcloudapi.common.exception.TencentCloudSDKException
;
...
...
@@ -7,66 +7,74 @@ import com.tencentcloudapi.common.profile.HttpProfile;
import
com.tencentcloudapi.sms.v20210111.SmsClient
;
import
com.tencentcloudapi.sms.v20210111.models.SendSmsRequest
;
import
com.tencentcloudapi.sms.v20210111.models.SendSmsResponse
;
import
com.tencentcloudapi.sms.v20210111.models.SendStatus
;
import
com.wangxiaolu.promotion.exception.ParamException
;
import
com.wangxiaolu.promotion.result.basedata.RCode
;
import
com.wangxiaolu.promotion.utils.DataUtils
;
import
lombok.extern.slf4j.Slf4j
;
import
org.apache.commons.lang3.StringUtils
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.web.bind.annotation.PostMapping
;
import
org.springframework.web.bind.annotation.RequestMapping
;
import
org.springframework.web.bind.annotation.RestController
;
import
java.util.Map
;
import
org.springframework.stereotype.Component
;
/**
* @author : liqiulin
* @date : 2024-04-15 11
* @describe : 对接腾讯云接口
* 如:腾讯云短信
* @date : 2024-04-15 15
* @describe :
*/
@Slf4j
@RestController
@RequestMapping
(
"/tencent"
)
public
class
TengXunYunController
{
@Component
public
class
TencentUtils
{
/**
* tengxunyun.sms
*/
@Value
(
"${tengxunyun.secret_d}"
)
private
String
secretId
;
@Value
(
"${tengxunyun.secret_key}"
)
private
String
secretKey
;
/**
* tengxunyun.sms
*/
@Value
(
"${tengxunyun.sms.endpoint_beijing}"
)
private
String
endpointBeijing
;
@Value
(
"${tengxunyun.sms.ap_beijing}"
)
private
String
apBeijing
;
@Value
(
"${tengxunyun.sms.sdk_app_id_defult}"
)
private
String
sdkAppIdDefult
;
@Value
(
"${tengxunyun.sms.sign_name}"
)
private
String
signName
;
@Value
(
"${tengxunyun.sms.template_id}"
)
private
String
templateId
;
@Value
(
"${tengxunyun.sms.sign_name
_a
}"
)
private
String
signName
A
;
@Value
(
"${tengxunyun.sms.template_id
_a
}"
)
private
String
templateId
A
;
@Value
(
"${tengxunyun.sms.overdue_long}"
)
private
Stri
ng
overdueLong
;
private
lo
ng
overdueLong
;
/**
* 腾讯云短信
* 给指定手机号发送验证码
*
* @param phone 手机号(1个)
*/
@PostMapping
(
"/send/sms"
)
public
void
sendSms
(
Map
<
String
,
String
>
phoneInfo
)
{
String
phone
=
phoneInfo
.
get
(
"phone"
)
;
public
boolean
sendSmsPhoneVerCOde
(
String
phone
,
String
phoneVerCode
)
{
String
signName
=
signNameA
;
String
templateId
=
templateIdA
;
if
(
StringUtils
.
isEmpty
(
phone
)
||
!
DataUtils
.
phonePattern
(
phone
))
{
throw
new
ParamException
(
RCode
.
PHONE_PARAM_ERROR
,
null
);
}
// todo 生成6位验证码并且放到redis中
String
phoneVerCode
=
DataUtils
.
phoneVerCode
();
System
.
out
.
println
(
"生成验证码:"
+
phoneVerCode
);
String
[]
templateParamSet
=
{
phoneVerCode
,
Long
.
toString
(
overdueLong
)};
String
[]
phoneNumberSet
=
{
"+86"
+
phone
};
SendSmsResponse
res
=
sendSms
(
templateParamSet
,
phoneNumberSet
,
signName
,
templateId
);
SendStatus
sendStatus
=
res
.
getSendStatusSet
()[
0
];
if
(
StringUtils
.
isBlank
(
sendStatus
.
getCode
())
||
!
sendStatus
.
getCode
().
contains
(
"OK"
))
{
return
false
;
}
return
true
;
}
/**
* 核心工具:发送短信
* @return 发送结果
*/
private
SendSmsResponse
sendSms
(
String
[]
templateParamSet
,
String
[]
phoneNumberSet
,
String
signName
,
String
templateId
)
{
try
{
/**
* 必要步骤
*/
// 必要步骤
// 1、实例化一个认证对象
Credential
cred
=
new
Credential
(
secretId
,
secretKey
);
// 2、实例化http选项,SDK默认使用POST方法,GET方法无法处理一些较大的请求(非必要不使用);SDK有默认的超时时间,非必要请不要进行调整
...
...
@@ -75,10 +83,7 @@ public class TengXunYunController {
httpProfile
.
setConnTimeout
(
60
);
httpProfile
.
setEndpoint
(
endpointBeijing
);
/**
* 非必要步骤
*/
// 非必要步骤
// 1、例化一个客户端配置对象
ClientProfile
clientProfile
=
new
ClientProfile
();
/* SDK默认用TC3-HMAC-SHA256进行签名
...
...
@@ -92,18 +97,11 @@ public class TengXunYunController {
req
.
setSmsSdkAppId
(
sdkAppIdDefult
);
req
.
setSignName
(
signName
);
req
.
setTemplateId
(
templateId
);
/**
* 模板参数
* index0:验证码;index1:过期时间(分钟)
*/
String
[]
templateParamSet
=
{
"654321"
,
overdueLong
};
// 参数列表:index0:验证码;index1:过期时间(分钟)
req
.
setTemplateParamSet
(
templateParamSet
);
/**
* 下发手机号码,采用E.164标准(+[国家或地区码][手机号]),最多不要超过200个手机号
*/
String
E164
=
"+86"
;
String
[]
phoneNumberSet
=
{
E164
+
"15701654502"
,
phoneVerCode
};
// 下发手机号码,采用E.164标准(+[国家或地区码][手机号]),最多不要超过200个手机号
req
.
setPhoneNumberSet
(
phoneNumberSet
);
/* 用户的 session 内容(无需要可忽略): 可以携带用户侧 ID 等上下文信息,server 会原样返回 */
...
...
@@ -117,16 +115,16 @@ public class TengXunYunController {
req
.
setSenderId
(
senderid
);
/* 通过 client 对象调用 SendSms 方法发起请求。注意请求方法名与请求对象是对应的
* 返回的 res 是一个 SendSmsResponse 类的实例,与请求对象对应 */
SendSmsResponse
res
=
client
.
SendSms
(
req
);
// 输出json格式的字符串回包,或者取出单个值:System.out.println(res.getRequestId());
System
.
out
.
println
(
SendSmsResponse
.
toJsonString
(
res
));
log
.
info
(
"腾讯云-短信发送结果:{}"
,
SendSmsResponse
.
toJsonString
(
res
));
return
res
;
}
catch
(
TencentCloudSDKException
e
)
{
log
.
error
(
"腾讯云-短信服务,SMS发送异常:{}"
,
e
.
getMessage
());
throw
new
ParamException
(
RCode
.
PHONE_PARAM
_ERROR
,
null
);
throw
new
ParamException
(
RCode
.
TENCENT_SMS
_ERROR
,
null
);
}
}
}
src/main/resources/application.yml
浏览文件 @
71bacad6
...
...
@@ -43,8 +43,8 @@ tengxunyun:
# 应用ID
sdk_app_id_defult
:
1400903035
# 模板名称
sign_name
:
北京王小卤
sign_name
_a
:
北京王小卤
# 模板ID
template_id
:
2127434
template_id
_a
:
2127434
# 验证码过期时间(分钟)
overdue_long
:
5
\ No newline at end of file
src/test/java/com/wangxiaolu/promotion/controller/tengxunyun/TencentCoreControllerTest.java
0 → 100644
浏览文件 @
71bacad6
package
com
.
wangxiaolu
.
promotion
.
controller
.
tengxunyun
;
import
org.junit.jupiter.api.Test
;
import
org.junit.runner.RunWith
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.boot.test.context.SpringBootTest
;
import
org.springframework.test.context.junit4.SpringRunner
;
import
java.util.HashMap
;
import
java.util.Map
;
import
static
org
.
junit
.
jupiter
.
api
.
Assertions
.*;
/**
* @author : liqiulin
* @date : 2024-04-15 14
* @describe :
*/
@SpringBootTest
@RunWith
(
SpringRunner
.
class
)
class
TencentCoreControllerTest
{
@Autowired
TencentCoreController
tencentCoreController
;
@Test
void
sendSms
()
{
Map
<
String
,
String
>
phoneInfo
=
new
HashMap
<>();
phoneInfo
.
put
(
"phone"
,
"15701654502"
);
tencentCoreController
.
sendSms
(
phoneInfo
);
}
}
\ No newline at end of file
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论