商品相关接口
先在controller,service下,声明和新建好ProductManageController和ProductService
项目结构
操作商品相关功能都需要验证管理员权限,所以先注入UserService,需要用到一个验证管理员身份方法,在UserService里添加
//校验是否是管理员
public ServerResponse checkAdminRole(User user){
if(user!=null && user.getRole()==Const.Role.ROLE_ADMIN){
return ServerResponse.createBySuccess();
}else {
return ServerResponse.createByError();
}
}
新增和更新商品
在每一个操作商品的controller方法里,都是这样一个框架验证身份后执行service方法
User user = (User)session.getAttribute(Const.CURRENT_USER);
if(user == null){
return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),"用户未登录,请登录管理员");
}
if(iUserService.checkAdminRole(user).isSuccess()){
//填充我们增加产品的业务逻辑
}else{
return ServerResponse.createByErrorMessage("无权限操作");
}
isSuccess判断成功后,直接return掉service层返回的结果
在service层里,把新增和更新商品放在一个方法里,因为实际上这两个操作就一点不同而已,如果是更新商品那么我们传过来的product类里的id肯定是非空的,而如果是新增商品,是不会有id值的,所以以此为判断条件,来判断执行insert还是update
public ServerResponse saveOrUpdateProduct(Product product){
if(product != null)
{
if(StringUtils.isNotBlank(product.getSubImages())){
String[] subImageArray = product.getSubImages().split(",");
if(subImageArray.length > 0){
product.setMainImage(subImageArray[0]);
}
}
if(product.getId() != null){
int rowCount = productMapper.updateByPrimaryKey(product);
if(rowCount > 0){
return ServerResponse.createBySuccess("更新产品成功");
}
return ServerResponse.createBySuccess("更新产品失败");
}else{
int rowCount = productMapper.insert(product);
if(rowCount > 0){
return ServerResponse.createBySuccess("新增产品成功");
}
return ServerResponse.createBySuccess("新增产品失败");
}
}
return ServerResponse.createByErrorMessage("新增或更新产品参数不正确");
}
商品主图片采用子图的第一张图片,所有用字符串分割,把第一张图取出来放到MainImage
更改商品状态
商品上架或者下架,只修改商品的状态码,逻辑较为简单,详见代码
商品细节
这里我们需要一个vo对象,value object,承载对象各个值的对象,来组装业务对象,因为pojo对象不一定能满足我们的业务需求。
在项目下新建vo包,在vo下新建ProductDetailVo
private Integer id;
private Integer categoryId;
private String name;
private String subtitle;
private String mainImage;
private String subImages;
private String detail;
private BigDecimal price;
private Integer stock;
private Integer status;
private String createTime;
private String updateTime;
private String imageHost;
private Integer parentCategoryId;
//忽略get(),set()
可以看到我们组装了两个属性,一个是图片服务器前缀,一个是父分类id
新增一个方法组装productDetail
private ProductDetailVo assembleProductDetailVo(Product product){
ProductDetailVo productDetailVo = new ProductDetailVo();
productDetailVo.setId(product.getId());
productDetailVo.setSubtitle(product.getSubtitle());
productDetailVo.setPrice(product.getPrice());
productDetailVo.setMainImage(product.getMainImage());
productDetailVo.setSubImages(product.getSubImages());
productDetailVo.setCategoryId(product.getCategoryId());
productDetailVo.setDetail(product.getDetail());
productDetailVo.setName(product.getName());
productDetailVo.setStatus(product.getStatus());
productDetailVo.setStock(product.getStock());
productDetailVo.setImageHost(PropertiesUtil.getProperty("ftp.server.http.prefix","http://image.kamisama.com/"));
Category category = categoryMapper.selectByPrimaryKey(product.getCategoryId());
if(category == null){
productDetailVo.setParentCategoryId(0);//默认根节点
}else{
productDetailVo.setParentCategoryId(category.getParentId());
}
productDetailVo.setCreateTime(DateTimeUtil.dateToStr(product.getCreateTime()));
productDetailVo.setUpdateTime(DateTimeUtil.dateToStr(product.getUpdateTime()));
return productDetailVo;
}
这里的ImageHost从配置文件里获取,所以需要一个PropertyUtils工具类,我们在tomcat启动的时候就要读取到里面的配置,所以使用静态块解决这类事情,静态代码块先于普通代码块,普通代码块先于构造代码块,静态代码块在加载的时候被执行且,只执行一次,用来初始化。
public class PropertiesUtil {
private static Logger logger = LoggerFactory.getLogger(PropertiesUtil.class);
private static Properties props;
static {
String fileName = "mmall.properties";
props = new Properties();
try {
props.load(new InputStreamReader(PropertiesUtil.class.getClassLoader().getResourceAsStream(fileName),"UTF-8"));
} catch (IOException e) {
logger.error("配置文件读取异常",e);
}
}
public static String getProperty(String key){
String value = props.getProperty(key.trim());
if(StringUtils.isBlank(value)){
return null;
}
return value.trim();
}
public static String getProperty(String key,String defaultValue){
String value = props.getProperty(key.trim());
if(StringUtils.isBlank(value)){
value = defaultValue;
}
return value.trim();
}
}
trim()可以避免配置文件里有多余的空格
从数据库拿出来的updateTime和createTime都是一个毫秒数不利于阅读,所以需要一个工具类转换一下,新建一个DateTimeUtils,字符串转date,date转字符串,这里用了joda-time,比jdk自带的时间函数要好用得多
public class DateTimeUtil {
//joda-time
public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static Date strToDate(String dateTimeStr,String formatStr){
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(formatStr);
DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
return dateTime.toDate();
}
public static String dateToStr(Date date,String formatStr){
if (date==null){
return StringUtils.EMPTY;
}
DateTime dateTime = new DateTime(date);
return dateTime.toString(formatStr);
}
public static Date strToDate(String dateTimeStr){
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(STANDARD_FORMAT);
DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
return dateTime.toDate();
}
public static String dateToStr(Date date){
if (date==null){
return StringUtils.EMPTY;
}
DateTime dateTime = new DateTime(date);
return dateTime.toString(STANDARD_FORMAT);
}
}
商品List
controller里和上面基本一样,参数多了一点,因为要分页,所以有个pageNum和pageSize,用@RequestParam注解加个默认值
@RequestMapping("list.do")
@ResponseBody
public ServerResponse getList(HttpSession session, @RequestParam(value = "pageNum",defaultValue = "1") int pageNum, @RequestParam(value = "pageSize",defaultValue = "10") int pageSize){
User user = (User)session.getAttribute(Const.CURRENT_USER);
if(user == null){
return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),"用户未登录,请登录管理员");
}
if(iUserService.checkAdminRole(user).isSuccess()){
//填充业务
return iProductService.getProductList(pageNum,pageSize);
}else{
return ServerResponse.createByErrorMessage("无权限操作");
}
}
service层里用mybatis分页插件进行分页,用PageHelper.startPage(pageNum,pageSize)设置第几个页面开始和页面大小,然后sql查询出商品list,然后把查出来的商品list转成我们需要的vo,然后把它交给PageInfo
public ServerResponse<PageInfo> getProductList(int pageNum, int pageSize){
//startPage--start
//填充自己的sql查询逻辑
//pageHelper-收尾
PageHelper.startPage(pageNum,pageSize);
List<Product> productList = productMapper.selectList();
List<ProductListVo> productListVoList = Lists.newArrayList();
for(Product productItem : productList){
ProductListVo productListVo = assembleProductListVo(productItem);
productListVoList.add(productListVo);
}
PageInfo pageResult = new PageInfo(productList);
pageResult.setList(productListVoList);
return ServerResponse.createBySuccess(pageResult);
}
private ProductListVo assembleProductListVo(Product product){
ProductListVo productListVo = new ProductListVo();
productListVo.setId(product.getId());
productListVo.setName(product.getName());
productListVo.setCategoryId(product.getCategoryId());
productListVo.setImageHost(PropertiesUtil.getProperty("ftp.server.http.prefix","http://image.kamisama.com/"));
productListVo.setMainImage(product.getMainImage());
productListVo.setPrice(product.getPrice());
productListVo.setSubtitle(product.getSubtitle());
productListVo.setStatus(product.getStatus());
return productListVo;
}
按名字查找商品
与上面一样,区别在于controller要再传一个productName和productId
@RequestMapping("search.do")
@ResponseBody
public ServerResponse productSearch(
HttpSession session,String productName,Integer productId,
@RequestParam(value = "pageNum",defaultValue = "1") int pageNum,
@RequestParam(value = "pageSize",defaultValue = "10") int pageSize){
User user = (User)session.getAttribute(Const.CURRENT_USER);
if(user == null){
return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),"用户未登录,请登录管理员");
}
if(iUserService.checkAdminRole(user).isSuccess()){
//填充业务
return iProductService.searchProduct(productName,productId,pageNum,pageSize);
}else{
return ServerResponse.createByErrorMessage("无权限操作");
}
}
public ServerResponse<PageInfo> searchProduct(String productName,Integer productId,int pageNum,int pageSize){
PageHelper.startPage(pageNum,pageSize);
if(StringUtils.isNotBlank(productName)){
productName = new StringBuilder().append("%").append(productName).append("%").toString();
}
List<Product> productList = productMapper.selectByNameAndProductId(productName,productId);
List<ProductListVo> productListVoList = Lists.newArrayList();
for(Product productItem : productList){
ProductListVo productListVo = assembleProductListVo(productItem);
productListVoList.add(productListVo);
}
PageInfo pageResult = new PageInfo(productList);
pageResult.setList(productListVoList);
return ServerResponse.createBySuccess(pageResult);
}
sql查询语句就有区别了,因为传了一个商品id和商品名称,所以不能简单的匹配这两个字段,因为当商品id为null,“where id = id and productName=productName” 是怎么也查不到的,所以,要用一个判断if,判断是否为空,所以这个查询的实现如下:
<select id="selectByNameAndProductId" resultMap="BaseResultMap" parameterType="map">
SELECT
<include refid="Base_Column_List"/>
FROM mmall_product
<where>
<if test="productName != null">
AND NAME LIKE #{productName}
</if>
<if test="productId != null">
and id = #{productId}
</if>
</where>
</select>
文件上传
在SpringMVC的配置里要有文件上传的配置
<!-- 文件上传 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760"/> <!-- 10m -->
<property name="maxInMemorySize" value="4096" />
<property name="defaultEncoding" value="UTF-8"></property>
</bean>
先创建一个FileService,写一个upload方法,返回值是新的文件名,参数传MultipartFile和路径Path,先拿到文件名再拿到扩展名,然后给原来的文件名生成一个随机的文件名,避免上传时会有重复文件名导致文件被覆盖,然后再连接上扩展名,上传之后把新文件名返回回去。上传时,如果路径不存在,那要先创建它,创建之前要先设置权限fileDir.setWritable(true),让它有写入的权限,然后fileDir.mkdirs()。然后就可以上传了
mkdir()和mkdirs()的区别在于,mkdirs可以创建多个文件路径
@Service("iFileService")
public class FileServiceImpl implements IFileService {
private Logger logger = LoggerFactory.getLogger(FileServiceImpl.class);
public String upload(MultipartFile file,String path){
String fileName = file.getOriginalFilename();
//扩展名
//abc.jpg
String fileExtensionName = fileName.substring(fileName.lastIndexOf(".")+1);
String uploadFileName = UUID.randomUUID().toString()+"."+fileExtensionName;
logger.info("开始上传文件,上传文件的文件名:{},上传的路径:{},新文件名:{}",fileName,path,uploadFileName);
File fileDir = new File(path);
if(!fileDir.exists()){
fileDir.setWritable(true);
fileDir.mkdirs();
}
File targetFile = new File(path,uploadFileName);
try {
file.transferTo(targetFile);
//文件已经上传成功了
//todo 文件上传
FTPUtil.uploadFile(Lists.newArrayList(targetFile));
//已经上传到ftp服务器上
targetFile.delete();
} catch (IOException e) {
logger.error("上传文件异常",e);
return null;
}
//A:abc.jpg
//B:abc.jpg
return targetFile.getName();
}
}
然后在工具类下新建一个FTPUtils,内部封装了连接服务器方法和文件上传方法
public class FTPUtil {
private static final Logger logger = LoggerFactory.getLogger(FTPUtil.class);
private static String ftpIp = PropertiesUtil.getProperty("ftp.server.ip");
private static String ftpUser = PropertiesUtil.getProperty("ftp.user");
private static String ftpPass = PropertiesUtil.getProperty("ftp.pass");
private String ip;
private int port;
private String user;
private String pwd;
private FTPClient ftpClient;
...省略get和set方法
public FTPUtil(String ip,int port,String user,String pwd){
this.ip = ip;
this.port = port;
this.user = user;
this.pwd = pwd;
}
public static boolean uploadFile(List<File> fileList) throws IOException {
FTPUtil ftpUtil = new FTPUtil(ftpIp,21,ftpUser,ftpPass);
logger.info("开始连接ftp服务器");
boolean result = ftpUtil.uploadFile("img",fileList);
logger.info("开始连接ftp服务器,结束上传,上传结果:{}");
return result;
}
private boolean uploadFile(String remotePath,List<File> fileList) throws IOException {
boolean uploaded = true;
FileInputStream fis = null;
//连接FTP服务器
if(connectServer(this.ip,this.port,this.user,this.pwd)){
try {
ftpClient.changeWorkingDirectory(remotePath);
ftpClient.setBufferSize(1024);
ftpClient.setControlEncoding("UTF-8");
ftpClient.setFileType(FTPClient.BINARY_FILE_TYPE);
ftpClient.enterLocalPassiveMode();
for(File fileItem : fileList){
fis = new FileInputStream(fileItem);
ftpClient.storeFile(fileItem.getName(),fis);
}
} catch (IOException e) {
logger.error("上传文件异常",e);
uploaded = false;
e.printStackTrace();
} finally {
fis.close();
ftpClient.disconnect();
}
}
return uploaded;
}
private boolean connectServer(String ip,int port,String user,String pwd){
boolean isSuccess = false;
ftpClient = new FTPClient();
try {
ftpClient.connect(ip);
isSuccess = ftpClient.login(user,pwd);
} catch (IOException e) {
logger.error("连接FTP服务器异常",e);
}
return isSuccess;
}
}
在controller里增加upload方法参数传session,request和一个MultipartFile(是SpringMVC的文件上传)
@RequestMapping("upload.do")
@ResponseBody
public ServerResponse upload(
HttpSession session, @RequestParam(value = "upload_file",required = false) MultipartFile file, HttpServletRequest request){
User user = (User)session.getAttribute(Const.CURRENT_USER);
if(user == null){
return ServerResponse.createByErrorCodeMessage(ResponseCode.NEED_LOGIN.getCode(),"用户未登录,请登录管理员");
}
if(iUserService.checkAdminRole(user).isSuccess()){
String path = request.getSession().getServletContext().getRealPath("upload");
String targetFileName = iFileService.upload(file,path);
String url = PropertiesUtil.getProperty("ftp.server.http.prefix")+targetFileName;
Map fileMap = Maps.newHashMap();
fileMap.put("uri",targetFileName);
fileMap.put("url",url);
return ServerResponse.createBySuccess(fileMap);
}else{
return ServerResponse.createByErrorMessage("无权限操作");
}
}
首先从request里拿到session然后拿到servlet的上下文,getRealPath()方法拿到路径,上传之后就是这里
上传路径
富文本上传
与普通的文件上传还是有一点点区别,富文本中对于返回值有自己的要求,我们使用是simditor所以按照simditor的要求进行返回,很多前端插件对后端的返回值是有要求的,所以要适当更改。
@RequestMapping("richtext_img_upload.do")
@ResponseBody
public Map richtextImgUpload(HttpSession session, @RequestParam(value = "upload_file",required = false) MultipartFile file, HttpServletRequest request, HttpServletResponse response){
Map resultMap = Maps.newHashMap();
User user = (User)session.getAttribute(Const.CURRENT_USER);
if(user == null){
resultMap.put("success",false);
resultMap.put("msg","请登录管理员");
return resultMap;
}
//富文本中对于返回值有自己的要求,我们使用是simditor所以按照simditor的要求进行返回
// {
// "success": true/false,
// "msg": "error message", # optional
// "file_path": "[real file path]"
// }
if(iUserService.checkAdminRole(user).isSuccess()){
String path = request.getSession().getServletContext().getRealPath("upload");
String targetFileName = iFileService.upload(file,path);
if(StringUtils.isBlank(targetFileName)){
resultMap.put("success",false);
resultMap.put("msg","上传失败");
return resultMap;
}
String url = PropertiesUtil.getProperty("ftp.server.http.prefix")+targetFileName;
resultMap.put("success",true);
resultMap.put("msg","上传成功");
resultMap.put("file_path",url);
response.addHeader("Access-Control-Allow-Headers","X-File-Name");
return resultMap;
}else{
resultMap.put("success",false);
resultMap.put("msg","无权限操作");
return resultMap;
}
}
网友评论