@ -0,0 +1,40 @@ | |||||
package org.jeecg.modules.api.teambuyController; | |||||
import io.swagger.annotations.Api; | |||||
import io.swagger.annotations.ApiOperation; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
import org.jeecg.common.api.vo.Result; | |||||
import org.jeecg.modules.apiService.LoginApiService; | |||||
import org.jeecg.modules.bean.LoginReq; | |||||
import org.springframework.web.bind.annotation.GetMapping; | |||||
import org.springframework.web.bind.annotation.RequestMapping; | |||||
import org.springframework.web.bind.annotation.RestController; | |||||
import javax.annotation.Resource; | |||||
@Api(tags="登录-登录相关接口") | |||||
@RestController | |||||
@RequestMapping("/teambuy/login") | |||||
@Slf4j | |||||
public class ApiLoginController { | |||||
/******************************************************************************************************************/ | |||||
//工作信息 | |||||
@Resource | |||||
private LoginApiService loginApiService; | |||||
/******************************************************************************************************************/ | |||||
//小程序-微信授权登录接口 | |||||
@ApiOperation(value="小程序-微信授权登录接口", notes="小程序-微信授权登录接口") | |||||
@GetMapping("/login") | |||||
public Result<?> login(LoginReq loginReq){ | |||||
return loginApiService.login(loginReq); | |||||
} | |||||
//绑定手机号码 | |||||
@ApiOperation(value="小程序-绑定手机号码", notes="小程序-绑定手机号码") | |||||
@GetMapping(value = "/bindPhone") | |||||
public Result<Object> bindPhone(String phoneCode){ | |||||
return loginApiService.bindPhone(phoneCode); | |||||
} | |||||
} |
@ -0,0 +1,44 @@ | |||||
package org.jeecg.modules.api.teambuyController; | |||||
import io.swagger.annotations.Api; | |||||
import io.swagger.annotations.ApiOperation; | |||||
import lombok.extern.slf4j.Slf4j; | |||||
import org.jeecg.common.api.vo.Result; | |||||
import org.jeecg.modules.apiBean.PageBean; | |||||
import org.jeecg.modules.apiService.RecommendService; | |||||
import org.springframework.web.bind.annotation.RequestHeader; | |||||
import org.springframework.web.bind.annotation.RequestMapping; | |||||
import org.springframework.web.bind.annotation.RequestMethod; | |||||
import org.springframework.web.bind.annotation.RestController; | |||||
import javax.annotation.Resource; | |||||
@Api(tags="我的-邀请好友&我的团队相关接口") | |||||
@RestController | |||||
@RequestMapping("/teambuy/share") | |||||
@Slf4j | |||||
public class RecommendController { | |||||
/******************************************************************************************************************/ | |||||
//邀请好友 | |||||
@Resource | |||||
private RecommendService recommendService; | |||||
/******************************************************************************************************************/ | |||||
//邀请好友-获取推荐二维码 | |||||
@ApiOperation(value="我的-获取推荐二维码", notes="我的-获取推荐二维码") | |||||
@RequestMapping(value = "/getInviteCode", method = {RequestMethod.POST}) | |||||
public Result<?> getInviteCode(@RequestHeader("X-Access-Token") String token){ | |||||
return recommendService.getInviteCode(token); | |||||
} | |||||
//我的团队-查询直推用户列表 | |||||
@ApiOperation(value="我的团队-查询直推用户列表", notes="我的-获取直推用户列表") | |||||
@RequestMapping(value = "/queryRecommendUserList", method = {RequestMethod.POST}) | |||||
public Result<?> queryRecommendUserList(@RequestHeader("X-Access-Token") String token, PageBean pageBean){ | |||||
return recommendService.queryRecommendUserList(token, pageBean); | |||||
} | |||||
} |
@ -0,0 +1,19 @@ | |||||
package org.jeecg.modules.apiBean; | |||||
import io.swagger.annotations.ApiModelProperty; | |||||
import lombok.Data; | |||||
@Data | |||||
public class PageBean { | |||||
/**显示条数*/ | |||||
@ApiModelProperty(value = "显示条数" ) | |||||
private Integer pageSize; | |||||
/**当前页*/ | |||||
@ApiModelProperty(value = "当前页" ) | |||||
private Integer pageNo; | |||||
public PageBean() { | |||||
this.pageNo = 1; | |||||
this.pageSize = 1000; | |||||
} | |||||
} |
@ -0,0 +1,14 @@ | |||||
package org.jeecg.modules.apiBean; | |||||
import io.swagger.annotations.ApiModelProperty; | |||||
import lombok.Data; | |||||
@Data | |||||
public class WxQrCodeVo { | |||||
/**图片地址*/ | |||||
@ApiModelProperty(value = "图片地址") | |||||
private String url; | |||||
@ApiModelProperty(value = "名称") | |||||
private String name; | |||||
} |
@ -0,0 +1,12 @@ | |||||
package org.jeecg.modules.apiService; | |||||
import org.jeecg.common.api.vo.Result; | |||||
import org.jeecg.modules.bean.LoginReq; | |||||
public interface LoginApiService { | |||||
//小程序-微信授权登录接口 | |||||
public Result<?> login(LoginReq loginReq); | |||||
//绑定手机号码 | |||||
public Result<Object> bindPhone(String phoneCode); | |||||
} |
@ -0,0 +1,12 @@ | |||||
package org.jeecg.modules.apiService; | |||||
import org.jeecg.common.api.vo.Result; | |||||
import org.jeecg.modules.apiBean.PageBean; | |||||
public interface RecommendService { | |||||
//邀请好友-获取推荐二维码 | |||||
public Result<?> getInviteCode(String token); | |||||
//我的团队-查询直推用户列表 | |||||
public Result<?> queryRecommendUserList(String token, PageBean pageBean); | |||||
} |
@ -0,0 +1,168 @@ | |||||
package org.jeecg.modules.apiService.impl; | |||||
import com.alibaba.fastjson.JSONObject; | |||||
import org.apache.commons.lang3.StringUtils; | |||||
import org.jeecg.common.api.vo.Result; | |||||
import org.jeecg.common.constant.CommonConstant; | |||||
import org.jeecg.common.exception.JeecgBootException; | |||||
import org.jeecg.common.system.util.JwtUtil; | |||||
import org.jeecg.common.util.RedisUtil; | |||||
import org.jeecg.modules.apiService.LoginApiService; | |||||
import org.jeecg.modules.bean.HttpConf; | |||||
import org.jeecg.modules.bean.LoginReq; | |||||
import org.jeecg.modules.hanHaiMember.entity.HanHaiMember; | |||||
import org.jeecg.modules.hanHaiMember.service.IHanHaiMemberService; | |||||
import org.springframework.stereotype.Service; | |||||
import javax.annotation.Resource; | |||||
import java.io.BufferedReader; | |||||
import java.io.DataOutputStream; | |||||
import java.io.InputStreamReader; | |||||
import java.net.HttpURLConnection; | |||||
import java.net.URL; | |||||
import java.nio.charset.StandardCharsets; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
@Service | |||||
public class LoginApiServiceImpl implements LoginApiService { | |||||
/******************************************************************************************************************/ | |||||
//微信小程序appid(敢为人鲜) | |||||
private static final String mpAppId = "wx94a640f07969d6c9"; | |||||
//微信小程序appSecret(敢为人鲜) | |||||
private static final String mpAppSecret = ""; | |||||
@Resource | |||||
private RedisUtil redisUtil; | |||||
@Resource | |||||
private IHanHaiMemberService hanHaiMemberService; | |||||
@Resource | |||||
private HttpConf httpConf; | |||||
/******************************************************************************************************************/ | |||||
@Override | |||||
public Result<?> login(LoginReq loginReq) { | |||||
Result<Object> result = new Result<>(); | |||||
Map<String, Object> map = new HashMap<>(); | |||||
if (StringUtils.isBlank(loginReq.getCode())) { | |||||
throw new JeecgBootException("小程序code为空"); | |||||
} | |||||
String loginUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=" + mpAppId + "&secret=" + mpAppSecret+ "&js_code=" + loginReq.getCode() + "&grant_type=authorization_code"; | |||||
//使用工具类 | |||||
JSONObject json_test = httpConf.getJSONObject(loginUrl); | |||||
String wxOpenid = json_test.getString("openid"); | |||||
String sessionKey = json_test.getString("session_key"); | |||||
if (StringUtils.isBlank(wxOpenid)) { | |||||
throw new JeecgBootException("未获取到openid"); | |||||
} | |||||
HanHaiMember member = hanHaiMemberService.lambdaQuery().eq(HanHaiMember::getAppletOpenid,wxOpenid).one(); | |||||
if (member == null) { | |||||
//如果user等于null说明该用户第一次登录,数据库没有该用户信息。 | |||||
loginReq.setOpenid(wxOpenid); | |||||
loginReq.setSession_key(sessionKey); | |||||
member = new HanHaiMember(); | |||||
// member.setSesssionKey(sessionKey); | |||||
member.setAppletOpenid(wxOpenid); | |||||
member.setNickName(loginReq.getNickName()); | |||||
member.setHeadImage(loginReq.getHeadimgurl()); | |||||
// 生成token返回给小程序端 | |||||
String token = JwtUtil.sign(member.getAppletOpenid(), wxOpenid); | |||||
hanHaiMemberService.save(member); | |||||
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token); | |||||
// 设置超时时间 | |||||
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 100); | |||||
map.put("userInfo", member); | |||||
map.put("token", token); | |||||
result.setCode(200); | |||||
result.setResult(map); | |||||
return result; | |||||
} else { | |||||
// member.setHeadImage(loginReq.getHeadimgurl()); | |||||
// member.setSessionKey(sessionKey); | |||||
// memberService.saveOrUpdate(member); | |||||
// 生成token返回给小程序端 | |||||
String token = JwtUtil.sign(member.getAppletOpenid(), wxOpenid); | |||||
redisUtil.set(CommonConstant.PREFIX_USER_TOKEN + token, token); | |||||
// 设置超时时间 | |||||
redisUtil.expire(CommonConstant.PREFIX_USER_TOKEN + token, JwtUtil.EXPIRE_TIME / 100); | |||||
map.put("userInfo", member); | |||||
map.put("token", token); | |||||
//用户id做im账号 | |||||
result.setResult(map); | |||||
result.setCode(200); | |||||
// result.setMessage(member.getNickName()+"已于"+member.getCreateTime()+"注册成功!请勿重复注册!"); | |||||
// // 未绑定手机号,跳转授权绑定手机号 | |||||
// if (StringUtils.isBlank(member.getPhone())) { | |||||
// result.setMessage("该微信用户尚未绑定手机号,请授权手机进行绑定"); | |||||
// result.setCode(905); | |||||
// result.setResult(map); | |||||
// return result; | |||||
// } | |||||
} | |||||
return result; | |||||
} | |||||
@Override | |||||
public Result<Object> bindPhone(String phoneCode) { | |||||
try { | |||||
String phoneNumber = this.getPhoneNumber(phoneCode); | |||||
return Result.OK(phoneNumber); | |||||
}catch (Exception e){ | |||||
return Result.error(e.getMessage()); | |||||
} | |||||
} | |||||
private static final String API_URL = "https://api.weixin.qq.com/wxa/business/getuserphonenumber"; | |||||
public String getPhoneNumber(String code) throws Exception { | |||||
URL url = new URL(API_URL + "?access_token=" + this.getAccessToken()); | |||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection(); | |||||
conn.setRequestMethod("POST"); | |||||
conn.setRequestProperty("Content-Type", "application/json; utf-8"); | |||||
conn.setRequestProperty("Accept", "application/json"); | |||||
conn.setDoOutput(true); | |||||
JSONObject jsonInput = new JSONObject(); | |||||
jsonInput.put("code", code); | |||||
try (DataOutputStream os = new DataOutputStream(conn.getOutputStream())) { | |||||
byte[] input = jsonInput.toString().getBytes(StandardCharsets.UTF_8); | |||||
os.write(input, 0, input.length); | |||||
} | |||||
try (BufferedReader br = new BufferedReader( | |||||
new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) { | |||||
StringBuilder response = new StringBuilder(); | |||||
String responseLine; | |||||
while ((responseLine = br.readLine()) != null) { | |||||
response.append(responseLine.trim()); | |||||
} | |||||
//获取手机号码 | |||||
return response.toString(); | |||||
} | |||||
} | |||||
private static final String TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"; | |||||
public String getAccessToken() throws Exception { | |||||
String requestUrl = String.format(TOKEN_URL, mpAppId, mpAppSecret); | |||||
URL url = new URL(requestUrl); | |||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection(); | |||||
conn.setRequestMethod("GET"); | |||||
try (BufferedReader br = new BufferedReader( | |||||
new InputStreamReader(conn.getInputStream(), "UTF-8"))) { | |||||
StringBuilder response = new StringBuilder(); | |||||
String responseLine; | |||||
while ((responseLine = br.readLine()) != null) { | |||||
response.append(responseLine.trim()); | |||||
} | |||||
org.json.JSONObject jsonResponse = new org.json.JSONObject(response.toString()); | |||||
return jsonResponse.getString("access_token"); | |||||
} | |||||
} | |||||
} |
@ -0,0 +1,263 @@ | |||||
package org.jeecg.modules.apiService.impl; | |||||
import com.alibaba.fastjson.JSON; | |||||
import com.alibaba.fastjson.TypeReference; | |||||
import com.aliyun.oss.OSS; | |||||
import com.aliyun.oss.OSSClientBuilder; | |||||
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper; | |||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; | |||||
import org.apache.commons.fileupload.FileItem; | |||||
import org.apache.commons.fileupload.disk.DiskFileItemFactory; | |||||
import org.jeecg.common.api.vo.Result; | |||||
import org.jeecg.config.shiro.ShiroRealm; | |||||
import org.jeecg.modules.apiBean.HttpClientUtil; | |||||
import org.jeecg.modules.apiBean.PageBean; | |||||
import org.jeecg.modules.apiBean.WxQrCodeVo; | |||||
import org.jeecg.modules.apiService.RecommendService; | |||||
import org.jeecg.modules.hanHaiMember.entity.HanHaiMember; | |||||
import org.jeecg.modules.hanHaiMember.service.IHanHaiMemberService; | |||||
import org.jeecg.modules.teambuyConfig.entity.TeambuyConfig; | |||||
import org.jeecg.modules.teambuyConfig.service.ITeambuyConfigService; | |||||
import org.springframework.beans.factory.annotation.Value; | |||||
import org.springframework.http.HttpMethod; | |||||
import org.springframework.http.ResponseEntity; | |||||
import org.springframework.stereotype.Service; | |||||
import org.springframework.util.LinkedMultiValueMap; | |||||
import org.springframework.util.MultiValueMap; | |||||
import org.springframework.web.client.RestTemplate; | |||||
import org.springframework.web.multipart.MultipartFile; | |||||
import org.springframework.web.multipart.commons.CommonsMultipartFile; | |||||
import javax.annotation.Resource; | |||||
import java.io.*; | |||||
import java.util.HashMap; | |||||
import java.util.Map; | |||||
@Service | |||||
public class RecommendServiceImpl implements RecommendService { | |||||
/*************************************************************************************/ | |||||
//权限验证 | |||||
@Resource | |||||
private ShiroRealm shiroRealm; | |||||
//用户信息 | |||||
@Resource | |||||
private IHanHaiMemberService hanHaiMemberService; | |||||
//配置信息 | |||||
@Resource | |||||
private ITeambuyConfigService teambuyConfigService; | |||||
private String appid = "wx94a640f07969d6c9";//小程序appid | |||||
private String secret = "";//小程序密钥 | |||||
@Value("${jeecg.oss.endpoint}") | |||||
private String endpoint; | |||||
@Value("${jeecg.oss.accessKey}") | |||||
private String accessKey; | |||||
@Value("${jeecg.oss.secretKey}") | |||||
private String secretKey; | |||||
@Value("${jeecg.oss.bucketName}") | |||||
private String bucketName; | |||||
@Value("${jeecg.oss.staticDomain}") | |||||
private String staticDomain; | |||||
/*************************************************************************************/ | |||||
//获取个人推荐二维码 | |||||
@Override | |||||
public Result<?> getInviteCode(String token) { | |||||
//权限验证 | |||||
HanHaiMember hanHaiMember = shiroRealm.checkUserTokenIsEffectHanHaiOpenId(token); | |||||
//获取版本信息 | |||||
TeambuyConfig vsion = teambuyConfigService.lambdaQuery() | |||||
.eq(TeambuyConfig::getParamCode, "v_sion") | |||||
.one(); | |||||
Integer vsionStr = Integer.parseInt(vsion.getParamValueText()); | |||||
String trial = "release"; | |||||
if(vsionStr == 0){ | |||||
trial= "release"; | |||||
}else if(vsionStr == 1){ | |||||
trial= "trial"; | |||||
}else{ | |||||
trial= "develop"; | |||||
} | |||||
Map<String, Object> param = new HashMap<>(); | |||||
//获取跳转路径信息 | |||||
TeambuyConfig xcxSharePage = teambuyConfigService.lambdaQuery() | |||||
.eq(TeambuyConfig::getParamCode, "xcxSharePage") | |||||
.one(); | |||||
String key = "shareId=" + hanHaiMember.getId(); | |||||
param.put("path", xcxSharePage.getParamValueText() + "?" + key); //跳转页面 | |||||
String accessToken = this.getAccessToken(); | |||||
RestTemplate rest = new RestTemplate(); | |||||
InputStream inputStream = null; | |||||
OutputStream outputStream = null; | |||||
File file = null; | |||||
//获取存储地址 | |||||
TeambuyConfig oneImage = teambuyConfigService.lambdaQuery() | |||||
.eq(TeambuyConfig::getParamCode, "codeImg") | |||||
.one(); | |||||
String codeImg = oneImage.getParamValueText(); | |||||
try{ | |||||
String url = "https://api.weixin.qq.com/wxa/getwxacode?access_token=" + accessToken; | |||||
param.put("scene", hanHaiMember.getId()); | |||||
param.put("width", 150); | |||||
param.put("auto_color", false); | |||||
param.put("env_version", trial); | |||||
Map<String, Object> line_color = new HashMap<>(); | |||||
line_color.put("r", 0); | |||||
line_color.put("g", 0); | |||||
line_color.put("b", 0); | |||||
param.put("line_color", line_color); | |||||
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>(); | |||||
org.springframework.http.HttpEntity requestEntity = new org.springframework.http.HttpEntity(JSON.toJSONString(param), headers); | |||||
ResponseEntity<byte[]> entity = rest.exchange(url, HttpMethod.POST, requestEntity, byte[].class, new Object[0]); | |||||
byte[] result = entity.getBody(); | |||||
inputStream = new ByteArrayInputStream(result); | |||||
String tempSave = codeImg; | |||||
String filePath = tempSave; | |||||
file = new File(filePath); | |||||
if (!file.exists()) { | |||||
file.createNewFile(); | |||||
} | |||||
outputStream = new FileOutputStream(file); | |||||
int len = 0; | |||||
byte[] buf = new byte[1024]; | |||||
while ((len = inputStream.read(buf, 0, 1024)) != -1) { | |||||
outputStream.write(buf, 0, len); | |||||
} | |||||
outputStream.flush(); | |||||
//将文件上传至阿里云 | |||||
DiskFileItemFactory factory = new DiskFileItemFactory(16, null); | |||||
FileItem item = factory.createItem("File", "text/plain", true, file.getName()); | |||||
int bytesRead = 0; | |||||
byte[] buffer = new byte[8192]; | |||||
try { | |||||
FileInputStream fis = new FileInputStream(file); | |||||
OutputStream os = item.getOutputStream(); | |||||
while ((bytesRead = fis.read(buffer, 0, 8192)) != -1) { | |||||
os.write(buffer, 0, bytesRead); | |||||
} | |||||
os.close(); | |||||
fis.close(); | |||||
} catch (IOException e) { | |||||
e.printStackTrace(); | |||||
} | |||||
MultipartFile mf = new CommonsMultipartFile(item); | |||||
//返回图片下载地址 | |||||
WxQrCodeVo wxCodeVo = new WxQrCodeVo(); | |||||
wxCodeVo.setUrl(staticDomain+this.uploadAliYunOss(mf)); | |||||
wxCodeVo.setName("敢为人鲜欢迎您"); | |||||
return Result.OK(wxCodeVo); | |||||
} catch (Exception e) { | |||||
e.printStackTrace(); | |||||
} finally { | |||||
if (inputStream != null) { | |||||
try { | |||||
inputStream.close(); | |||||
} catch (IOException e) { | |||||
e.printStackTrace(); | |||||
} | |||||
} | |||||
if (outputStream != null) { | |||||
try { | |||||
outputStream.close(); | |||||
} catch (IOException e) { | |||||
e.printStackTrace(); | |||||
} | |||||
} | |||||
//删除文件 | |||||
if (file.exists()) { | |||||
file.delete(); | |||||
} | |||||
} | |||||
return null; | |||||
} | |||||
//我的团队-查询直推用户列表 | |||||
@Override | |||||
public Result<?> queryRecommendUserList(String token, PageBean pageBean) { | |||||
//权限验证 | |||||
HanHaiMember hanHaiMember = shiroRealm.checkUserTokenIsEffectHanHaiOpenId(token); | |||||
//返回信息 | |||||
String massege = ""; | |||||
//分页信息 | |||||
Page<HanHaiMember> page = null; | |||||
//查询信息 | |||||
LambdaQueryChainWrapper<HanHaiMember> query = null; | |||||
//返回信息 | |||||
Page<HanHaiMember> pageList = null; | |||||
try{ | |||||
//分页 | |||||
page = new Page<HanHaiMember>(pageBean.getPageNo(), pageBean.getPageSize()); | |||||
query = hanHaiMemberService | |||||
.lambdaQuery(); | |||||
//组装查询条件 | |||||
query.eq(HanHaiMember::getVid, hanHaiMember.getId()); | |||||
//按照创建时间降序排列 | |||||
query.orderByAsc(HanHaiMember::getCreateTime); | |||||
//获取直推用户信息 | |||||
pageList = query.page(page); | |||||
return Result.OK("直推用户列表", pageList); | |||||
}catch (Exception e){ | |||||
e.printStackTrace(); | |||||
return Result.error("直推用户列表查询失败"); | |||||
} | |||||
} | |||||
/** | |||||
* 获取令牌 | |||||
* | |||||
* @return | |||||
*/ | |||||
private String getAccessToken() { | |||||
String requestUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid + "&secret=" + secret; | |||||
String doGet2 = HttpClientUtil.doGet2(requestUrl); | |||||
Map<String, String> map = JSON.parseObject(doGet2, new TypeReference<Map<String, String>>() { | |||||
}); | |||||
return map.get("access_token"); | |||||
} | |||||
/** | |||||
* 上传文件至阿里云oss | |||||
* | |||||
* @return | |||||
*/ | |||||
private String uploadAliYunOss(MultipartFile mf) throws Exception { | |||||
String uploadFile = "massage"; | |||||
String fileName = new StringBuffer(mf.getOriginalFilename()).append(".jpg").toString();// 获取文件名 | |||||
String dbpath = uploadFile + File.separator + fileName; | |||||
if (dbpath.contains("\\")) { | |||||
dbpath = dbpath.replace("\\", "/"); | |||||
} | |||||
String endpoint = this.endpoint; | |||||
String accessKey = this.accessKey; | |||||
String secretKey = this.secretKey; | |||||
// 创建OSSClient实例。 | |||||
OSS ossClient = new OSSClientBuilder().build(endpoint, accessKey, secretKey); | |||||
// 上传Byte数组。 | |||||
byte[] content = mf.getBytes(); | |||||
String bucketName = this.bucketName; | |||||
ossClient.putObject(bucketName, dbpath, new ByteArrayInputStream(content)); | |||||
// 关闭OSSClient。 | |||||
ossClient.shutdown(); | |||||
return dbpath; | |||||
} | |||||
} |