【实例简介】
采用SpringBoot、HTML、JqueryUI、mysql实现的一整套进销存ERP系统,功能比较齐全,能立刻部署运行支持普通公司的ERP运营,附带测试使用的数据库,后管默认登录账号:admin 密码:123456
文件:590m.com/f/25127180-488778732-3b5ac3(访问密码:551685)
【实例截图】
【核心代码】
package com.jsh.erp.controller; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.jsh.erp.constants.BusinessConstants; import com.jsh.erp.constants.ExceptionConstants; import com.jsh.erp.datasource.entities.Account; import com.jsh.erp.datasource.vo.AccountVo4InOutList; import com.jsh.erp.exception.BusinessRunTimeException; import com.jsh.erp.service.account.AccountService; import com.jsh.erp.utils.BaseResponseInfo; import com.jsh.erp.utils.ErpInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.math.BigDecimal; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.jsh.erp.utils.ResponseJsonUtil.returnJson; /** * @author jishenghua 752718920 / @RestController @RequestMapping(value = “/account”) public class AccountController { private Logger logger = LoggerFactory.getLogger(AccountController.class); @Resource private AccountService accountService; /* * 查找结算账户信息-下拉框 * @param request * @return / @GetMapping(value = “/findBySelect”) public String findBySelect(HttpServletRequest request) throws Exception {
String res = null; try {
List dataList = accountService.findBySelect(); //存放数据json数组 JSONArray dataArray = new JSONArray(); if (null != dataList) { for (Account account : dataList) {
JSONObject item = new JSONObject();
item.put(“Id”, account.getId()); //结算账户名称 item.put(“AccountName”, account.getName());
dataArray.add(item);
}
}
res = dataArray.toJSONString();
} catch(Exception e){
e.printStackTrace();
res = “获取数据失败”;
} return res;
} /* * 获取所有结算账户 * @param request * @return / @GetMapping(value = “/getAccount”) public BaseResponseInfo getAccount(HttpServletRequest request) throws Exception {
BaseResponseInfo res = new BaseResponseInfo();
Map map = new HashMap(); try {
List accountList = accountService.getAccount();
map.put(“accountList”, accountList);
res.code = 200;
res.data = map;
} catch(Exception e){
e.printStackTrace();
res.code = 500;
res.data = “获取数据失败”;
} return res;
} /* * 账户流水信息 * @param currentPage * @param pageSize * @param accountId * @param initialAmount * @param request * @return / @GetMapping(value = “/findAccountInOutList”) public BaseResponseInfo findAccountInOutList(@RequestParam(“currentPage”) Integer currentPage, @RequestParam(“pageSize”) Integer pageSize, @RequestParam(“accountId”) Long accountId, @RequestParam(“initialAmount”) BigDecimal initialAmount,
HttpServletRequest request) throws Exception{
BaseResponseInfo res = new BaseResponseInfo();
Map map = new HashMap(); try {
List dataList = accountService.findAccountInOutList(accountId, (currentPage-1)pageSize, pageSize); int total = accountService.findAccountInOutListCount(accountId);
map.put(“total”, total); //存放数据json数组 JSONArray dataArray = new JSONArray(); if (null != dataList) { for (AccountVo4InOutList aEx : dataList) {
String timeStr = aEx.getOperTime().toString();
BigDecimal balance = accountService.getAccountSum(accountId, timeStr, “date”).add(accountService.getAccountSumByHead(accountId, timeStr, “date”))
.add(accountService.getAccountSumByDetail(accountId, timeStr, “date”)).add(accountService.getManyAccountSum(accountId, timeStr, “date”)).add(initialAmount);
aEx.setBalance(balance);
dataArray.add(aEx);
}
}
map.put(“rows”, dataArray);
res.code = 200;
res.data = map;
} catch(Exception e){
e.printStackTrace();
res.code = 500;
res.data = “获取数据失败”;
} return res;
} @PostMapping(value = “/updateAmountIsDefault”) public String updateAmountIsDefault(@RequestParam(“isDefault”) Boolean isDefault, @RequestParam(“accountId”) Long accountId,
HttpServletRequest request) throws Exception{
Map objectMap = new HashMap(); int res = accountService.updateAmountIsDefault(isDefault, accountId); if(res > 0) { return returnJson(objectMap, ErpInfo.OK.name, ErpInfo.OK.code);
} else { return returnJson(objectMap, ErpInfo.ERROR.name, ErpInfo.ERROR.code);
}
} / * create by: qiankunpingtai * website:
https://qiankunpingtai.cn
* description: * 批量删除账户信息 * create time: 2019/3/29 10:49 * @Param: ids * @return java.lang.Object */ @RequestMapping(value = “/batchDeleteAccountByIds”) public Object batchDeleteAccountByIds(@RequestParam(“ids”) String ids,@RequestParam(value=“deleteType”,
required =false,defaultValue=BusinessConstants.DELETE_TYPE_NORMAL)String deleteType) throws Exception {
JSONObject result = ExceptionConstants.standardSuccess(); /** * create by: qiankunpingtai * create time: 2019/4/10 10:19 * website:https://qiankunpingtai.cn * description: * 出于兼容性考虑,没有传递删除类型时,默认为正常删除 */ int i=0; if(BusinessConstants.DELETE_TYPE_NORMAL.equals(deleteType)){
i= accountService.batchDeleteAccountByIdsNormal(ids);
}else if(BusinessConstants.DELETE_TYPE_FORCE.equals(deleteType)){
i= accountService.batchDeleteAccountByIds(ids);
}else{ logger.error(“异常码[{}],异常提示[{}],参数,ids[{}],deleteType[{}]”,
ExceptionConstants.DELETE_REFUSED_CODE,ExceptionConstants.DELETE_REFUSED_MSG,ids,deleteType); throw new BusinessRunTimeException(ExceptionConstants.DELETE_REFUSED_CODE,
ExceptionConstants.DELETE_REFUSED_MSG);
} if(i<1){ logger.error(“异常码[{}],异常提示[{}],参数,ids[{}]”,
ExceptionConstants.ACCOUNT_DELETE_FAILED_CODE,ExceptionConstants.ACCOUNT_DELETE_FAILED_MSG,ids); throw new BusinessRunTimeException(ExceptionConstants.ACCOUNT_DELETE_FAILED_CODE,
ExceptionConstants.ACCOUNT_DELETE_FAILED_MSG);
} return result;
}
}
package com.jsh.erp.controller; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import com.jsh.erp.constants.BusinessConstants; import com.jsh.erp.constants.ExceptionConstants; import com.jsh.erp.datasource.entities.Tenant; import com.jsh.erp.datasource.entities.User; import com.jsh.erp.datasource.entities.UserEx; import com.jsh.erp.datasource.vo.TreeNodeEx; import com.jsh.erp.exception.BusinessParamCheckingException; import com.jsh.erp.service.log.LogService; import com.jsh.erp.service.tenant.TenantService; import com.jsh.erp.service.user.UserService; import com.jsh.erp.utils.; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.net.URLEncoder; import java.security.NoSuchAlgorithmException; import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import static com.jsh.erp.utils.ResponseJsonUtil.returnJson; /** * @author ji_sheng_hua 华夏erp */ @RestController @RequestMapping(value = “/user”) public class UserController { private Logger logger = LoggerFactory.getLogger(UserController.class); @Value(“${manage.roleId}”) private Integer manageRoleId; @Resource private UserService userService; @Resource private TenantService tenantService; @Resource private LogService logService; private static String message = “成功”; private static final String HTTP = “http://”; private static final String CODE_OK = “200”;
@PostMapping(value = “/login”) public BaseResponseInfo login(@RequestParam(value = “loginame”, required = false) String loginame,
@RequestParam(value = “password”, required = false) String password,
HttpServletRequest request)throws Exception {
logger.info(“============用户登录 login 方法调用开始==============”);
String msgTip = “”;
User user=null;
BaseResponseInfo res = new BaseResponseInfo(); try {
String username = loginame.trim();
password = password.trim(); //判断用户是否已经登录过,登录过不再处理 Object userInfo = request.getSession().getAttribute(“user”);
User sessionUser = new User(); if (userInfo != null) {
sessionUser = (User) userInfo;
} if (sessionUser != null && username.equalsIgnoreCase(sessionUser.getLoginame())) {
logger.info(“====用户 ” username “已经登录过, login 方法调用结束====”);
msgTip = “user already login”;
} //获取用户状态 int userStatus = -1; try {
userStatus = userService.validateUser(username, password);
} catch (Exception e) {
e.printStackTrace();
logger.error(“>>>>>>>>>>>>>用户 ” username ” 登录 login 方法 访问服务层异常====”, e);
msgTip = “access service exception”;
} switch (userStatus) { case ExceptionCodeConstants.UserExceptionCode.USER_NOT_EXIST:
msgTip = “user is not exist”; break; case ExceptionCodeConstants.UserExceptionCode.USER_PASSWORD_ERROR:
msgTip = “user password error”; break; case ExceptionCodeConstants.UserExceptionCode.BLACK_USER:
msgTip = “user is black”; break; case ExceptionCodeConstants.UserExceptionCode.USER_ACCESS_EXCEPTION:
msgTip = “access service error”; break; default: try {
msgTip = “user can login”; //验证通过 ,可以登录,放入session,记录登录日志 user = userService.getUserByUserName(username);
request.getSession().setAttribute(“user”,user); if(user.getTenantId()!=null) {
Tenant tenant = tenantService.getTenantByTenantId(user.getTenantId()); if(tenant!=null) {
Long tenantId = tenant.getTenantId();
Integer userNumLimit = tenant.getUserNumLimit();
Integer billsNumLimit = tenant.getBillsNumLimit(); if(tenantId!=null) {
request.getSession().setAttribute(“tenantId”,tenantId); //租户tenantId request.getSession().setAttribute(“userNumLimit”,userNumLimit); //用户限制数 request.getSession().setAttribute(“billsNumLimit”,billsNumLimit); //单据限制数 }
}
}
logService.insertLog(“用户”, new StringBuffer(BusinessConstants.LOG_OPERATION_TYPE_LOGIN).append(user.getId()).toString(),
((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest());
} catch (Exception e) {
e.printStackTrace();
logger.error(“>>>>>>>>>>>>>>>查询用户名为:” username ” ,用户信息异常”, e);
} break;
}
Map<String, Object> data = new HashMap<String, Object>();
data.put(“msgTip”, msgTip); /** * 在IE模式下,无法获取到user数据, * 在此处明确添加上user信息 * */ if(user!=null){
data.put(“user”,user);
}
res.code = 200;
res.data = data;
logger.info(“===============用户登录 login 方法调用结束===============”);
} catch(Exception e){
e.printStackTrace();
logger.error(e.getMessage());
res.code = 500;
res.data = “用户登录失败”;
} return res;
}
@GetMapping(value = “/getUserSession”) public BaseResponseInfo getSessionUser(HttpServletRequest request)throws Exception {
BaseResponseInfo res = new BaseResponseInfo(); try {
Map<String, Object> data = new HashMap<String, Object>();
Object userInfo = request.getSession().getAttribute(“user”); if(userInfo!=null) {
User user = (User) userInfo;
user.setPassword(null);
data.put(“user”, user);
}
res.code = 200;
res.data = data;
} catch(Exception e){
e.printStackTrace();
res.code = 500;
res.data = “获取session失败”;
} return res;
}
@GetMapping(value = “/logout”) public BaseResponseInfo logout(HttpServletRequest request, HttpServletResponse response)throws Exception {
BaseResponseInfo res = new BaseResponseInfo(); try {
request.getSession().removeAttribute(“user”);
request.getSession().removeAttribute(“tenantId”);
request.getSession().removeAttribute(“userNumLimit”);
request.getSession().removeAttribute(“billsNumLimit”);
response.sendRedirect(“/login.html”);
} catch(Exception e){
e.printStackTrace();
res.code = 500;
res.data = “退出失败”;
} return res;
}
@PostMapping(value = “/resetPwd”) public String resetPwd(@RequestParam(“id”) Long id,
HttpServletRequest request) throws Exception {
Map<String, Object> objectMap = new HashMap<String, Object>();
String password = “123456”;
String md5Pwd = Tools.md5Encryp(password); int update = userService.resetPwd(md5Pwd, id); if(update > 0) { return returnJson(objectMap, message, ErpInfo.OK.code);
} else { return returnJson(objectMap, message, ErpInfo.ERROR.code);
}
}
@PostMapping(value = “/updatePwd”) public String updatePwd(@RequestParam(“userId”) Long userId, @RequestParam(“password”) String password,
@RequestParam(“oldpwd”) String oldpwd, HttpServletRequest request)throws Exception {
Integer flag = 0;
Map<String, Object> objectMap = new HashMap<String, Object>(); try {
User user = userService.getUser(userId);
String oldPassword = Tools.md5Encryp(oldpwd);
String md5Pwd = Tools.md5Encryp(password); //必须和原始密码一致才可以更新密码 if(user.getLoginame().equals(“jsh”)){
flag = 3; //管理员jsh不能修改密码 } else if (oldPassword.equalsIgnoreCase(user.getPassword())) {
user.setPassword(md5Pwd);
flag = userService.updateUserByObj(user); //1-成功 } else {
flag = 2; //原始密码输入错误 }
objectMap.put(“status”, flag); if(flag > 0) { return returnJson(objectMap, message, ErpInfo.OK.code);
} else { return returnJson(objectMap, message, ErpInfo.ERROR.code);
}
} catch (Exception e) {
logger.error(“>>>>>>>>>>>>>修改用户ID为 : ” userId “密码信息失败”, e);
flag = 3;
objectMap.put(“status”, flag); return returnJson(objectMap, message, ErpInfo.ERROR.code);
}
} /** * 获取全部用户数据列表 * @param request * @return */ @GetMapping(value = “/getAllList”) public BaseResponseInfo getAllList(HttpServletRequest request)throws Exception {
BaseResponseInfo res = new BaseResponseInfo(); try {
Map<String, Object> data = new HashMap<String, Object>();
List<User> dataList = userService.getUser(); if(dataList!=null) {
data.put(“userList”, dataList);
}
res.code = 200;
res.data = data;
} catch(Exception e){
e.printStackTrace();
res.code = 500;
res.data = “获取失败”;
} return res;
} /** * create by: cjl * description: * 查询分页用户列表 * create time: 2019/3/8 15:08 * @Param: pageSize * @Param: currentPage * @Param: search * @return java.lang.String */ @GetMapping(value = “/getUserList”) public String getUserList(@RequestParam(value = Constants.PAGE_SIZE, required = false) Integer pageSize,
@RequestParam(value = Constants.CURRENT_PAGE, required = false) Integer currentPage,
@RequestParam(value = Constants.SEARCH, required = false) String search)throws Exception {
Map<String, Object> parameterMap = new HashMap<String, Object>(); //查询参数 JSONObject obj= JSON.parseObject(search);
Set<String> key= obj.keySet(); for(String keyEach: key){
parameterMap.put(keyEach,obj.getString(keyEach));
}
PageQueryInfo queryInfo = new PageQueryInfo();
Map<String, Object> objectMap = new HashMap<String, Object>(); if (pageSize == null || pageSize <= 0) {
pageSize = BusinessConstants.DEFAULT_PAGINATION_PAGE_SIZE;
} if (currentPage == null || currentPage <= 0) {
currentPage = BusinessConstants.DEFAULT_PAGINATION_PAGE_NUMBER;
}
PageHelper.startPage(currentPage,pageSize,true);
List<UserEx> list = userService.getUserList(parameterMap); //获取分页查询后的数据 PageInfo<UserEx> pageInfo = new PageInfo<>(list);
objectMap.put(“page”, queryInfo); if (list == null) {
queryInfo.setRows(new ArrayList<Object>());
queryInfo.setTotal(BusinessConstants.DEFAULT_LIST_NULL_NUMBER); return returnJson(objectMap, “查找不到数据”, ErpInfo.OK.code);
}
queryInfo.setRows(list);
queryInfo.setTotal(pageInfo.getTotal()); return returnJson(objectMap, ErpInfo.OK.name, ErpInfo.OK.code);
} /** * create by: cjl * description: * 新增用户及机构和用户关系 * create time: 2019/3/8 16:06 * @Param: beanJson * @return java.lang.Object */ @PostMapping(“/addUser”)
@ResponseBody public Object addUser(@RequestParam(“info”) String beanJson, HttpServletRequest request)throws Exception{
JSONObject result = ExceptionConstants.standardSuccess();
Long userNumLimit = Long.parseLong(request.getSession().getAttribute(“userNumLimit”).toString());
Long count = userService.countUser(null,null); if(count>= userNumLimit) { throw new BusinessParamCheckingException(ExceptionConstants.USER_OVER_LIMIT_FAILED_CODE,
ExceptionConstants.USER_OVER_LIMIT_FAILED_MSG);
} else {
UserEx ue= JSON.parseObject(beanJson, UserEx.class);
userService.addUserAndOrgUserRel(ue);
} return result;
} /** * 注册用户 * @param loginame * @param password * @return * @throws Exception */ @PostMapping(value = “/registerUser”) public Object registerUser(@RequestParam(value = “loginame”, required = false) String loginame,
@RequestParam(value = “password”, required = false) String password,
HttpServletRequest request)throws Exception{
JSONObject result = ExceptionConstants.standardSuccess();
UserEx ue= new UserEx();
ue.setUsername(loginame);
ue.setLoginame(loginame);
ue.setPassword(password);
userService.checkUserNameAndLoginName(ue); //检查用户名和登录名 ue = userService.registerUser(ue,manageRoleId,request); return result;
} /** * create by: cjl * description: * 修改用户及机构和用户关系 * create time: 2019/3/8 16:06 * @Param: beanJson * @return java.lang.Object */ @PostMapping(“/updateUser”)
@ResponseBody public Object updateUser(@RequestParam(“info”) String beanJson,@RequestParam(“id”) Long id)throws Exception{
JSONObject result = ExceptionConstants.standardSuccess();
UserEx ue= JSON.parseObject(beanJson, UserEx.class);
ue.setId(id);
userService.updateUserAndOrgUserRel(ue); return result;
}
@PostMapping(“/deleteUser”)
@ResponseBody public Object deleteUser(@RequestParam(“ids”) String ids)throws Exception{
JSONObject result = ExceptionConstants.standardSuccess();
userService.batDeleteUser(ids); return result;
}
@PostMapping(“/batchDeleteUser”)
@ResponseBody public Object batchDeleteUser(@RequestParam(“ids”) String ids)throws Exception{
JSONObject result = ExceptionConstants.standardSuccess();
userService.batDeleteUser(ids); return result;
}
@RequestMapping(“/getOrganizationUserTree”) public JSONArray getOrganizationUserTree()throws Exception{
JSONArray arr=new JSONArray();
List<TreeNodeEx> organizationUserTree= userService.getOrganizationUserTree(); if(organizationUserTree!=null&&organizationUserTree.size()>0){ for(TreeNodeEx node:organizationUserTree){
String str=JSON.toJSONString(node);
JSONObject obj=JSON.parseObject(str);
arr.add(obj) ;
}
} return arr;
}
}
以下内容无关:
——————————————-分割线———————————————
环境信息:
mysql:5.7.32
seata-server:1.4.1
SpringCloud:Hoxton.SR10
SpringBoot:2.3.8.RELEASE
注册中心:Eureka
涉及服务:
Seata-server
1、在file.conf中修改
mode = “db”
然后配置DB信息:
database store property
db {
datasource = “druid”
## mysql/oracle/postgresql/h2/oceanbase etc.
dbType = “mysql”
driverClassName = “com.mysql.jdbc.Driver”
url = “jdbc:mysql://127.0.0.1:3306/seata”
user = “root”
password = “123456”
minConn = 5
maxConn = 100
globalTable = “global_table”
branchTable = “branch_table”
lockTable = “lock_table”
queryLimit = 100
maxWait = 5000
}
2、在register.conf
registry {
file 、nacos 、eureka、redis、zk、consul、etcd3、sofa
type = “eureka”
loadBalance = “RandomLoadBalance”
loadBalanceVirtualNodes = 10
eureka {
serviceUrl = “
http://eureka-chengdu:8761/eureka,http://eureka-hangzhou:8762/eureka
”
application = “seata-server”
weight = “1”
}
3、客户端修改
这里所指的客户端包含所有的资源管理器,包含所有需要seata-server管理的服务
在服务启动yml中增加:
seata:
enabled: true
事务群组(可以每个应用独立取名,也可以使用相同的名字)
tx-service-group: my_tx_group
client:
rm-report-success-enable: true
# 异步提交缓存队列长度(默认10000)
rm-async-commit-buffer-limit: 1000
# 一阶段全局提交结果上报TC重试次数(默认1次,建议大于1)
tm-commit-retry-count: 3
# 一阶段全局回滚结果上报TC重试次数(默认1次,建议大于1)
tm-rollback-retry-count: 3
support:
# 数据源自动代理开关(默认false关闭)
spring-datasource-autoproxy: false
service:
vgroup-mapping:
# TC 集群(必须与seata-server保持一致)
my_tx_group: seata-server
grouplist:
default: seata-server:8091
registry:
type: eureka
eureka:
serviceUrl:
http://eureka-chengdu:8761/eureka/,http://eureka-hangzhou:8762/eureka/
TCC模式
TCC模式实践需要四个服务,除了seata-server外,其他服务调用关系如下:
business服务是全局事务的发起者,需要增加@GlobalTransactional注解
@Override
@GlobalTransactional
public String processTcc(Map params) {
String xid = RootContext.getXID();
System.out.println((“—》》》》xid:” + xid));
uploadFeign.upload(params);
downloadFeign.download(params);
return xid;
}
business服务会通过feign远程调用upload和download服务,这两个服务都要声明TCC的三个接口,并通过TwoPhaseBusinessAction注解声明。
upload服务:
@LocalTCC
public interface TccService {
@TwoPhaseBusinessAction(name = “upload”, commitMethod = “commitTcc”, rollbackMethod = “cancel”)
String upload(@BusinessActionContextParameter(paramName = “params”) Map<String, String> params);
boolean commitTcc(BusinessActionContext context);
boolean cancel(BusinessActionContext context);
}
具体实现,这里模拟了TCC结果并放到Result中,通过restful接口可以查看,实际业务需要考虑防悬挂和空回滚问题,例子只是简单描述如何使用TCC模式:
@Slf4j
@Service
public class TccServiceImpl implements TccService {
@Value(“${spring.application.name}”)
private String appName;
@PostConstruct
private void initAppName() {
Result.getResult().setAppName(appName);
}
@Override
public String upload(Map<String, String> params) {
String xid = RootContext.getXID();
System.out.println((“—》》》》xid: ” + xid));
return “success”;
}
@Override
public boolean commitTcc(BusinessActionContext context) {
String xbid = context.getXid();
System.out.println((“—》》》》xid: ” + xbid + “提交成功”));
Result.getResult().setActionResult(context.getXid(), +context.getBranchId(), “Commit”, context.getActionContext(“params”));
return true;
}
@Override
public boolean cancel(BusinessActionContext context) {
System.out.println((“—》》》》xid: ” + context.getXid() + “回滚成功”));
Result.getResult().setActionResult(context.getXid(), context.getBranchId(), “Rollback”, context.getActionContext(“params”));
return true;
}
}
download服务
download服务也同样需要声明一个TCC接口,实现上在Try阶段模拟每三次调用,,延迟30s失败一次场景
@Override
public String download(Map params) {
String xid = RootContext.getXID();
System.out.println((“—》》》》xid: ” + xid));
if (count.incrementAndGet() % 3 == 0) {
try {
TimeUnit.SECONDS.sleep(30);
} catch (InterruptedException e) {
log.warn(“InterruptedException”, e);
}
throw new RuntimeException(“服务异常”);
}
return “success”;
}
测试
1、通过restful接口调用两次,返回全局事务ID
2、查看download和upload服务结果,可以看到结果都是成功。