Java電商專案面試--商品模組
面試:商品模組技術要點
1、POJO、BO、VO抽象模型
2、高效分頁及動態排序
3、FTP服務對接、富文字上傳
一、商品模組功能
前臺功能:
1、產品搜尋
2、動態排序列表
3、商品詳情
後臺功能:
1、商品列表
2、商品搜尋
3、圖片上傳
4、增加商品、更新商品、商品上下架
二、後臺新增和更新商品
Controller:
@Controller
@RequestMapping("/manage/product")
public class ProductManageController {
@Autowired
private IUserService iUserService;
@Autowired
private IProductService iProductService;
@Autowired
private IFileService iFileService;
@RequestMapping("save.do")
@ResponseBody
public ServerResponse productSave(HttpSession session, Product product) {
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.saveOrUpdateProduct(product);
} else {
return ServerResponse.createByErrorMessage("無許可權操作" );
}
}
Service層:
@Service("iProductService")
public class ProductServiceImpl implements IProductService {
@Autowired
private ProductMapper productMapper;
@Autowired
private CategoryMapper categoryMapper;
@Autowired
private ICategoryService iCategoryService;
//儲存或者更新商品
public ServerResponse saveOrUpdateProduct(Product product){
if(product != null) //產品不為空
{
// 獲取前端傳來的子圖字串用;分割~譬如前端傳來1.jpg,2.jpg,3.jpg,
// 然後第一塊程式碼就是將這個字串按照;來分割成字串陣列,就可以獲得所有子圖,
// 然後把第一張子圖當做主圖來儲存
if(StringUtils.isNotBlank(product.getSubImages())){
String[] subImageArray = product.getSubImages().split(",");
if(subImageArray.length > 0)
product.setMainImage(subImageArray[0]);
}
//當產品id存在則更新商品
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("新增或更新產品引數不正確");
}
}
三、後臺新增和更新商品
Controller:
@RequestMapping("set_sale_status.do")
@ResponseBody
public ServerResponse setSaleStatus(HttpSession session, Integer productId,Integer status){
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.setSaleStatus(productId,status);
}else{
return ServerResponse.createByErrorMessage("無許可權操作");
}
}
Service:
public ServerResponse<String> setSaleStatus(Integer productId,Integer status){
if(productId == null || status == null){
return ServerResponse.createByErrorCodeMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(),ResponseCode.ILLEGAL_ARGUMENT.getDesc());
}
Product product = new Product();
product.setId(productId);
product.setStatus(status);
int rowCount = productMapper.updateByPrimaryKeySelective(product);
if(rowCount > 0){
return ServerResponse.createBySuccess("修改產品銷售狀態成功");
}
return ServerResponse.createByErrorMessage("修改產品銷售狀態失敗");
}
四、後臺獲取產品詳情
Controller:
@RequestMapping("detail.do")
@ResponseBody
public ServerResponse getDetail(HttpSession session, Integer productId){
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.manageProductDetail(productId);
}else{
return ServerResponse.createByErrorMessage("無許可權操作");
}
}
Service:
@Service("iProductService")
public class ProductServiceImpl implements IProductService {
@Autowired
private ProductMapper productMapper;
@Autowired
private CategoryMapper categoryMapper;
@Autowired
private ICategoryService iCategoryService;
public ServerResponse<ProductDetailVo> manageProductDetail(Integer productId){
if(productId == null){
return ServerResponse.createByErrorCodeMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(),ResponseCode.ILLEGAL_ARGUMENT.getDesc());
}
Product product = productMapper.selectByPrimaryKey(productId);
if(product == null)
return ServerResponse.createByErrorMessage("產品已下架或者刪除");
//VO -- value object
ProductDetailVo productDetailVo = assembleProductDetailVo(product);
return ServerResponse.createBySuccess(productDetailVo);
}
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.long.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;
}
VO:
public class 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;
/*******相對於Product物件對出來的屬性******/
private String imageHost; //圖片伺服器url字首
private Integer parentCategoryId; //父分類
//...setter和getter方法...
}
簡單分析下VO:
包裝類其實就是我們專案中的pojo類,欄位與資料庫表的欄位是相同的,而vo類可以簡單理解成專門用於展示給使用者看的類。
這裡使用VO,這樣可以減少大量的工作量(也就意味著減少bug,減少風險),也不需要擔心未來的維護工作!VO還有一個好處就是是例如時間型別,返回給前臺的時候需要轉String型別,pojo轉vo是為了封裝,例如時間做成字串,或者列舉轉換成漢字,或者增加其他屬性,這樣vo的靈活性就突顯出來了。
使用流讀取配置properties配置檔案:
基礎部分可以看這個:Java基礎系列(二十)Properties類、commons-IO包
1、為什麼使用propertie
有些常量需要動態的配置,如果專案上線後,每次修改Constants.java然後再編譯,再上傳Constants.class檔案,再重啟伺服器。這樣導致很繁瑣。
如果將需要修改的配置項寫成properties檔案,程式寫好後,以後要修改資料,直接在配置檔案裡修改,程式就不用修改,也不用重新編譯了,將會在專案後期維護帶來很大的方便~!
2、propertie檔案讀取方式
① 基於 ClassLoader 讀取配置檔案:
注意:該方式只能讀取類路徑下的配置檔案,有侷限但是如果配置檔案在類路徑下比較方便。
Properties properties = new Properties();
// 使用ClassLoader載入properties配置檔案生成對應的輸入流
InputStream in = PropertiesMain.class.getClassLoader().getResourceAsStream("config/config.properties");
// 使用properties物件載入輸入流
properties.load(in);
//獲取key對應的value值
properties.getProperty(String key);
② 基於 InputStream 讀取配置檔案:
注意:該方式的優點在於可以讀取任意路徑下的配置檔案
Properties properties = new Properties();
// 使用InPutStream流讀取properties檔案
BufferedReader bufferedReader = new BufferedReader(new FileReader("E:/config.properties"));
properties.load(bufferedReader);
// 獲取key對應的value值
properties.getProperty(String key);
③ 通過 java.util.ResourceBundle 類來讀取,這種方式比使用 Properties 要方便一些:
注意:該方式的優點在於這種方式來獲取properties屬性檔案不需要加.properties字尾名,只需要檔名即可
properties.getProperty(String key);
//config為屬性檔名,放在包com.test.config下,如果是放在src下,直接用config即可
ResourceBundle resource = ResourceBundle.getBundle("com/test/config/config");
String key = resource.getString("keyWord");
因為配置檔案在類路徑下使用第一種方式比較方便,所以本專案採用的是第一種方式:
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();
}
//如果值為空,返回defaultValue
public static String getProperty(String key,String defaultValue){
String value = props.getProperty(key.trim());
if(StringUtils.isBlank(value))
value = defaultValue;
return value.trim();
}
}
joda-time和JDK自帶的SimpleDateformat的區別是什麼?
因為jdk的Date/Calender等api使用具有一定的難度,且jdk預設的有多執行緒問題,Joda-Time 令時間和日期值變得易於管理、操作和理解。事實上,易於使用是 Joda 的主要設計目標。其他目標包括可擴充套件性、完整的特性集以及對多種日曆系統的支援。並且 Joda 與 JDK 是百分之百可互操作的。
public class DateTimeUtil {
public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";
//字串轉時間
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);
}
//測試
public static void main(String[] args) {
System.out.println(DateTimeUtil.dateToStr(new Date(),"yyyy-MM-dd HH:mm:ss"));
System.out.println(DateTimeUtil.strToDate("2010-01-01 11:11:11","yyyy-MM-dd HH:mm:ss"));
}
}
五、後臺商品列表
Controller:
//商品列表
@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:
//後臺獲取商品列表 頁數 每頁多少條資料
public ServerResponse<PageInfo> getProductList(int pageNum,int pageSize){
/*********使用步驟:*******
* 1、startPage
* 2、填充自己的sql查詢邏輯
* 3、pageHelper-收尾
*************************/
//1、startPage
PageHelper.startPage(pageNum,pageSize); //頁數,每頁容量
//2、填充自己的sql查詢邏輯
List<Product> productList = productMapper.selectList(); //查詢產品列表
List<ProductListVo> productListVoList = Lists.newArrayList();
for(Product productItem : productList){
ProductListVo productListVo = assembleProductListVo(productItem);
productListVoList.add(productListVo);
}
//3、pageHelper-收尾
PageInfo pageResult = new PageInfo(productList);
pageResult.setList(productListVoList);
//使用PageInfo包裝查詢結果,只需要將pageInfo交給頁面就可以
//使用PageInfo這個類,你需要將查詢出來的list放進去:
return ServerResponse.createBySuccess(pageResult);
}
//ProducyList的組裝方法
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.long.com/"));
productListVo.setMainImage(product.getMainImage());
productListVo.setPrice(product.getPrice());
productListVo.setSubtitle(product.getSubtitle());
productListVo.setStatus(product.getStatus());
return productListVo;
}
Mapper.xml:
<!--獲取商品列表-->
<select id="selectList" resultMap="BaseResultMap" >
SELECT
<include refid="Base_Column_List"/>
from mmall_product
ORDER BY id asc
</select>
Mybatis配置:
<!-- spring和MyBatis完美整合,不需要mybatis的配置對映檔案 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath*:mappers/*Mapper.xml"></property>
<!-- 分頁外掛 -->
<property name="plugins">
<array>
<bean class="com.github.pagehelper.PageHelper">
<property name="properties">
<value>
dialect=mysql
</value>
</property>
</bean>
</array>
</property>
</bean>
<!--掃描mapper-->
<bean name="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--基本包,全部放介面-->
<property name="basePackage" value="com.mmall.dao"/>
</bean>
PageInfo底層:
//當前頁
private int pageNum;
//每頁的數量
private int pageSize;
//當前頁的數量
private int size;
//排序
private String orderBy;
//由於startRow和endRow不常用,這裡說個具體的用法
//可以在頁面中"顯示startRow到endRow 共size條資料"
//當前頁面第一個元素在資料庫中的行號
private int startRow;
//當前頁面最後一個元素在資料庫中的行號
private int endRow;
//總記錄數
private long total;
//總頁數
private int pages;
//結果集
private List<T> list;
//第一頁
private int firstPage;
//前一頁
private int prePage;
//下一頁
private int nextPage;
//最後一頁
private int lastPage;
//是否為第一頁
private boolean isFirstPage = false;
//是否為最後一頁
private boolean isLastPage = false;
//是否有前一頁
private boolean hasPreviousPage = false;
//是否有下一頁
private boolean hasNextPage = false;
//導航頁碼數
private int navigatePages;
//所有導航頁號
private int[] navigatepageNums;
Mybatis流程
從圖中可以看出,mybatis中首先要在配置檔案中配置一些東西,然後根據這些配置去建立一個會話工廠,再根據會話工廠建立會話,會話發出操作資料庫的sql語句,然後通過執行器操作資料,再使用mappedStatement對資料進行封裝,這就是整個mybatis框架的執行情況。那麼mybatis的外掛作用在哪一環節呢?它主要作用在Executor執行器與mappedeStatement之間,也就是說mybatis可以在外掛中獲得要執行的sql語句,在sql語句中新增limit語句,然後再去對sql進行封裝,從而可以實現分頁處理。
動態分頁
pageHelper分頁的底層主要是通過 aop來實現,在執行sql之前會在sql語句中新增limit offset這兩個引數。這樣就完成了動態的分頁。
我們需要用vo返回給前端。如果我們用vo裡的欄位,是和pojo總會有不一致的地方。例如時間的型別,又例如新增的一些列舉狀態等。那麼為了自動分頁,我們會用dao層找到原始的pojoList,(因為pageHelper是對dao層在執行mapper的時候才會動態分頁,所以我們要先執行一下mapper)然後轉換成vo。那麼其實這兩個list的集合的分頁引數是一致的。所以用了一個比較巧妙的辦法,來把vo進行分頁。
六、後臺商品搜尋功能
Controller:
//商品搜尋功能
@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("無許可權操作");
}
Service:
//商品搜尋功能
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);
}
Mapper.xml:
<!--//通過名字或者id查詢商品-->
<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>
七、後臺商品圖片上傳功能
Controller:
//上傳檔案功能
@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("無許可權操作");
}
}
Service:
@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(); //獲取檔案的名字
String fileExtensionName = fileName.substring(fileName.lastIndexOf(".")+1); //獲取副檔名,例如檔名為:abc.jpg
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); //建立檔案(假設在upload資料夾下)
try {
file.transferTo(targetFile); //檔案已經上傳成功到了upload資料夾
FTPUtil.uploadFile(Lists.newArrayList(targetFile)); //已經上傳到ftp伺服器上
targetFile.delete(); //上傳成功之後,就刪除建立的的檔案,如upload下面的檔案
} catch (IOException e) {
logger.error("上傳檔案異常",e);
return null;
}
return targetFile.getName();
}
}
FTPUtil:
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");
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); //上傳檔案到FTP伺服器下面的img目錄下
logger.info("開始連線ftp伺服器,結束上傳,上傳結果:{}");
return result;
}
//上傳檔案
private boolean uploadFile(String remotePath,List<File> fileList) throws IOException { //遠端路徑
boolean uploaded = true; //是否傳了
FileInputStream fis = null;
if(connectServer(this.ip,this.port,this.user,this.pwd)){ //連線FTP伺服器
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:
//富文字中圖片上傳
@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;
}
}
八、前臺功能
Controller:
//查詢商品詳情
@RequestMapping("detail.do")
@ResponseBody
public ServerResponse<ProductDetailVo> detail(Integer productId){
return iProductService.getProductDetail(productId);
}
//商品搜尋
@RequestMapping("list.do")
@ResponseBody
public ServerResponse<PageInfo> list(@RequestParam(value = "keyword",required = false)String keyword,
@RequestParam(value = "categoryId",required = false)Integer categoryId,
@RequestParam(value = "pageNum",defaultValue = "1") int pageNum,
@RequestParam(value = "pageSize",defaultValue = "10") int pageSize,
@RequestParam(value = "orderBy",defaultValue = "") String orderBy){
return iProductService.getProductByKeywordCategory(keyword,categoryId,pageNum,pageSize,orderBy);
}
Service:
//前臺獲取商品資訊
public ServerResponse<ProductDetailVo> getProductDetail(Integer productId){
if(productId == null)
return ServerResponse.createByErrorCodeMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(),ResponseCode.ILLEGAL_ARGUMENT.getDesc());
Product product = productMapper.selectByPrimaryKey(productId);
if(product == null)
return ServerResponse.createByErrorMessage("產品已下架或者刪除");
if(product.getStatus() != Const.ProductStatusEnum.ON_SALE.getCode())
return ServerResponse.createByErrorMessage("產品已下架或者刪除");
ProductDetailVo productDetailVo = assembleProductDetailVo(product);
return ServerResponse.createBySuccess(productDetailVo);
}
//獲取商品列表,搜尋商品,排序
public ServerResponse<PageInfo> getProductByKeywordCategory(String keyword,Integer categoryId,int pageNum,int pageSize,String orderBy){
if(StringUtils.isBlank(keyword) && categoryId == null)
return ServerResponse.createByErrorCodeMessage(ResponseCode.ILLEGAL_ARGUMENT.getCode(),ResponseCode.ILLEGAL_ARGUMENT.getDesc());
List<Integer> categoryIdList = new ArrayList<Integer>();
if(categoryId != null){
Category category = categoryMapper.selectByPrimaryKey(categoryId); //獲取分類
if(category == null && StringUtils.isBlank(keyword)){ //沒有該分類,並且還沒有關鍵字,這個時候返回一個空的結果集,不報錯
PageHelper.startPage(pageNum,pageSize);
List<ProductListVo> productListVoList = Lists.newArrayList();
PageInfo pageInfo = new PageInfo(productListVoList);
return ServerResponse.createBySuccess(pageInfo);
}
categoryIdList = iCategoryService.selectCategoryAndChildrenById(category.getId()).getData(); //根據id查詢此節點id和其子節點id的集合
}
if(StringUtils.isNotBlank(keyword))
keyword = new StringBuilder().append("%").append(keyword).append("%").toString(); //%關鍵字%
PageHelper.startPage( pageNum , pageSize );
//排序處理
if(StringUtils.isNotBlank(orderBy)){ //如果排序關鍵字不為空
if(Const.ProductListOrderBy.PRICE_ASC_DESC.contains(orderBy)){ //price_desc
String[] orderByArray = orderBy.split("_");
PageHelper.orderBy(orderByArray[0]+" "+orderByArray[1]); //格式為price desc
}
}
List<Product> productList = productMapper.selectByNameAndCategoryIds(StringUtils.isBlank(keyword)?null:keyword,categoryIdList.size()==0?null:categoryIdList);
List<ProductListVo> productListVoList = Lists.newArrayList();
for(Product product : productList){
ProductListVo productListVo = assembleProductListVo(product); //封裝為VO
productListVoList.add(productListVo);
}
PageInfo pageInfo = new PageInfo(productList);
pageInfo.setList(productListVoList);
return ServerResponse.createBySuccess(pageInfo);
}
動態排序
兩種方法:
1、方式1
//其中A為排序依據的欄位名,B為排序規律,desc為降序,asc為升序
PageHelper.startPage(pageNum , pageSize);
PageHelper.orderBy("A B");
2、方式2
//其中A為排序依據的欄位名,B為排序規律,desc為降序,asc為升序
String orderBy="A B";
PageHelper.startPage(pageNum, pageSize, orderBy);
九、FTP伺服器
1、FTP是什麼?
FTP是File Transfer Protocol(檔案傳輸協議)的英文簡稱,用於Internet上的控制檔案的雙向傳輸。簡單的說,支援FTP協議的伺服器就是FTP伺服器。FTP是一個客戶機/伺服器系統。使用者通過一個支援FTP協議的客戶機程式,連線到在遠端主機上的FTP伺服器程式。使用者通過客戶機程式向伺服器程式發出命令,伺服器程式執行使用者所發出的命令,並將執行的結果返回到客戶機。
2、為什麼使用FTP伺服器
從一個小網站說起。一臺伺服器也就足夠了。檔案伺服器,資料庫,還有應用都部署在一臺機器,俗稱ALL IN ONE。隨著我們使用者越來越多,訪問越來越大,硬碟,CPU,記憶體等都開始吃緊。一臺伺服器已經滿足不了,我們將資料服務和應用服務分離,給應用伺服器配置更好的 CPU,記憶體。而給資料伺服器配置更好更大的硬碟。分離之後提高一定的可用性,例如Files Server掛了,我們還是可以操作應用和資料庫等。
3、對比FTP和FastDFS
單獨部署的檔案伺服器是網際網路專案必不可少的一項。簡單的話可以採用FTP做檔案伺服器,但是專案訪問量持續增加的話,必要考慮檔案伺服器的擴充套件性與高可用。這時候採用FastDFS是比較明智的,當伺服器容量不夠用時,FastDFS可以快速的進行線性擴容。FastDFS是用c語言編寫的一款開源的分散式檔案系統。FastDFS為網際網路量身定製,充分考慮了冗餘備份、負載均衡、線性擴容等機制,並注重高可用、高效能等指標,使用FastDFS很容易搭建一套高效能的檔案伺服器叢集提供檔