1.提交代码git截图
差一周不到三个月时间提交代码不少于130次,不算合并代码。
2. 统计相关的代码部分实现
controller的实现
package com.zjht.shoplineserver.controller.bigdata;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zjht.shoplineserver.dao.mapper.SalesDataAnalysisMapper;
import com.zjht.shoplineserver.model.vo.MsgEnum;
import com.zjht.shoplineserver.model.vo.ResultInfo;
import org.apache.commons.lang.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
@RequestMapping("/sales/analysis")
@RestController
public class SalesDataAnalysisController {
@Resource
private SalesDataAnalysisMapper salesDataAnalysisMapper;
List<String> types = new ArrayList<String>(){
{
this.add("官网");
this.add("公众号");
this.add("小程序");
}
};
@RequestMapping("/channel")
public ResultInfo channel(@RequestBody JSONObject args) {
ResultInfo resultInfo = new ResultInfo();
try {
String type = args.getString("type");
if(StringUtils.isEmpty(type))
type = "1";
String beginTm = getBeginTm(type);
String endTm = LocalDate.now().plusDays(1l).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))+" 00:00:00";
List<Map<String, Object>> stats = salesDataAnalysisMapper.channel(beginTm , endTm);
List<Map<String, Object>> newStats = new ArrayList<>();
if(stats.size() < types.size()) {
Map<String, BigDecimal> maps = new HashMap<>();
for (Map<String, Object> one : stats) {
maps.put(one.get("ItemKey").toString(), (BigDecimal) one.get("ItemValue"));
}
for (String t : types) {
Map<String, Object> newMap = new HashMap<>();
newMap.put("ItemKey", t);
BigDecimal d = maps.get(t);
if (d != null) {
newMap.put("ItemValue", d);
} else {
newMap.put("ItemValue", BigDecimal.ZERO);
}
newStats.add(newMap);
}
} else {
newStats = stats;
}
resultInfo.setData(newStats);
} catch (Exception e) {
resultInfo.setCode(MsgEnum.EXISTS_ERROR.getCode());
resultInfo.setMsg(e.getMessage());
}
return resultInfo;
}
private String getBeginTm(String type) throws Exception {
String beginTm = "";
if(type.equals("1")) {
beginTm = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))+" 00:00:00";
} else if(type.equals("2")) {
beginTm = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM"))+"-01 00:00:00";
} else if(type.equals("3")) {
beginTm = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy"))+"-01-01 00:00:00";
} else {
throw new Exception("type类型参数不对!");
}
return beginTm;
}
@RequestMapping("/salesType")
public ResultInfo salesType(@RequestBody JSONObject args) {
ResultInfo resultInfo = new ResultInfo();
try {
String type = args.getString("type");
if(StringUtils.isEmpty(type))
type = "1";
String beginTm = getBeginTm(type);
String endTm = LocalDate.now().plusDays(1l).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))+" 00:00:00";
List<Map<String, Object>> stats = salesDataAnalysisMapper.salesType(beginTm , endTm);
Map<String, List<Map>> map = stats.stream().collect(Collectors.groupingBy(
(Map m) -> (String)m.get("type"))
);
List<Object> data = new ArrayList<>();
Map<String, Object> newMapResult = null;
List<Map> newList = null;
for(Map.Entry<String, List<Map>> entry : map.entrySet()) {
newMapResult = new HashMap<>();
newList = new ArrayList<>();
List<Map> oldMap = entry.getValue();
Map nm = null;
for(Map om : oldMap) {
nm = new HashMap();
nm.put("ItemValue", om.get("ItemValue"));
nm.put("ItemKey", om.get("ItemKey"));
newList.add(nm);
}
newMapResult.put("type", entry.getKey());
newMapResult.put("value", newList);
data.add(newMapResult);
resultInfo.setData(data);
}
}catch (Exception e) {
resultInfo.setCode(MsgEnum.EXISTS_ERROR.getCode());
resultInfo.setMsg(e.getMessage());
}
return resultInfo;
}
@RequestMapping("/salesAmountTop10") // 商铺
public ResultInfo salesAmountTop10(@RequestBody JSONObject args) {
ResultInfo resultInfo = new ResultInfo();
try {
List<Map<String, Object>> stats = this.returnStats(args, 0);
resultInfo.setData(stats);
}catch (Exception e) {
resultInfo.setCode(MsgEnum.EXISTS_ERROR.getCode());
resultInfo.setMsg(e.getMessage());
}
return resultInfo;
}
@RequestMapping("/orderAmountTop10")
public ResultInfo orderAmountTop10(@RequestBody JSONObject args) {
ResultInfo resultInfo = new ResultInfo();
try {
List<Map<String, Object>> stats = this.returnStats(args, 100);
resultInfo.setData(stats);
}catch (Exception e) {
resultInfo.setCode(MsgEnum.EXISTS_ERROR.getCode());
resultInfo.setMsg(e.getMessage());
}
return resultInfo;
}
private List<Map<String, Object>> returnStats(JSONObject args, int opt) throws Exception {
List<Map<String, Object>> stats = null;
try {
String type = args.getString("type");
if(StringUtils.isEmpty(type))
type = "1";
String beginTm = "";
String endTm = "";
if(type.equals("4")) {
beginTm = args.getString("startTime") + " 00:00:00";;
endTm = args.getString("endTime") + " 00:00:00";;
} else {
beginTm = getBeginTm(type);
endTm = LocalDate.now().plusDays(1l).format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + " 00:00:00";
}
if(opt == 0)
stats = salesDataAnalysisMapper.salesAmountTop10(beginTm , endTm);
else
stats = salesDataAnalysisMapper.orderAmountTop10(beginTm , endTm);
}catch (Exception e) {
throw new Exception("统计失败,失败原因:"+e);
}
return stats;
}
@RequestMapping("/processSatisfy")
public ResultInfo processSatisfy(@RequestBody JSONObject args) {
ResultInfo resultInfo = new ResultInfo();
try {
String type = args.getString("type");
if(StringUtils.isEmpty(type))
type = "1";
String beginTm = getBeginTm(type);
String endTm = LocalDate.now().plusDays(1l).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))+" 00:00:00";
List<Map<String, Object>> stats = salesDataAnalysisMapper.processSatisfy(beginTm, endTm);
resultInfo.setData(stats);
}catch (Exception e) {
resultInfo.setCode(MsgEnum.EXISTS_ERROR.getCode());
resultInfo.setMsg(e.getMessage());
}
return resultInfo;
}
private List<String> dics = new ArrayList<String>(){
{
this.add("curDayDayCount");
this.add("lastDayDayCount");
this.add("curMonthMonthCount");
this.add("lastMonthMonthCount");
this.add("curYearYearCount");
this.add("lastYearYearCount");
}
};
@RequestMapping("/qoqSatisfy")
public ResultInfo qoqSatisfy() {
ResultInfo resultInfo = new ResultInfo();
try {
Map<String, String> args = new HashMap<>();
// 当日的开始 结束时间
String curDtBeginTm = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))+" 00:00:00";
String curDtEndTm = LocalDate.now().plusDays(1l).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))+" 00:00:00";
args.put("curDtBeginTm", curDtBeginTm);
args.put("curDtEndTm", curDtEndTm);
// 昨天的开始和结束时间
String preDtBeginTm = LocalDate.now().minusDays(1l).format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))+" 00:00:00";
String preDtEndTm = curDtBeginTm;
args.put("preDtBeginTm", preDtBeginTm);
args.put("preDtEndTm", preDtEndTm);
// 当月开始和结束时间
String curMonthBeginTm = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy-MM"))+"-01 00:00:00";
String curMonthEndTm = curDtEndTm;
args.put("curMonthBeginTm", curMonthBeginTm);
args.put("curMonthEndTm", curMonthEndTm);
// 上一个月开始和结束时间
String preMonthBeginTm = LocalDate.now().minusMonths(1).format(DateTimeFormatter.ofPattern("yyyy-MM"))+"-01 00:00:00";
String preMonthEndTm = curMonthBeginTm;
args.put("preMonthBeginTm", preMonthBeginTm);
args.put("preMonthEndTm", preMonthEndTm);
// 当年的开始和结束时间
String curYearBeginTm = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyy"))+"-01-01 00:00:00";
String curYearEndTm = curDtEndTm;
args.put("curYearBeginTm", curYearBeginTm);
args.put("curYearEndTm", curYearEndTm);
// 上一年的开始和结束时间
String preYearBeginTm = LocalDate.now().minusYears(1l).format(DateTimeFormatter.ofPattern("yyyy"))+"-01-01 00:00:00";
String preYearEndTm = curYearBeginTm;
args.put("preYearBeginTm", preYearBeginTm);
args.put("preYearEndTm", preYearEndTm);
List<Integer> stats = salesDataAnalysisMapper.qoqSatisfy(args);
Map<String, Object> dataMap = new HashMap<>();
int i = 0;
for(Integer x : stats) {
dataMap.put(dics.get(i++), x);
}
resultInfo.setData(new ArrayList<Map>(){{this.add(dataMap);}});
}catch (Exception e) {
resultInfo.setCode(MsgEnum.EXISTS_ERROR.getCode());
resultInfo.setMsg(e.getMessage());
}
return resultInfo;
}
}
mapper实现
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zjht.shoplineserver.dao.mapper.SalesDataAnalysisMapper">
<select id="channel" resultType="java.util.HashMap">
SELECT sum(amount) as ItemValue ,
CASE platform
WHEN 3 THEN '官网'
WHEN 4 THEN '公众号'
ELSE '小程序' END as ItemKey
FROM s_online_order WHERE 1=1
and completedate BETWEEN #{beginTm} and #{endTm}
and `status` = 9
GROUP BY platform
</select>
<select id="salesType" resultType="java.util.HashMap">
SELECT sum(amount) as ItemValue,
CASE platform
WHEN 3 THEN '官网'
WHEN 4 THEN '公众号'
ELSE '小程序' END as ItemKey,
CASE type
WHEN 1 THEN '门票'
WHEN 2 THEN '美食'
WHEN 3 THEN '酒店'
WHEN 4 THEN '特产'
ELSE '跟团游' END as type
FROM s_online_order WHERE 1=1
and completedate BETWEEN #{beginTm} and #{endTm}
and `status` = 9
GROUP BY type,platform
</select>
<select id="salesAmountTop10" resultType="java.util.HashMap">
SELECT
sum(amount) as ItemValue,
shopname as ItemKey
FROM s_online_order
WHERE 1=1
and completedate BETWEEN #{beginTm} and #{endTm}
and `status`= 9
GROUP BY shopname order by ItemValue desc limit 10
</select>
<select id="orderAmountTop10" resultType="java.util.HashMap">
SELECT
sum(quantity) as ItemValue,
shopname as ItemKey
FROM s_online_order
WHERE 1=1
and completedate BETWEEN #{beginTm} and #{endTm}
and `status`= 9
GROUP BY shopname order by ItemValue desc limit 10
</select>
<select id="processSatisfy" resultType="java.util.HashMap">
SELECT count(*) as counts, '满意' as ItemKey FROM s_member_commercial_suggest WHERE `status` < 4
and createdate BETWEEN #{beginTm} and #{endTm}
union
SELECT count(*) as counts, '不满意' as ItemKey FROM s_member_commercial_suggest WHERE `status` > 3
and createdate BETWEEN #{beginTm} and #{endTm}
</select>
<select id="qoqSatisfy" resultType="java.lang.Integer">
SELECT count(*) as count FROM s_member_commercial_suggest
WHERE createdate BETWEEN #{curDtBeginTm} and #{curDtEndTm}
union all
SELECT count(*) as count FROM s_member_commercial_suggest
WHERE createdate BETWEEN #{preDtBeginTm} and #{preDtEndTm}
union all
SELECT count(*) as count FROM s_member_commercial_suggest
WHERE createdate BETWEEN #{curMonthBeginTm} and #{curMonthEndTm}
union all
SELECT count(*) FROM s_member_commercial_suggest
WHERE createdate BETWEEN #{preMonthBeginTm} and #{preMonthEndTm}
union all
SELECT count(*) FROM s_member_commercial_suggest
WHERE createdate BETWEEN #{curYearBeginTm} and #{curYearEndTm}
union all
SELECT count(*) FROM s_member_commercial_suggest
WHERE createdate BETWEEN #{preYearBeginTm} and #{preYearEndTm}
</select>
</mapper>
3.排序代码实现
代码样例 做成模板,其他调用处模板化调用
package com.zjht.currentserver.model.zyb;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import javax.persistence.Table;
import java.util.*;
import java.util.stream.Collectors;
/***
* 重排序的工具类
*/
@Component
public class ReOrderUtils {
Logger log = LoggerFactory.getLogger(ReOrderUtils.class);
@Autowired
JdbcTemplate jdbcTemplate;
private static final String COMMON_ID_SORT_QUERY_TEMPLATE = "select id, #{sortColumn} from #{tabName} where #{argsSql} order by #{sortColumn}" ;
private static final String COMMON_ID_SORT_QUERY_TEMPLATE_NO_WHERE = "select id, #{sortColumn} from #{tabName} order by #{sortColumn}" ;
private static final String COMMON_ID_SORT_UPDATE_TEMPLATE = "update #{tabName} set #{sortColumn} = #{newSort} where id = #{id}";
/***
* 对clazz类型的参数字段相关的数据重新编号
* @param clazz
* @param argsColumns
* @param argsVals
* @param sortColumn
* @throws Exception
*/
public void realignOrder(Class clazz, String[] argsColumns, Object[] argsVals, String sortColumn) throws Exception{
this.realignOrder(clazz, argsColumns, argsVals, sortColumn, new HashSet<>());
}
public void realignOrder(Class clazz, String sortColumn) throws Exception{
this.realignOrder(clazz, null, new Object[]{}, sortColumn, new HashSet<>());
}
public void realignOrder(Class clazz, String sortColumn, Set<Integer> deleteIds) throws Exception{
this.realignOrder(clazz, null, new Object[]{}, sortColumn, deleteIds);
}
/**
*
* @param clazz
* @param argsColumns
* @param argsVals
* @param sortColumn
* @param deleteIds
* @throws Exception
*/
public void realignOrder(Class clazz, String[] argsColumns, Object[] argsVals, String sortColumn, Set<Integer> deleteIds) throws Exception{
try {
Table table = (Table)clazz.getAnnotation(Table.class);
String tabName = table.name();
String argsSql = "";
if(argsColumns != null)
argsSql = this.whereSql(argsColumns, argsVals);
String sql = ReOrderUtils.COMMON_ID_SORT_QUERY_TEMPLATE
.replace("#{tabName}", tabName)
.replace("#{argsSql}", argsSql)
.replace("#{sortColumn}", sortColumn);
if(argsColumns == null) {
sql = ReOrderUtils.COMMON_ID_SORT_QUERY_TEMPLATE_NO_WHERE
.replace("#{tabName}", tabName)
.replace("#{sortColumn}", sortColumn);
}
Map<Integer, Integer> origOnes = jdbcTemplate.queryForList(sql, argsVals).stream().collect(Collectors.toMap(s->(Integer)s.get("id"), s -> (Integer)s.get(sortColumn)));
// 原来的排序
Map<Integer, Integer> originalOrderOnes = origOnes.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.collect(
Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldVal, newVal) -> oldVal,
LinkedHashMap::new
)
);
this.executeUpdateSortNo(origOnes, originalOrderOnes, deleteIds, tabName, sortColumn);
} catch (Exception e) {
log.info("重排序号失败,原因:{}", e);
throw e;
}
}
private void executeUpdateSortNo(Map<Integer, Integer> origOnes, Map<Integer, Integer> originalOrderOnes, Set<Integer> deleteIds, String tabName, String sortColumn) {
String sql = "";
int newNo = 1;
int newSort = 0; // 新顺序号
int oldSort = 0; // 老顺序号
for (Map.Entry<Integer, Integer> entry : originalOrderOnes.entrySet()) {
if(deleteIds.contains(entry.getKey())) continue;
newSort = newNo++;
oldSort = origOnes.get(entry.getKey());
if (newSort == oldSort) continue;
sql = COMMON_ID_SORT_UPDATE_TEMPLATE
.replace("#{tabName}", tabName)
.replace("#{sortColumn}", sortColumn)
.replace("#{newSort}", newSort+"")
.replace("#{id}", entry.getKey()+"");
jdbcTemplate.execute(sql);
}
}
public void realignOrder(Class clazz, String argsSql, String sortColumn, Set<Integer> deleteIds) throws Exception{
try {
Table table = (Table)clazz.getAnnotation(Table.class);
String tabName = table.name();
String sql = ReOrderUtils.COMMON_ID_SORT_QUERY_TEMPLATE
.replace("#{tabName}", tabName)
.replace("#{argsSql}", argsSql)
.replace("#{sortColumn}", sortColumn);
Object[] args = new Object[]{};
Map<Integer, Integer> origOnes = jdbcTemplate.queryForList(sql, args).stream().collect(Collectors.toMap(s->(Integer)s.get("id"), s -> (Integer)s.get(sortColumn)));
// 原来的排序
Map<Integer, Integer> originalOrderOnes = origOnes.entrySet().stream()
.sorted(Map.Entry.comparingByValue())
.collect(
Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue,
(oldVal, newVal) -> oldVal,
LinkedHashMap::new
)
);
this.executeUpdateSortNo(origOnes, originalOrderOnes, deleteIds, tabName, sortColumn);
} catch (Exception e) {
log.info("重排序号失败,原因:{}", e);
throw e;
}
}
/**
* 前后的记录要更新新的顺序编号
* @param sqlTemplate sql 模板
* @param id 主键
* @param newSort 新的顺序号
* @param oldSort 旧的顺序号
* @param type 类型
* @param up 是否向上
* @throws Exception
*/
public void changeUpDownOrder(String sqlTemplate,int id, int newSort, int oldSort, String type, boolean up) throws Exception{
this.changeUpDownOrder(sqlTemplate, id, newSort, oldSort, type, up, new HashMap<>());
}
public void changeUpDownOrder(String sqlTemplate,int id, int newSort, int oldSort, boolean up) throws Exception{
this.changeUpDownOrder(sqlTemplate, id, newSort, oldSort, "", up, new HashMap<>());
}
/**
*
* @param sqlTemplate sql 模板
* @param id 主键
* @param newSort 新的顺序号
* @param oldSort 旧的顺序号
* @param type 类型
* @param up 是否向上
* @param exts 扩展的判断字段及值
* @throws Exception
*/
public void changeUpDownOrder(String sqlTemplate,int id, int newSort, int oldSort, String type, boolean up, Map<String,String> exts) throws Exception{
String updateSql = sqlTemplate.replace("#{newSort}", newSort+"")
.replace("#{oldSort}", oldSort+"")
.replace("#{type}", type);
for(Map.Entry<String, String> entry : exts.entrySet()) {
updateSql = updateSql.replace(entry.getKey(), entry.getValue());
}
String upStr = up? "上":"下";
try {
jdbcTemplate.execute(updateSql);
}catch (Exception e) {
log.info("id为{}的{}一条数据的编号从{}变到{}失败!", id,upStr, oldSort, newSort);
throw new Exception("id为"+id+"的" + upStr + "一条数据的编号从"+oldSort+"变到"+newSort+"失败!");
}
}
/***
* 拼接where 子句
* @param argsColumns 参数字段数组
* @param argsVals 参数字段对应值数组
* @return
*/
private String whereSql(String[] argsColumns,Object[] argsVals){
String sqlSnippet = "";
int i = 0;
int length = argsColumns.length;
for(String argsColumn : argsColumns) {
if(argsVals[i] == null) {
i++;
continue;
//sqlSnippet += argsColumn + " is null ";
} else {
sqlSnippet += argsColumn + "=? ";
}
if(i++ < length-1){
sqlSnippet += " and ";
}
}
return sqlSnippet;
}
}
上下移操作的函数调用
@Autowired
ReOrderUtils reOrderUtils;
@Autowired
JdbcTemplate jdbcTemplate;
@Transactional(rollbackFor = Exception.class)
@Override
public ResultInfo upordownmoveindex(int id, String type,String sceniccategoryid) throws Exception{
reOrderUtils.realignOrder(SResourceScenic.class, "sortindex");
// 当前编号的数据
SResourceScenic curNeedChanged = null;
Optional<SResourceScenic> byId = scenicDao.findById(id);
if (byId.isPresent()) {
curNeedChanged = byId.get();
} else {
logger.info("未获取id为{}的数据!", id);
throw new Exception("未获取id为"+id+"的数据!");
}
Map<String, Object> map = new HashMap<>();
int currNeedSort = curNeedChanged.getSortindex();
if (type.equals("up")) {
if(currNeedSort > 1) {
int nowPrevSort = currNeedSort - 1;
reOrderUtils.changeUpDownOrder(ScenicDao.UPDATE_SORT_SQL,id, currNeedSort, nowPrevSort, type.equals("up"));
// 当前节点上移一个编号
curNeedChanged.setSortindex(nowPrevSort);
} else {
logger.info("id为{}的数据已经是置顶数据!", id);
}
} else if (type.equals("down")) {
int maxNo = jdbcTemplate.queryForObject(ScenicDao.COUNT_SQL, Integer.class);
if(currNeedSort < maxNo) {
int nowNextSort = currNeedSort + 1;
reOrderUtils.changeUpDownOrder(ScenicDao.UPDATE_SORT_SQL,id, currNeedSort, nowNextSort, type.equals("up"));
// 当前节点下移一个编号
curNeedChanged.setSortindex(nowNextSort);
} else {
logger.info("id为{}的数据已经是最后一个序号的数据!", id);
}
} else if (type.equals("stick")) {
if(currNeedSort > 1) {
scenicDao.findAllBySortIndexAndType(curNeedChanged.getSortindex()).stream()
.filter(Objects::nonNull)
.forEach(o-> o.setSortindex(o.getSortindex() + 1));
// 当前节点置顶
curNeedChanged.setSortindex(1);
} else {
logger.info("id为{}的数据已经是置顶数据!", id);
}
}
return new ResultInfo(ServerMsgEnum.SUCCESS.getServerCode(), ServerMsgEnum.SUCCESS.getServerMsg(), map);
}
删除操作
public void deleteByIds(String ids, String sceniccategoryid ) throws Exception{
if( StringUtils.isEmpty( ids)) {
logger.info("删除的主键不能为空!");
throw new Exception("删除的主键不能为空!");
}
/*if(StringUtils.isEmpty(sceniccategoryid)) {
logger.info("景区景点的类型不能为空!");
throw new Exception("景区景点的类型不能为空!");
}*/
String[] idStr = ids.split(",");
Arrays.stream(idStr).filter(StringUtils::isNotEmpty).map(Integer::valueOf).forEach(o->scenicDao.deleteById(o));
Set<Integer> deleteIds = Arrays.stream(idStr).filter(StringUtils::isNotEmpty).map(Integer::valueOf).collect(Collectors.toSet());
// 删除后进行该类型的重新排序
reOrderUtils.realignOrder(SResourceScenic.class, "sortindex", deleteIds);
}
4.支付代码
封装调用支付网关的接口
package com.zjht.currentserver.model.zyb;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zjht.currentserver.model.po.YNCredit.BaseInputVo;
import com.zjht.currentserver.model.po.YNCredit.Constants;
import com.zjht.currentserver.model.zyb.ynrcpay.YNCreditSignUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.*;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@Component
public class HttpUtils {
private static Logger logger = LoggerFactory.getLogger(HttpUtils.class);
@Resource
RestTemplate restTemplate;
/***
*
* @param inputVo
* @return
* @throws Exception
*/
public String postYNCreditGateWay(BaseInputVo inputVo) throws Exception{
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("request", inputVo);
/***开始设置头信息*/
HttpHeaders headers = new HttpHeaders();
MediaType type = MediaType.parseMediaType("application/json; charset=UTF-8");
headers.setContentType(type);
headers.add("Accept", MediaType.APPLICATION_JSON.toString());
// 设置证书
headers.add("ynrcc-cert",Constants.gzh_ynrcc_cert);
// 设置签名
// 签名串
String signTxt = YNCreditSignUtils.sign(Constants.My_priKey, JSON.toJSONString(requestBody));
logger.info("请求签名串:{}",signTxt);
headers.add("ynrcc-sign",signTxt);
restTemplate.getMessageConverters().set(1,new StringHttpMessageConverter(StandardCharsets.UTF_8));
HttpEntity<String> formEntity = new HttpEntity<String>(JSON.toJSONString(requestBody), headers);
/***结束设置头信息*/
/**开始调用微信小程序支付接口**/
logger.info("云南农信小程序公众号支付网关地址{}", Constants.Yunnan_Rural_Credit_url_with_sign);
String payResult = null;
HttpHeaders responseHeaders = null;
try {
ResponseEntity<String> responseEntity = restTemplate.postForEntity(Constants.Yunnan_Rural_Credit_url_with_sign, formEntity, String.class);
responseHeaders = responseEntity.getHeaders();
payResult = responseEntity.getBody();
}catch (Exception e) {
logger.error("调用云南农信支付网关地址{}失败,原因{}",Constants.Yunnan_Rural_Credit_url_with_sign, e);
}
/**结束调用微信小程序支付接口**/
if(responseHeaders == null) {
logger.error("调用云南农信支付网关地址{}失败,响应报文头不能为空!",Constants.Yunnan_Rural_Credit_url_with_sign);
throw new Exception("调用云南农信支付网关地址"+ Constants.Yunnan_Rural_Credit_url_with_sign+"失败,响应报文头不能为空!");
}
String responseSign = responseHeaders.getFirst("ynrcc-sign");
// 验签测试
if(Constants.LOCAL_CHECK_TEST) {
char s = responseSign.charAt(0);
char replace = '0';
if(s == '0') {
replace = '1';
}
String last = responseSign.substring(1);
responseSign = replace + last;
}
if(StringUtils.isEmpty(responseSign)) {
logger.error("调用云南农信支付网关地址{}失败,响应报文头签名不能为空!",Constants.Yunnan_Rural_Credit_url_with_sign);
throw new Exception("调用云南农信支付网关地址"+ Constants.Yunnan_Rural_Credit_url_with_sign+"失败,响应报文头签名不能为空!");
}
boolean checkResponse = YNCreditSignUtils.verifySign(Constants.Yunnan_Rural_Credit_pubKey, payResult, responseSign);
if(!checkResponse) {
logger.error("调用云南农信支付网关地址{}失败,响应报文验签失败!",Constants.Yunnan_Rural_Credit_url_with_sign);
throw new Exception("调用云南农信支付网关地址"+ Constants.Yunnan_Rural_Credit_url_with_sign+"失败,响应报文验签失败!");
}
/**开始解析微信小程序支付接口报文**/
Throwable t = null;
if(StringUtils.isEmpty(payResult)) {
logger.error("调用云南农信支付网关响应报文不能为空!");
throw new Exception("调用云南农信支付网关响应报文不能为空!");
}
JSONObject jsonObject = null;
try {
jsonObject = JSONObject.parseObject(payResult);
}catch (Exception e) {
t = e;
}
if(jsonObject == null) {
logger.error("响应报文反序列化失败!原因:{}", t);
throw new Exception("响应报文反序列化失败!原因"+t);
}
String resText = jsonObject.getString("response");
if(StringUtils.isEmpty(resText)) {
logger.error("报文信息有误,没有一级response节点信息");
throw new Exception("报文信息有误,没有一级response节点信息");
}
return resText;
}
}
某行微信预付款的代码实现
package com.zjht.payserver.service.impl;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.github.wxpay.sdk.WXPayConstants;
import com.github.wxpay.sdk.WXPayUtil;
import com.zjht.currentserver.model.po.YNCredit.*;
import com.zjht.currentserver.model.zyb.HttpUtils;
import com.zjht.currentserver.model.zyb.StringUtil;
import com.zjht.currentserver.model.zyb.ynrcpay.SeqNoGenerator;
import com.zjht.currentserver.model.zyb.ynrcpay.YNCreditSignUtils;
import com.zjht.payserver.feign.FeignOrderService;
import com.zjht.payserver.mapper.PayInfoMapper;
import com.zjht.payserver.service.YNCreditPayService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Service
public class YNCreditPayServiceImpl implements YNCreditPayService {
private Logger logger = LoggerFactory.getLogger(YNCreditPayServiceImpl.class);
@Resource
RestTemplate restTemplate;
@Resource
FeignOrderService feignOrderService;
@Resource
HttpUtils httpUtils;
@Autowired
private ValueOperations<String,Object> valueOperations;
@Override
public String wxPay(JSONObject json, String pConf) throws Exception{
// 参数校验
if(StringUtils.isEmpty(pConf)) {
throw new Exception("支付配置信息不能为空!");
}
JSONObject wxconf = JSONObject.parseObject(pConf);
String notifyUrl = wxconf.getString("notify_url"); // 回调地址
String mchId = wxconf.getString("mch_id"); // 商户号
String appId = wxconf.getString("appid"); //"wx6be77239f2397ff4";//
String openid = json.getString("openid"); // openid
if(StringUtils.isEmpty(openid))
throw new Exception("openid不能为空!");
String tradeNo = json.getString("out_trade_no");
if(StringUtils.isEmpty(tradeNo)) {
throw new Exception("tradeNo不能为空!");
}
// 预付单再缓存中则 从缓存返回
Object preOrderInfo = valueOperations.get(tradeNo);
if(preOrderInfo!= null){
return preOrderInfo.toString();
}
//存入attach信息
String attach = wxconf.getString("attach");
if(StringUtils.isNotBlank(attach)){
valueOperations.set("yn_attach_"+tradeNo,attach,1, TimeUnit.DAYS);
}
// 查询订单数量
String totalNum = json.getString("totalNum");
if(StringUtils.isEmpty(totalNum)) {
totalNum = "1";
}
String totalAmt = json.getString("total_fee");
if(StringUtils.isEmpty(totalAmt) ) {
throw new Exception("交易金额不能为空!");
}
// 组装请求报文
String orderDesc = json.getString("body");
PayInputVo payInputVo =PayInputVo.builder()
.tranCode(PayInputVo.TRANCODE)
.merId(Constants.gzh_merId) // 商户id
.temId(Constants.gzh_temId) // 终端id
.seqNo(SeqNoGenerator.generate())
.tradeNo(tradeNo)
.txnTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))) // 取当前时间
.totalNum(totalNum)
.totalAmt(totalAmt) // 交易金额
.orderDesc(orderDesc)
.subOpenId(openid)
.subAppId(appId)
.notifyUrl(notifyUrl) // 我们定义写死的
.build();
String payResult = httpUtils.postYNCreditGateWay(payInputVo);
String result = this.parseResponseExeResult(payResult, json, pConf);
if(StringUtils.isEmpty(result)) {
logger.error("返回解析的三级微信支付信息错误!");
throw new Exception("返回解析的三级微信支付信息错误!");
}
return result;
}
@Override
public String queryOrder(JSONObject json, String pConf) throws Exception {
// 参数校验
if(StringUtils.isEmpty(pConf)) {
throw new Exception("支付配置信息不能为空!");
}
String tradeNo = json.getString("out_trade_no");
if(StringUtils.isEmpty(tradeNo)) {
throw new Exception("tradeNo不能为空!");
}
// 组装请求报文
QueryOrderInputVo payInputVo =QueryOrderInputVo.builder()
.merId(Constants.gzh_merId) // 商户id
.temId(Constants.gzh_temId) // 终端id
.seqNo(SeqNoGenerator.generate())
// 商品信息
.tradeNo(tradeNo)
.txnTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))) // 取当前时间
.build();
String resText = httpUtils.postYNCreditGateWay(payInputVo);
JSONObject jsonResult = JSON.parseObject(resText);
if(jsonResult == null) {
logger.error("返回数据二级json序列化对象不能为空!");
throw new Exception("返回数据二级json序列化对象不能为空!");
}
String state = jsonResult.getString("state");
String code = jsonResult.getString("code");
String returnTradeNo = jsonResult.getString("tradeNo");
if(StringUtils.isEmpty(state) || StringUtils.isEmpty("code") || !returnTradeNo.equals(tradeNo)) {
logger.error("返回解析对象参数state、code、tradeNo错误!");
throw new Exception("返回解析对象参数state、code、tradeNo错误!");
}
String result = "FAIL";
if( code.equals("000000") && returnTradeNo.equals(tradeNo)) {
result = "SUCCESS";
}
/**结束解析微信小程序支付接口报文**/
return result;
}
@Transactional(rollbackFor = Exception.class)
@Override
public String closeOrder(JSONObject json, String pConf) throws Exception {
// 查询订单状态,如果未支付状态 则关闭,否则不予处理
// 参数校验
if(StringUtils.isEmpty(pConf)) {
throw new Exception("支付配置信息不能为空!");
}
JSONObject wxconf = JSONObject.parseObject(pConf);
String tradeNo = json.getString("out_trade_no");
if(StringUtils.isEmpty(tradeNo)) {
throw new Exception("tradeNo不能为空!");
}
String totalAmt = json.getString("total_fee");
if(StringUtils.isEmpty(totalAmt) ) {
throw new Exception("交易金额不能为空!");
}
CloseOrderInputVo payInputVo =CloseOrderInputVo.builder()
.merId(Constants.gzh_merId) // 商户id
.temId(Constants.gzh_temId) // 终端id
.seqNo(SeqNoGenerator.generate())
.tradeNo(tradeNo+"01")
.originTradeNo(tradeNo)
.txnTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"))) // 取当前时间
.totalAmt(totalAmt) // 交易金额
.build();
String processResult = httpUtils.postYNCreditGateWay(payInputVo);
JSONObject result = JSONObject.parseObject(processResult);
if(result == null) {
logger.error("response节点信息反序列化对象不能为空!");
throw new Exception("response节点信息反序列化对象不能为空!");
}
String code = result .getString("code");
String state = result.getString("state");
if(StringUtils.isEmpty(code) || StringUtils.isEmpty(state)) {
logger.error("response节点信息反序列化对象code 或 state不能为空!");
throw new Exception("response节点信息反序列化对象code 或 state不能为空!");
}
String returnResult = "FAIL";
if(code .equals("000000") && state.equals("0")) {
returnResult = "SUCCESS";
valueOperations.getOperations().delete(tradeNo);
}
return returnResult;
}
// @Resource
// MQSender mqSender;
/***
*
* @param resText
* @return
* @throws Exception
*/
private String parseResponseExeResult(String resText, JSONObject originalArgs , String pConf) throws Exception {
PayOutputVo payOutputVo = null;
Throwable t = null;
String result = "";
logger.info("响应报文response节点二级字符串内容{}", resText);
try {
payOutputVo = JSON.parseObject(resText, PayOutputVo.class);
} catch (Exception e) {
t = e;
}
if(payOutputVo == null) {
logger.error("响应报文response节点二级字符串反序列化失败!原因:{}", t);
return null;
}
String state = payOutputVo.getState();
String code = payOutputVo.getCode();
if(StringUtils.isEmpty(state)) {
logger.info("响应报文二级状态信息有误!");
return null;
}
if(StringUtils.isEmpty(code)) {
logger.info("响应报文二级订单执行成功失败码信息有误!");
return null;
}
Map<String, String> payInfos = payOutputVo.getWxPayData();
if(CollectionUtils.isEmpty(payInfos)) {
logger.info("响应报文三级微信信息不能为空!");
return null;
}
try {
JSONObject request = new JSONObject();
request.put("ordertradelogsn", payOutputVo.getWxPayData().get("tradeNo"));
request.put("orderno", payOutputVo.getWxPayData().get("orderId"));
feignOrderService.updateTransIdByOrderno(request);
}catch (Exception e) {
//logger.error("跨服务更新order表失败,原因:", e);
}
// 返回微信支付需要的签名内容
Map appMap = new HashMap();
appMap.put("appId", payInfos.get("appId")); //
appMap.put("timeStamp", payInfos.get("timeStamp"));
appMap.put("nonceStr", payInfos.get("nonceStr"));
appMap.put("package", payInfos.get("package"));
appMap.put("signType", payInfos.get("signType"));
appMap.put("sign", payInfos.get("paySign"));
logger.info("签名内容log-----" + JSONObject.toJSONString(appMap));
// 待支付放在缓存中 从缓存中删除
valueOperations.set(payOutputVo.getTradeNo(), JSONObject.toJSONString(appMap), 2l, TimeUnit.HOURS);
// 用延迟队列处理延迟未支付的订单
//mqSender.sendLazy(originalArgs.toJSONString());
return JSONObject.toJSONString(appMap);
}
}
退款接口
package com.zjht.shoplineserver.util;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zjht.currentserver.model.ResultInfo;
import com.zjht.currentserver.model.po.SOnlineOrder;
import com.zjht.currentserver.model.po.SOnlineOrderLog;
import com.zjht.currentserver.model.po.SOnlineRefund;
import com.zjht.currentserver.model.po.YNCredit.Constants;
import com.zjht.currentserver.model.po.YNCredit.PayInputVo;
import com.zjht.currentserver.model.po.YNCredit.RefundInputVo;
import com.zjht.currentserver.model.po.YNCredit.RefundOutputVo;
import com.zjht.currentserver.model.zyb.ynrcpay.SeqNoGenerator;
import com.zjht.currentserver.model.zyb.ynrcpay.YNCreditSignUtils;
import com.zjht.shoplineserver.dao.SOnlineOrderLogDao;
import com.zjht.shoplineserver.dao.order.OrderDao;
import com.zjht.shoplineserver.dao.order.SOnlineOrderDao;
import com.zjht.shoplineserver.dao.order.SOnlineRefundsDao;
import com.zjht.shoplineserver.model.enums.OrderServerStatus;
import com.zjht.shoplineserver.model.enums.OrderTechanStatus;
import com.zjht.shoplineserver.model.enums.RefundTechanStatus;
import com.zjht.shoplineserver.service.order.SOnlineOrderService;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
// 农行退款接口
@Component
public class YNCreditRefundUtil {
private static final Logger logger = LoggerFactory.getLogger(YNCreditRefundUtil.class);
@Resource
com.zjht.currentserver.model.zyb.HttpUtils httpUtils;
public String postRefund(JSONObject json, String pConf) throws Exception{
// 参数校验
if(StringUtils.isEmpty(pConf)) {
throw new Exception("支付配置信息不能为空!");
}
JSONObject wxconf = JSONObject.parseObject(pConf);
String notifyUrl = wxconf.getString("notify_url"); // 回调地址
String mchId = wxconf.getString("mch_id"); // 商户号
String tradeNo = json.getString("out_trade_no");
if(StringUtils.isEmpty(tradeNo)) {
throw new Exception("tradeNo不能为空!");
}
// 查询订单数量
String totalNum = json.getString("totalNum");
if(StringUtils.isEmpty(totalNum)) {
totalNum = "1";
}
String totalAmt = json.getString("total_fee");
if(StringUtils.isEmpty(totalAmt) ) {
throw new Exception("交易金额不能为空!");
}
RefundInputVo refundInputVo =RefundInputVo.builder()
.merId(mchId)
.temId(Constants.gzh_temId)
.seqNo(SeqNoGenerator.generate())
.tradeNo(tradeNo+"00")
.originTradeNo(tradeNo)
.txnTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss")))
.totalNum(totalNum)
.totalAmt(totalAmt)
.notifyUrl(notifyUrl)
.build();
String resText = httpUtils.postYNCreditGateWay(refundInputVo);
RefundOutputVo refundOutputVo = null;
try {
refundOutputVo = JSON.parseObject(resText, RefundOutputVo.class);
}catch (Exception e) {
logger.error("农信行退款响应报文反序列化失败,原因:", e);
throw new Exception("农信行退款响应报文反序列化失败,原因:"+e);
}
if(refundOutputVo == null) {
logger.error("农信行退款响应报文反序列化对象为空,执行退款失败!");
throw new Exception("农信行退款响应报文反序列化对象为空,执行退款失败!");
}
if( StringUtils.isEmpty(refundOutputVo.getState())) {
logger.error("农信行退款失败,退款结果状态不能为空!");
throw new Exception("农信行退款失败,退款结果状态不能为空!");
}
String resultStr = "SUCCESS";
if(refundOutputVo.getState().equals("1")) {
resultStr = "FAIL";
}
/**结束解析微信小程序支付接口报文**/
return resultStr;
}
}
退款流程调用处的接口调用代码
if(sOnlineOrder.getPaymethod() == 4) { // TODO 农信行退款
//
JSONObject obj = new JSONObject();
obj.put("out_trade_no", sOnlineOrder.getOrderno());
obj.put("totalNum", sOnlineOrder.getQuantity()+"");
obj.put("total_fee", getMoney(sOnlineOrder.getAmount()));
obj.put("body", sOnlineOrder.getGoodname());
JSONObject redisConfig = new JSONObject();
redisConfig.put("mch_id", Constants.gzh_merId);
redisConfig.put("notify_url", "https://www.xxxx.cn/ab/pay/callback/xx");
try {
String result = ynCreditRefundUtil.postRefund(obj, redisConfig.toJSONString());
if(StringUtils.isNotEmpty(result) && result.equals("SUCCESS")) {
ynCreditRefundUtil.changeOrderStatus(sOnlineOrder, sOnlineRefund);
}
} catch (Exception e) {
logger.error("退款失败,原因", e);
}
}
5. 原项目代码冗余啰嗦250行代码改为25行
修改的项目为老项目,代码冗余且不满足需求,阅读起来费解,只好手刃重写!不支持java stream否则2~5行最多不超过10行就搞定了