|
@@ -0,0 +1,312 @@
|
|
|
|
|
+package com.cyksj.web.controller.cps;
|
|
|
|
|
+
|
|
|
|
|
+import cn.hutool.core.date.DateTime;
|
|
|
|
|
+import cn.hutool.core.date.DateUtil;
|
|
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
|
|
+import cn.hutool.json.JSONObject;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
|
|
|
|
+import com.cyksj.common.annotation.NoSubmit;
|
|
|
|
|
+import com.cyksj.common.exception.BusinessRuntimeException;
|
|
|
|
|
+import com.cyksj.common.snowflake.Sequence;
|
|
|
|
|
+import com.cyksj.common.util.Codec;
|
|
|
|
|
+import com.cyksj.common.util.Jsons;
|
|
|
|
|
+import com.cyksj.config.wxlogin.WxOpenPlatYHLXLoginConfig;
|
|
|
|
|
+import com.cyksj.dto.Result;
|
|
|
|
|
+import com.cyksj.enums.GatewayResponse;
|
|
|
|
|
+import com.cyksj.mapper.GoodsDonMapper;
|
|
|
|
|
+import com.cyksj.mapper.UserMapper;
|
|
|
|
|
+import com.cyksj.mapper.shop.YhShopMapper;
|
|
|
|
|
+import com.cyksj.model.dto.AuthRedirect;
|
|
|
|
|
+import com.cyksj.model.dto.AuthToken;
|
|
|
|
|
+import com.cyksj.model.dto.CpsAuthDto;
|
|
|
|
|
+import com.cyksj.model.dto.GzhOAuth2UserInfo;
|
|
|
|
|
+import com.cyksj.model.entity.GoodsDon;
|
|
|
|
|
+import com.cyksj.model.entity.User;
|
|
|
|
|
+import com.cyksj.model.entity.YhShopDistributeMoneyApply;
|
|
|
|
|
+import com.cyksj.model.entity.YhShopDistributeWaitingSendPoints;
|
|
|
|
|
+import com.cyksj.model.manage.entity.YhShop;
|
|
|
|
|
+import com.cyksj.model.request.LoginReq;
|
|
|
|
|
+import com.cyksj.model.views.*;
|
|
|
|
|
+import com.cyksj.redis.RedisService;
|
|
|
|
|
+import com.cyksj.service.authorization.AuthorizationService;
|
|
|
|
|
+import com.cyksj.service.cps.shop.YhShopManageService;
|
|
|
|
|
+import com.cyksj.service.wechat.WeChatService;
|
|
|
|
|
+import com.cyksj.web.util.StpUserUtil;
|
|
|
|
|
+import com.cyksj.web.util.StpYhShopManageUser;
|
|
|
|
|
+import com.ejlchina.searcher.BeanSearcher;
|
|
|
|
|
+import com.ejlchina.searcher.SearchResult;
|
|
|
|
|
+import com.ejlchina.searcher.util.MapBuilder;
|
|
|
|
|
+import com.ejlchina.searcher.util.MapUtils;
|
|
|
|
|
+import lombok.RequiredArgsConstructor;
|
|
|
|
|
+import org.springframework.web.bind.annotation.*;
|
|
|
|
|
+
|
|
|
|
|
+import javax.servlet.http.HttpServletRequest;
|
|
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
|
|
+import java.io.IOException;
|
|
|
|
|
+import java.math.BigDecimal;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+import java.util.Optional;
|
|
|
|
|
+
|
|
|
|
|
+/*
|
|
|
|
|
+ *项目名: yhlxj
|
|
|
|
|
+ *文件名: YhShopController
|
|
|
|
|
+ *创建者: JavaZou
|
|
|
|
|
+ *创建时间:2023/9/19 17:34
|
|
|
|
|
+ */
|
|
|
|
|
+@RestController
|
|
|
|
|
+@RequestMapping("/yhShop/manage")
|
|
|
|
|
+@RequiredArgsConstructor
|
|
|
|
|
+public class YhShopLoginController {
|
|
|
|
|
+ private final YhShopManageService yhShopManageService;
|
|
|
|
|
+
|
|
|
|
|
+ private final BeanSearcher beanSearcher;
|
|
|
|
|
+
|
|
|
|
|
+ private final HttpServletRequest request;
|
|
|
|
|
+
|
|
|
|
|
+ private final GoodsDonMapper goodsDonMapper;
|
|
|
|
|
+
|
|
|
|
|
+ private final RedisService redisService;
|
|
|
|
|
+
|
|
|
|
|
+ private final static Sequence SEQUENCE = new Sequence(0);
|
|
|
|
|
+
|
|
|
|
|
+ private final WxOpenPlatYHLXLoginConfig wxOpenPlatYHLXLoginConfig;
|
|
|
|
|
+
|
|
|
|
|
+ private final AuthorizationService authorizationService;
|
|
|
|
|
+
|
|
|
|
|
+ private final WeChatService weChatService;
|
|
|
|
|
+
|
|
|
|
|
+ private final UserMapper userMapper;
|
|
|
|
|
+
|
|
|
|
|
+ private final YhShopMapper yhShopMapper;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 获取登录验证码
|
|
|
|
|
+ */
|
|
|
|
|
+ @GetMapping("/get/LoginCode")
|
|
|
|
|
+ @NoSubmit
|
|
|
|
|
+ public Result getLoginCode(@RequestParam(defaultValue = "1") Integer type, String login, Integer cid) {
|
|
|
|
|
+ yhShopManageService.getLoginCode(type, login, cid);
|
|
|
|
|
+ return GatewayResponse.SUCCESS.newBuilder().toResult();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 跳转店铺后台 是否是店铺主
|
|
|
|
|
+ */
|
|
|
|
|
+ @GetMapping("/redirect")
|
|
|
|
|
+ public void yhShopManageRedirect(String redirectUrl, HttpServletResponse response) throws IOException {
|
|
|
|
|
+ //银河官网token值
|
|
|
|
|
+ long userId = StpUserUtil.getLoginIdAsLong();
|
|
|
|
|
+ yhShopManageService.yhShopManageRedirect(userId);
|
|
|
|
|
+ //校验通过重定向
|
|
|
|
|
+ StpYhShopManageUser.login(userId);
|
|
|
|
|
+ response.sendRedirect(redirectUrl.contains("?") ? redirectUrl + "&t=" + StpYhShopManageUser.getTokenValue() : redirectUrl + "?t=" + StpYhShopManageUser.getTokenValue());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 手机号、邮箱登录
|
|
|
|
|
+ */
|
|
|
|
|
+ @PostMapping("/login")
|
|
|
|
|
+ public Result login(@RequestBody LoginReq loginReq) {
|
|
|
|
|
+ User user = yhShopManageService.login(loginReq.getType(), loginReq.getLogin(), loginReq.getCode());
|
|
|
|
|
+ StpYhShopManageUser.login(user.getId());
|
|
|
|
|
+ String yhShopToken = StpYhShopManageUser.getTokenValue();
|
|
|
|
|
+ JSONObject re = new JSONObject();
|
|
|
|
|
+ re.putOpt("satoken-yhShop", yhShopToken);
|
|
|
|
|
+ re.putOpt("img", user.getHeadimgurl());
|
|
|
|
|
+ re.putOpt("nickname", user.getNickname());
|
|
|
|
|
+ return GatewayResponse.SUCCESS.newBuilder().toResult(re.toString());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 小店后台微信授权登录
|
|
|
|
|
+ */
|
|
|
|
|
+ @GetMapping("/wx/authLogin")
|
|
|
|
|
+ public Result<CpsAuthDto> wxAuthorize() throws Exception {
|
|
|
|
|
+ Object authUrlObj = redisService.get(RedisService.key.WX_LOGIN_AUTH_URI.getNameFormat("yhShop", wxOpenPlatYHLXLoginConfig.getAppId()));
|
|
|
|
|
+ if (authUrlObj != null) {
|
|
|
|
|
+ CpsAuthDto cpsAuthDto = Jsons.parseObject(authUrlObj.toString(), CpsAuthDto.class);
|
|
|
|
|
+ return GatewayResponse.SUCCESS.newBuilder().toResult(cpsAuthDto);
|
|
|
|
|
+ }
|
|
|
|
|
+ CpsAuthDto cpsAuthDto = new CpsAuthDto();
|
|
|
|
|
+ Long mappingId = SEQUENCE.nextId();
|
|
|
|
|
+ String authState = mappingId.toString();
|
|
|
|
|
+ redisService.set(authState, authState, 60 * 60 * 24L);
|
|
|
|
|
+ AuthRedirect redirect = new AuthRedirect().setMappingId(mappingId);
|
|
|
|
|
+ String state = Codec.DoBase64.custom().setData(Jsons.toJson(redirect)).encodeAsText();
|
|
|
|
|
+ String wxAuthUrl = authorizationService.createWxAuthLoginUrl(state, wxOpenPlatYHLXLoginConfig.getYhShopRedirectUri());
|
|
|
|
|
+ cpsAuthDto.setAuthUrl(wxAuthUrl);
|
|
|
|
|
+ cpsAuthDto.setAuthState(authState);
|
|
|
|
|
+ redisService.set(RedisService.key.WX_LOGIN_AUTH_URI.getNameFormat("yhShop", wxOpenPlatYHLXLoginConfig.getAppId()), Jsons.toJson(cpsAuthDto), RedisService.key.WX_LOGIN_AUTH_URI.getTimeout());
|
|
|
|
|
+ return GatewayResponse.SUCCESS.newBuilder().toResult(cpsAuthDto);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 网页微信授权后跳转
|
|
|
|
|
+ */
|
|
|
|
|
+ @GetMapping("/wx/authLogin/baseRedirect")
|
|
|
|
|
+ public void wxAuthLoginRedirect(String code, String state, HttpServletResponse response) throws Exception {
|
|
|
|
|
+ if (StrUtil.isEmpty(state)) throw BusinessRuntimeException.getInstance("授权错误,请重新授权");
|
|
|
|
|
+ String json = Codec.DoBase64.custom().setData(state).decodeAsText();
|
|
|
|
|
+ AuthRedirect re = Jsons.parseObject(json, AuthRedirect.class);
|
|
|
|
|
+ AuthToken token = weChatService.getAuthToken(code, wxOpenPlatYHLXLoginConfig.getAppId());
|
|
|
|
|
+ GzhOAuth2UserInfo userinfo = weChatService.getUserInfo(token.getAccessToken(), token.getOpenid());
|
|
|
|
|
+ String unionId = userinfo.getUnionid();
|
|
|
|
|
+ String openId = userinfo.getOpenid();
|
|
|
|
|
+ User user = Optional.ofNullable(
|
|
|
|
|
+ userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getUnionid, unionId)
|
|
|
|
|
+ .orderByAsc(User::getId)
|
|
|
|
|
+ .last("limit 1")))
|
|
|
|
|
+ .orElseGet(() -> userMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getOpenId, openId)
|
|
|
|
|
+ .orderByAsc(User::getId)
|
|
|
|
|
+ .last("limit 1")));
|
|
|
|
|
+ if (user == null) throw BusinessRuntimeException.getInstance("用户信息不存在");
|
|
|
|
|
+ YhShop yhShop = yhShopMapper.selectOne(Wrappers.lambdaQuery(YhShop.class)
|
|
|
|
|
+ .eq(YhShop::getVerifyStatus, YhShop.Status.success)
|
|
|
|
|
+ .eq(YhShop::getUserId, user.getId()).last("limit 1"));
|
|
|
|
|
+ if (yhShop == null) {
|
|
|
|
|
+ throw BusinessRuntimeException.getInstance("您不是银河小店店铺主");
|
|
|
|
|
+ }
|
|
|
|
|
+ JSONObject res = new JSONObject();
|
|
|
|
|
+ StpYhShopManageUser.login(user.getId());
|
|
|
|
|
+ String cpsToken = StpYhShopManageUser.getTokenValue();
|
|
|
|
|
+ res.putOpt("satoken-yhShop", cpsToken);
|
|
|
|
|
+ res.putOpt("img", user.getHeadimgurl());
|
|
|
|
|
+ res.putOpt("nickname", user.getNickname());
|
|
|
|
|
+ redisService.set(RedisService.key.YH_SHOP_AUTH_SUCCESS_KEY.getNameFormat(re.getMappingId()), res.toString(), RedisService.key.YH_SHOP_AUTH_SUCCESS_KEY.getTimeout());
|
|
|
|
|
+ response.sendRedirect(wxOpenPlatYHLXLoginConfig.getYhShopLoginUrl());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 店铺用户是否已授权
|
|
|
|
|
+ */
|
|
|
|
|
+ @GetMapping("/get/userInfo")
|
|
|
|
|
+ public Result<String> getCpsUserInfo(String authState) {
|
|
|
|
|
+ String key = RedisService.key.YH_SHOP_AUTH_SUCCESS_KEY.getNameFormat(authState);
|
|
|
|
|
+ Object o = redisService.get(key);
|
|
|
|
|
+ if (o == null) return GatewayResponse.SUCCESS.newBuilder().toResult();
|
|
|
|
|
+ redisService.del(key);
|
|
|
|
|
+ return GatewayResponse.SUCCESS.newBuilder().toResult(o.toString());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 店铺分销平台数据详情
|
|
|
|
|
+ */
|
|
|
|
|
+ @GetMapping("/get/distribute/detail")
|
|
|
|
|
+ public Result<YhShopUserDistributeDetailView> getDistributeDetail() {
|
|
|
|
|
+ long yhShopUserId = StpYhShopManageUser.getLoginIdAsLong();
|
|
|
|
|
+ User user = beanSearcher.searchFirst(User.class, MapUtils.builder().field(User::getId, yhShopUserId).onlySelect(User::getPoints).build());
|
|
|
|
|
+ BigDecimal wApplyMoney = user.getYhsMoney().divide(BigDecimal.valueOf(100));
|
|
|
|
|
+ //已提现
|
|
|
|
|
+ Number gotApplyMoney = beanSearcher.searchSum(YhShopDistributeMoneyApply.class, MapUtils.builder()
|
|
|
|
|
+ .field(YhShopDistributeMoneyApply::getUserId, yhShopUserId)
|
|
|
|
|
+ .field(YhShopDistributeMoneyApply::getVerifyStatus, YhShopDistributeMoneyApply.Status.success.name())
|
|
|
|
|
+ .build(), "money");
|
|
|
|
|
+ Number reMoneyPoints = beanSearcher.searchSum(YhShopDistributeWaitingSendPoints.class, MapUtils.builder().field(YhShopDistributeWaitingSendPoints::getSharedId, yhShopUserId).field(YhShopDistributeWaitingSendPoints::getSendStatus, YhShopDistributeWaitingSendPoints.Status.success.name()).build(), "money");
|
|
|
|
|
+ Number wMoneyPoints = beanSearcher.searchSum(YhShopDistributeWaitingSendPoints.class, MapUtils.builder().field(YhShopDistributeWaitingSendPoints::getSharedId, yhShopUserId).field(YhShopDistributeWaitingSendPoints::getSendStatus, YhShopDistributeWaitingSendPoints.Status.waiting.name()).build(), "money");
|
|
|
|
|
+
|
|
|
|
|
+ List<YhShopUserDistributeRateView> rateViews = beanSearcher.searchAll(YhShopUserDistributeRateView.class, MapUtils.builder()
|
|
|
|
|
+ .put("uid", String.format("and user_id = %s", yhShopUserId))
|
|
|
|
|
+ .orderBy(YhShopUserDistributeRateView::getRate).desc()
|
|
|
|
|
+ .build());
|
|
|
|
|
+ YhShopUserDistributeDetailView detailView = new YhShopUserDistributeDetailView();
|
|
|
|
|
+ detailView.setRateViews(rateViews);
|
|
|
|
|
+ detailView.setWMoney(BigDecimal.valueOf(wMoneyPoints.doubleValue()).divide(BigDecimal.valueOf(100)));
|
|
|
|
|
+ detailView.setReMoney(BigDecimal.valueOf(reMoneyPoints.doubleValue()).divide(BigDecimal.valueOf(100)));
|
|
|
|
|
+
|
|
|
|
|
+ detailView.setWApplyMoney(wApplyMoney);
|
|
|
|
|
+ detailView.setGotApplyMoney(BigDecimal.valueOf(gotApplyMoney.doubleValue()).divide(BigDecimal.valueOf(100)));
|
|
|
|
|
+ return GatewayResponse.SUCCESS.newBuilder().toResult(detailView);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 店铺待入账数据统计
|
|
|
|
|
+ */
|
|
|
|
|
+ @GetMapping("/get/wMoney/statisticsData")
|
|
|
|
|
+ public Result<YhShopBrokenLineCpsUserWMoneyView> getWMoneyStatisticsDataView(Long firstDate, Long lastDate) {
|
|
|
|
|
+ long yhShopUserId = StpYhShopManageUser.getLoginIdAsLong();
|
|
|
|
|
+ DateTime first = null;
|
|
|
|
|
+ DateTime end = null;
|
|
|
|
|
+ if (firstDate != null) {
|
|
|
|
|
+ first = DateUtil.date(firstDate);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (lastDate != null) {
|
|
|
|
|
+ end = DateUtil.date(lastDate);
|
|
|
|
|
+ }
|
|
|
|
|
+ YhShopBrokenLineCpsUserWMoneyView brokenLineCpsUserWMoneyView = Optional.ofNullable(yhShopManageService.getWMoneyStatisticsDate(yhShopUserId, first, end)).orElse(new YhShopBrokenLineCpsUserWMoneyView());
|
|
|
|
|
+ List<YhShopUserWMoneyStatisticsDataView> dataView = yhShopManageService.getWMoneyStatisticsDataView(yhShopUserId, first, end);
|
|
|
|
|
+ brokenLineCpsUserWMoneyView.setDataView(dataView);
|
|
|
|
|
+ return GatewayResponse.SUCCESS.newBuilder().toResult(brokenLineCpsUserWMoneyView);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 店铺分销订单收入统计
|
|
|
|
|
+ */
|
|
|
|
|
+ @GetMapping("/get/distribute/orders/statistics")
|
|
|
|
|
+ public Result<SearchResult<YhShopUserDistributeOrdersStatisticsView>> getUserDistributeOrdersStatisticsView(Long firstDate, Long lastDate) {
|
|
|
|
|
+ long yhShopUserId = StpYhShopManageUser.getLoginIdAsLong();
|
|
|
|
|
+ String first = null;
|
|
|
|
|
+ String end = null;
|
|
|
|
|
+ String timeSql = StrUtil.EMPTY;
|
|
|
|
|
+ if (firstDate != null) {
|
|
|
|
|
+ first = DateUtil.format(DateTime.of(firstDate), "yyyy-MM-dd HH:mm:ss");
|
|
|
|
|
+ timeSql += String.format(" and o.created_time >= '%s'", first);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (lastDate != null) {
|
|
|
|
|
+ end = DateUtil.format(DateTime.of(lastDate), "yyyy-MM-dd HH:mm:ss");
|
|
|
|
|
+ timeSql += String.format(" and o.created_time < '%s'", end);
|
|
|
|
|
+ }
|
|
|
|
|
+ MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap());
|
|
|
|
|
+ builder.put("shopUserId", String.format("shop_user_id = %s", yhShopUserId));
|
|
|
|
|
+ if (StrUtil.isNotEmpty(timeSql)) {
|
|
|
|
|
+ builder.put("time", timeSql);
|
|
|
|
|
+ }
|
|
|
|
|
+ SearchResult<YhShopUserDistributeOrdersStatisticsView> search = beanSearcher.search(YhShopUserDistributeOrdersStatisticsView.class, builder.build());
|
|
|
|
|
+ return GatewayResponse.SUCCESS.newBuilder().toResult(search);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 店铺分销订单收入详细
|
|
|
|
|
+ */
|
|
|
|
|
+ @GetMapping("/get/distribute/orders/detail")
|
|
|
|
|
+ public Result<SearchResult<YhShopUserDistributeOrdersDetailView>> getUserDistributeOrdersDetailView(Long firstDate, Long lastDate) {
|
|
|
|
|
+ long yhShopUserId = StpYhShopManageUser.getLoginIdAsLong();
|
|
|
|
|
+ String first = null;
|
|
|
|
|
+ String end = null;
|
|
|
|
|
+ String timeSql = StrUtil.EMPTY;
|
|
|
|
|
+ if (firstDate != null) {
|
|
|
|
|
+ first = DateUtil.format(DateTime.of(firstDate), "yyyy-MM-dd HH:mm:ss");
|
|
|
|
|
+ timeSql += String.format(" and o.created_time >= '%s'", first);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (lastDate != null) {
|
|
|
|
|
+ end = DateUtil.format(DateTime.of(lastDate), "yyyy-MM-dd HH:mm:ss");
|
|
|
|
|
+ timeSql += String.format(" and o.created_time < '%s'", end);
|
|
|
|
|
+ }
|
|
|
|
|
+ MapBuilder builder = MapUtils.flatBuilder(request.getParameterMap());
|
|
|
|
|
+ builder.put("shopUserId", String.format("shop_user_id = %s", yhShopUserId));
|
|
|
|
|
+ if (StrUtil.isNotEmpty(timeSql)) {
|
|
|
|
|
+ builder.put("time", timeSql);
|
|
|
|
|
+ }
|
|
|
|
|
+ SearchResult<YhShopUserDistributeOrdersDetailView> search = beanSearcher.search(YhShopUserDistributeOrdersDetailView.class, builder.build());
|
|
|
|
|
+ return GatewayResponse.SUCCESS.newBuilder().toResult(search);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 店铺分销商品列表
|
|
|
|
|
+ */
|
|
|
|
|
+ @GetMapping("/get/goods")
|
|
|
|
|
+ public Result<List<GoodsDon>> getGoodsList() {
|
|
|
|
|
+ long yhShopUserId = StpYhShopManageUser.getLoginIdAsLong();
|
|
|
|
|
+ YhShop yhShop = yhShopMapper.selectById(Wrappers.lambdaQuery(YhShop.class)
|
|
|
|
|
+ .eq(YhShop::getUserId, yhShopUserId)
|
|
|
|
|
+ .eq(YhShop::getVerifyStatus, YhShop.Status.success)
|
|
|
|
|
+ .last("limit 1"));
|
|
|
|
|
+ if (yhShop == null) {
|
|
|
|
|
+ return GatewayResponse.SUCCESS.newBuilder().toResult();
|
|
|
|
|
+ }
|
|
|
|
|
+ List<GoodsDon> goodsDons = goodsDonMapper.getYhShopDistributeGoodsDetail(yhShop.getId());
|
|
|
|
|
+ return GatewayResponse.SUCCESS.newBuilder().toResult(goodsDons);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|