Merge branch 'master' of gitee.com:beijing_hongye_huicheng/lilishop into pg

This commit is contained in:
misworga831 2023-08-25 14:06:07 +08:00
commit 9189a6d433
15 changed files with 183 additions and 92 deletions

View File

@ -84,7 +84,7 @@ ALTER TABLE li_foot_print ADD `store_id` varchar(255) DEFAULT NULL COMMENT '店
/**
*/
ALTER TABLE li_member_withdraw_apply ADD `real_name` varchar(255) DEFAULT NULL;
ALTER TABLE li_member_withdraw_apply ADD `connect_number` varchar(255) DEFAULT NULL;

View File

View File

@ -74,14 +74,36 @@ PS手机验证码为 111111
#### 平台管理端功能
![平台管理端功能](https://static.pickmall.cn/images/other/managerList1.jpg)
| 模块 <img width=80/> | 功能 |
|--------------------|-----------------------------------------------------------------|
| 首页 | 平台基础信息统计、待办事项 |
| 会员 | 会员列表、评价列表、积分历史、会员资金、会员充值 |
| 订单 | 商品订单、虚拟订单、订单售后、订单投诉、售后原因维护、收款流水、退款流水 |
| 商品 | 商品列表、商品审核、商品分类、商品品牌、商品规格、商品计量单位 |
| 促销 | 优惠券、券活动(每日&每月&每周&邀新 赠券)、秒杀活动、砍价活动、拼团活动、积分商品 |
| 店铺 | 店铺管理、店铺审核、店铺结算、店铺对账 |
| 运营 | 楼层装修、分销商管理、文章管理、意见反馈、站内信、短信、搜索热词管理 |
| 统计 | 会员统计、订单统计、流量统计、商品销量统计 |
| 设置 | 菜单管理、角色管理、部门管理、管理员管理、系统设置、行政地区管理、OSS管理、联合登陆、支付、物流公司、敏感词、验证码资源 |
#### 卖家功能
![商家端功能](https://static.pickmall.cn/images/other/storeList.jpg)
| 模块 <img width=80/> | 功能 |
|----|-------------------------------|
| 首页 | 店铺基础信息统计、待办事项、店铺公告 |
| 商品 | 商品发布、商品列表、商品模板、店铺分类 |
| 订单 | 商品订单、虚拟订单、订单评价、订单投诉、退款申请、退货申请 |
| 财务 | 店铺对账、店铺结算、发票管理 |
| 促销 | 优惠券、满额优惠、秒杀、拼团 、分销商品、分校订单 |
| 统计 |单统计、流量统计、商品销量统计 |
| 设置 | 配送公司、物流模板、店铺设置、店铺自提设置、PC装修、移动端装修、店员管理、部门管理、角色管理 |
| 消息 | 站内信 |
### 商城前端功能展示

View File

@ -76,8 +76,8 @@ public class ConnectBuyerWebController {
}
@ApiOperation(value = "APP-unionID登录")
@GetMapping("/app/login")
public ResultMessage<Token> unionLogin(ConnectAuthUser authUser, @RequestHeader("uuid") String uuid) {
@PostMapping("/app/login")
public ResultMessage<Token> unionLogin(@RequestBody ConnectAuthUser authUser, @RequestHeader("uuid") String uuid) {
try {
return ResultUtil.data(connectService.unionLoginCallback(authUser, uuid));
} catch (Exception e) {

View File

@ -5,7 +5,6 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.lili.cache.Cache;
import cn.lili.cache.CachePrefix;
import cn.lili.common.enums.ClientTypeEnum;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException;
@ -125,24 +124,18 @@ public class ConnectServiceImpl extends ServiceImpl<ConnectMapper, Connect> impl
@Transactional
public Token miniProgramAutoLogin(WechatMPLoginParams params) {
Object cacheData = cache.get(CachePrefix.WECHAT_SESSION_PARAMS.getPrefix() + params.getUuid());
Map<String, String> map = new HashMap<>(3);
if (cacheData == null) {
//得到微信小程序联合登陆信息
JSONObject json = this.getConnect(params.getCode());
//存储session key 后续登录用得到
String sessionKey = json.getStr("session_key");
String unionId = json.getStr("unionid");
String openId = json.getStr("openid");
map.put("sessionKey", sessionKey);
map.put("unionId", unionId);
map.put("openId", openId);
cache.put(CachePrefix.WECHAT_SESSION_PARAMS.getPrefix() + params.getUuid(), map, 900L);
} else {
map = (Map<String, String>) cacheData;
}
//微信联合登陆参数
//得到微信小程序联合登陆信息
JSONObject json = this.getConnect(params.getCode());
//存储session key 后续登录用得到
String sessionKey = json.getStr("session_key");
String unionId = json.getStr("unionid");
String openId = json.getStr("openid");
map.put("sessionKey", sessionKey);
map.put("unionId", unionId);
map.put("openId", openId);
//微信联合登陆参数
return phoneMpBindAndLogin(map.get("sessionKey"), params, map.get("openId"), map.get("unionId"));
}
@ -217,7 +210,7 @@ public class ConnectServiceImpl extends ServiceImpl<ConnectMapper, Connect> impl
connectQueryDTO.getUnionType())
.eq(CharSequenceUtil.isNotEmpty(connectQueryDTO.getUnionId()), Connect::getUnionId,
connectQueryDTO.getUnionId());
return this.getOne(queryWrapper,false);
return this.getOne(queryWrapper, false);
}
@Override
@ -267,16 +260,16 @@ public class ConnectServiceImpl extends ServiceImpl<ConnectMapper, Connect> impl
private Token unionLoginCallback(ConnectAuthUser authUser, boolean longTerm) {
try {
Member member =null;
Member member = null;
//判断是否传递手机号如果传递手机号则使用手机号登录
if(StrUtil.isNotBlank(authUser.getPhone())){
if (StrUtil.isNotBlank(authUser.getPhone())) {
member = memberService.findByMobile(authUser.getPhone());
}
//如果未查到手机号的会员则使用第三方登录
if(member==null){
if (member == null) {
LambdaQueryWrapper<Connect> queryWrapper = new LambdaQueryWrapper<Connect>();
//使用UnionId登录
if (StrUtil.isNotBlank(authUser.getToken().getUnionId())) {
if (authUser.getToken() != null && StrUtil.isNotBlank(authUser.getToken().getUnionId())) {
queryWrapper.eq(Connect::getUnionId, authUser.getToken().getUnionId())
.eq(Connect::getUnionType, authUser.getSource());
} else {

View File

@ -7,6 +7,7 @@ import cn.lili.modules.file.plugin.FilePlugin;
import cn.lili.modules.system.entity.dto.OssSetting;
import io.minio.*;
import io.minio.errors.ErrorResponseException;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import lombok.extern.slf4j.Slf4j;
@ -96,8 +97,20 @@ public class MinioFilePlugin implements FilePlugin {
return;
}
MinioClient ossClient = getOssClient();
List<DeleteObject> objectList = key.stream().map(DeleteObject::new).collect(Collectors.toList());
ossClient.removeObjects(RemoveObjectsArgs.builder().objects(objectList).bucket(ossSetting.getM_bucketName()).build());
List<DeleteObject> objectList = key.stream().map(DeleteObject::new).collect(Collectors.toList()); Iterable<Result<DeleteError>> results =
ossClient.removeObjects(RemoveObjectsArgs.builder().objects(objectList).bucket(ossSetting.getM_bucketName()).build());
for (Result<DeleteError> result : results) {
DeleteError error = null;
try {
error = result.get();
log.error(
"Error in deleting object " + error.objectName() + "; " + error.message());
} catch (Exception e) {
log.error(
"Error in deleting object " + e.getMessage());
}
}
}

View File

@ -65,7 +65,7 @@ public class TencentFilePlugin implements FilePlugin {
* @return
*/
private String getUrlPrefix() {
return "https://" + ossSetting.getTencentCOSBucket() + ".cos" + ossSetting.getTencentCOSEndPoint() + ".myqcloud.com/";
return "https://" + ossSetting.getTencentCOSBucket() + ".cos" + ossSetting.getTencentCOSRegion() + ".myqcloud.com/";
}
@Override

View File

@ -106,10 +106,10 @@ public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> i
@Override
public List<CategoryVO> categoryTree() {
List<CategoryVO> categoryVOList = (List<CategoryVO>) cache.get(CachePrefix.CATEGORY.getPrefix());
if (categoryVOList != null) {
return categoryVOList;
}
// List<CategoryVO> categoryVOList = (List<CategoryVO>) cache.get(CachePrefix.CATEGORY.getPrefix());
// if (categoryVOList != null) {
// return categoryVOList;
// }
//获取全部分类
LambdaQueryWrapper<Category> queryWrapper = new LambdaQueryWrapper<>();
@ -117,7 +117,7 @@ public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> i
List<Category> list = this.list(queryWrapper);
//构造分类树
categoryVOList = new ArrayList<>();
List<CategoryVO> categoryVOList = new ArrayList<>();
for (Category category : list) {
if ("0".equals(category.getParentId())) {
CategoryVO categoryVO = new CategoryVO(category);

View File

@ -77,7 +77,8 @@ public class PageDataServiceImpl extends ServiceImpl<PageDataMapper, PageData> i
//如果页面为发布则关闭其他页面开启此页面
if (pageData.getPageShow().equals(SwitchEnum.OPEN.name())) {
LambdaUpdateWrapper<PageData> lambdaUpdateWrapper = Wrappers.lambdaUpdate();
lambdaUpdateWrapper.eq(CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), PageData::getNum, UserContext.getCurrentUser().getStoreId());
lambdaUpdateWrapper.eq(CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), PageData::getNum
, UserContext.getCurrentUser().getStoreId());
lambdaUpdateWrapper.eq(PageData::getPageType, pageData.getPageType());
lambdaUpdateWrapper.eq(PageData::getPageClientType, pageData.getPageClientType());
lambdaUpdateWrapper.set(PageData::getPageShow, SwitchEnum.CLOSE.name());
@ -96,8 +97,16 @@ public class PageDataServiceImpl extends ServiceImpl<PageDataMapper, PageData> i
if (pageData.getPageShow() != null && pageData.getPageShow().equals(SwitchEnum.OPEN.name())) {
LambdaUpdateWrapper<PageData> lambdaUpdateWrapper = Wrappers.lambdaUpdate();
lambdaUpdateWrapper.eq(CharSequenceUtil.isNotEmpty(pageData.getPageType()), PageData::getPageType, pageData.getPageType());
lambdaUpdateWrapper.eq(CharSequenceUtil.isNotEmpty(pageData.getPageClientType()), PageData::getPageClientType, pageData.getPageClientType());
lambdaUpdateWrapper.eq(PageData::getNum, pageData.getNum());
lambdaUpdateWrapper.eq(CharSequenceUtil.isNotEmpty(pageData.getPageClientType()), PageData::getPageClientType,
pageData.getPageClientType());
//如果是管理员则判定页面num为null
if (UserContext.getCurrentUser().getRole().name().equals(UserEnums.MANAGER.name())) {
lambdaUpdateWrapper.isNull(PageData::getNum);
} else {
lambdaUpdateWrapper.eq(PageData::getNum, pageData.getNum());
}
lambdaUpdateWrapper.set(PageData::getPageShow, SwitchEnum.CLOSE.name());
this.update(lambdaUpdateWrapper);
} else {
@ -107,8 +116,10 @@ public class PageDataServiceImpl extends ServiceImpl<PageDataMapper, PageData> i
LambdaUpdateWrapper<PageData> lambdaUpdateWrapper = Wrappers.lambdaUpdate();
lambdaUpdateWrapper.set(PageData::getPageData, pageData.getPageData());
lambdaUpdateWrapper.eq(PageData::getId, pageData.getId());
lambdaUpdateWrapper.eq(CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), PageData::getPageType, PageEnum.STORE.name());
lambdaUpdateWrapper.eq(CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), PageData::getNum, UserContext.getCurrentUser().getStoreId());
lambdaUpdateWrapper.eq(CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()),
PageData::getPageType, PageEnum.STORE.name());
lambdaUpdateWrapper.eq(CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), PageData::getNum,
UserContext.getCurrentUser().getStoreId());
this.updateById(pageData);
return pageData;
}
@ -117,8 +128,10 @@ public class PageDataServiceImpl extends ServiceImpl<PageDataMapper, PageData> i
@Transactional(rollbackFor = Exception.class)
public PageData releasePageData(String id) {
PageData pageData = this.getOne(new LambdaQueryWrapper<PageData>()
.eq(CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), PageData::getPageType, PageEnum.STORE.name())
.eq(CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), PageData::getNum, UserContext.getCurrentUser().getStoreId())
.eq(CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), PageData::getPageType,
PageEnum.STORE.name())
.eq(CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), PageData::getNum,
UserContext.getCurrentUser().getStoreId())
.eq(PageData::getId, id));
if (pageData == null) {
throw new ServiceException(ResultCode.PAGE_NOT_EXIST);
@ -152,8 +165,10 @@ public class PageDataServiceImpl extends ServiceImpl<PageDataMapper, PageData> i
@Transactional(rollbackFor = Exception.class)
public boolean removePageData(String id) {
PageData pageData = this.getOne(new LambdaQueryWrapper<PageData>()
.eq(CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), PageData::getPageType, PageEnum.STORE.name())
.eq(CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), PageData::getNum, UserContext.getCurrentUser().getStoreId())
.eq(CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), PageData::getPageType,
PageEnum.STORE.name())
.eq(CharSequenceUtil.equals(UserContext.getCurrentUser().getRole().name(), UserEnums.STORE.name()), PageData::getNum,
UserContext.getCurrentUser().getStoreId())
.eq(PageData::getId, id));
if (pageData == null) {
throw new ServiceException(ResultCode.PAGE_NOT_EXIST);

View File

@ -232,8 +232,15 @@ public class KanjiaActivityGoodsServiceImpl extends AbstractPromotionsServiceImp
KanjiaActivityGoodsVO kanJiaActivityGoodsVO = new KanjiaActivityGoodsVO();
//获取砍价商品
KanjiaActivityGoods kanJiaActivityGoods = this.getById(id);
if (kanJiaActivityGoods == null) {
throw new ServiceException(ResultCode.KANJIA_ACTIVITY_NOT_FOUND_ERROR);
}
//获取商品SKU
GoodsSku goodsSku = this.goodsSkuService.getCanPromotionGoodsSkuByIdFromCache(kanJiaActivityGoods.getSkuId());
if (goodsSku == null) {
throw new ServiceException(ResultCode.KANJIA_ACTIVITY_NOT_FOUND_ERROR);
}
//填写活动商品价格剩余数量
kanJiaActivityGoodsVO.setGoodsSku(goodsSku);
kanJiaActivityGoodsVO.setStock(kanJiaActivityGoods.getStock());

View File

@ -1,8 +1,9 @@
package cn.lili.modules.search.utils;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import cn.lili.common.utils.StringUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* sql 关键字过滤
@ -15,13 +16,17 @@ import java.util.Set;
public class SqlFilter {
private static final Set<String> SQL_KEYWORDS = new HashSet<>(Arrays.asList(
"SELECT", "FROM", "WHERE", "AND", "OR", "NOT", "INSERT", "UPDATE", "DELETE", "CREATE",
"TABLE", "INDEX", "VIEW", "DROP", "ALTER", "COLUMN", "ADD", "SET", "GROUP", "BY",
"HAVING", "ORDER", "ASC", "DESC", "LIKE", "IN", "BETWEEN", "IS", "NULL", "TRUE", "FALSE",
"JOIN", "LEFT", "RIGHT", "INNER", "OUTER", "FULL", "ON", "AS", "DISTINCT", "COUNT",
"MAX", "MIN", "SUM", "AVG"
));
// SQL注入过滤
static final String SQL_KEYWORDS_PATTERN =
"(?i)(SELECT|FROM|WHERE|CONCAT|AND|NOT|INSERT|UPDATE|DELETE" +
"|TABLE|INDEX|VIEW|DROP|ALTER|COLUMN|ADD|SET|GROUP|BY" +
"|HAVING|ORDER|ASC|DESC|LIKE|IN|BETWEEN|IS|NULL|TRUE|FALSE" +
"|JOIN|LEFT|RIGHT|INNER|OUTER|FULL|ON|AS|DISTINCT|COUNT" +
"|MAX|MIN|SUM|AVG|IF|RAND|UPDATEXML|EXTRACTVALUE|LOAD_FILE|SLEEP|OFFSET)";
// OR 影响排序字段 sort所以暂时不过滤
// CREATE 影响常用排序字段 CREATE_TIME所以暂时不过滤
static final Pattern keywordPattern = Pattern.compile(SQL_KEYWORDS_PATTERN, Pattern.CASE_INSENSITIVE);
/**
@ -31,29 +36,12 @@ public class SqlFilter {
* @return
*/
public static Boolean hit(String sql) {
String[] tokens = sql.split("\\s+");
for (String token : tokens) {
if (!SQL_KEYWORDS.contains(token.toUpperCase())) {
return true;
}
if (StringUtils.isEmpty(sql)) {
return false;
}
return false;
Matcher matcher = keywordPattern.matcher(sql);
return matcher.find();
}
/**
* 关键字替换
*
* @param sql
* @return
*/
public static String filterSql(String sql) {
String[] tokens = sql.split("\\s+");
StringBuilder filteredSql = new StringBuilder();
for (String token : tokens) {
if (!SQL_KEYWORDS.contains(token.toUpperCase())) {
filteredSql.append(token).append(" ");
}
}
return filteredSql.toString().trim();
}
}

View File

@ -19,6 +19,27 @@ import java.util.Map;
@CacheConfig(cacheNames = "{regions}")
public interface RegionService extends IService<Region> {
/**
* 更新地区
*
* @param region 地区
* @return
*/
@CacheEvict(allEntries = true)
boolean updateById(Region region);
/**
* 更新地区
*
* @param region 地区
* @return
*/
@CacheEvict(allEntries = true)
boolean save(Region region);
@CacheEvict(allEntries = true)
boolean removeByIds(List<String> idList);
/**
* 同步行政数据
*

View File

@ -16,6 +16,7 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.lang3.ArrayUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
@ -36,6 +37,24 @@ public class RegionServiceImpl extends ServiceImpl<RegionMapper, Region> impleme
@Autowired
private Cache cache;
@Override
@Transactional
public boolean updateById(Region region) {
return super.updateById(region);
}
@Transactional
@Override
public boolean save(Region region) {
return super.save(region);
}
@Override
public boolean removeByIds(List<String> idList) {
return super.removeByIds(idList);
}
@Override
public void synchronizationData(String url) {
try {

View File

@ -1,15 +1,17 @@
package cn.lili.mybatis.util;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.lili.common.utils.BeanUtil;
import cn.lili.common.utils.StringUtils;
import cn.lili.common.vo.PageVO;
import cn.lili.common.vo.SearchVO;
import cn.lili.modules.search.utils.SqlFilter;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Date;
@ -22,19 +24,21 @@ import java.util.List;
* @version v4.0
* @since 2020/11/26 15:23
*/
@Slf4j
public class PageUtil {
//有order by 注入风险限制长度
static final Integer orderByLengthLimit = 20;
/**
* Mybatis-Plus分页封装
*
* @param page 分页VO
* @param <T> 范型
* @param <T> 范型
* @return 分页响应
*/
public static <T> Page<T> initPage(PageVO page) {
Page<T> p;
int pageNumber = page.getPageNumber();
int pageSize = page.getPageSize();
String sort = page.getSort();
@ -49,30 +53,39 @@ public class PageUtil {
if (pageSize > 100) {
pageSize = 100;
}
if (StrUtil.isNotBlank(sort)) {
Boolean isAsc = false;
if (StrUtil.isBlank(order)) {
isAsc = false;
} else {
Page<T> p = new Page<>(pageNumber, pageSize);
if (CharSequenceUtil.isNotBlank(sort)) {
if (sort.length() > orderByLengthLimit || SqlFilter.hit(sort)) {
log.error("排序字段长度超过限制或包含sql关键字请关注{}", sort);
return p;
}
boolean isAsc = false;
if (!CharSequenceUtil.isBlank(order)) {
if ("desc".equals(order.toLowerCase())) {
isAsc = false;
} else if ("asc".equals(order.toLowerCase())) {
isAsc = true;
}
}
p = new Page<>(pageNumber, pageSize);
if (isAsc) {
p.addOrder(OrderItem.asc(sort));
} else {
p.addOrder(OrderItem.desc(sort));
}
} else {
p = new Page<>(pageNumber, pageSize);
}
return p;
}
private void orderByHandler() {
}
/**
* 生成条件搜索 全对象对比 equals
* 如果需要like 需要另行处理
@ -87,14 +100,14 @@ public class PageUtil {
/**
* 生成条件搜索 全对象对比
*
* @param object 对象
* @param object 对象
* @param searchVo 查询条件
* @return 查询wrapper
*/
public static <T> QueryWrapper<T> initWrapper(Object object, SearchVO searchVo) {
QueryWrapper<T> queryWrapper = new QueryWrapper<>();
//创建时间区间判定
if (searchVo != null && StrUtil.isNotBlank(searchVo.getStartDate()) && StrUtil.isNotBlank(searchVo.getEndDate())) {
if (searchVo != null && CharSequenceUtil.isNotBlank(searchVo.getStartDate()) && CharSequenceUtil.isNotBlank(searchVo.getEndDate())) {
Date start = DateUtil.parse(searchVo.getStartDate());
Date end = DateUtil.parse(searchVo.getEndDate());
queryWrapper.between("create_time", start, DateUtil.endOfDay(end));
@ -156,8 +169,8 @@ public class PageUtil {
* 转换分页类型
*
* @param originPage 原分页
* @param records 新分页数据
* @param <T> 新类型
* @param records 新分页数据
* @param <T> 新类型
* @return 新类型分页
*/
public static <T> IPage<T> convertPage(IPage originPage, List<T> records) {

View File

@ -18,7 +18,7 @@
<properties>
<java.version>1.8</java.version>
<revision>4.2.5</revision>
<revision>4.3</revision>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<skipTests>true</skipTests>