Authored by sailing-PC\sailing

Merge branch 'master' of http://git.yoho.cn/ufo/yohoufo-fore

# Conflicts:
#	common/src/main/java/com/yohoufo/common/caller/UfoServiceCaller.java
Showing 36 changed files with 1088 additions and 80 deletions
... ... @@ -10,7 +10,8 @@ MAINTAINER jimi <jimi.ji@yoho.cn>
ENV TZ Asia/Shanghai
COPY deploy/target/yohoufo-fore-web.war /usr/local/tomcat/webapps/yohoufo-fore-web.war
#COPY deploy/target/yohoufo-fore-web.war /usr/local/tomcat/webapps/yohoufo-fore-web.war
COPY deploy/target/ufo-gwateway.war /usr/local/tomcat/webapps/ufo-gwateway.war
RUN sed -i 's/<Connector port="8080" protocol/<Connector port="8080" protocol/g' /usr/local/tomcat/conf/server.xml
... ...
package com.yohoufo.common.alarm;
public class Event {
}
\ No newline at end of file
... ...
package com.yohoufo.common.alarm;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.SubscriberExceptionContext;
import com.google.common.eventbus.SubscriberExceptionHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.Executor;
public class EventBusPublisher implements ApplicationContextAware {
private final Logger logger = LoggerFactory.getLogger(EventBusPublisher.class);
private static AsyncEventBus asyncEventBus;
public EventBusPublisher(Executor executor) {
asyncEventBus = new AsyncEventBus(executor, new SubscriberExceptionHandler() {
@Override
public void handleException(Throwable exception, SubscriberExceptionContext context) {
logger.error("Could not dispatch event:{} to {}.{}", context.getEvent(), context.getSubscriber(), context.getSubscriberMethod(), exception);
}
});
}
public void register(Object handler) {
asyncEventBus.register(handler);
}
public static void publishEvent(Object event) {
asyncEventBus.post(event);
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Map<String, IEventHandler> handlerBeans = applicationContext.getBeansOfType(IEventHandler.class);
addEventHandlers(handlerBeans.values());
logger.info("Loaded event handler {} success.", handlerBeans);
}
private void addEventHandlers(Collection<IEventHandler> handlers) {
for (IEventHandler handler : handlers) {
register(handler);
}
}
}
\ No newline at end of file
... ...
package com.yohoufo.common.alarm;
public interface IEventHandler<T extends Event> {
void handle(T event);
}
... ...
package com.yohoufo.common.alarm;
import com.yoho.error.event.CommonEvent;
public class SendSmsEvent extends CommonEvent {
private static final long serialVersionUID = 5805800371910913724L;
private String type;//事件类型
private String smsContent;// 短信内容
public SendSmsEvent(String name, String type, String smsContent) {
super(name);
this.smsContent = smsContent;
this.type = type;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getSmsContent() {
return smsContent;
}
public void setSmsContent(String smsContent) {
this.smsContent = smsContent;
}
}
... ...
package com.yohoufo.common.alarm;
import lombok.Data;
import lombok.ToString;
@Data
@ToString
public class SmsAlarmEvent extends Event {
private String name;
private String type;
private String content;
public SmsAlarmEvent() {
}
public SmsAlarmEvent(String name, String type, String content) {
this.name = name;
this.type = type;
this.content = content;
}
}
\ No newline at end of file
... ...
package com.yohoufo.common.alarm;
import com.google.common.eventbus.Subscribe;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
public class SmsAlarmEventHandler implements IEventHandler<SmsAlarmEvent> {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private ApplicationEventPublisher publisher;
@Override
@Subscribe
public void handle(SmsAlarmEvent event) {
logger.info("begin to handle sms alarm event:{}", event);
publisher.publishEvent(new SendSmsEvent(event.getName(), event.getType(), event.getContent()));
logger.info("end to handle sms alarm event");
}
}
\ No newline at end of file
... ...
... ... @@ -47,7 +47,8 @@ public class UfoServiceCaller implements ApplicationListener<ContextRefreshedEve
* @param param 方法参数
* @return 返回值
*/
public <T> T call(String serviceMethod, Object... param) {
@SuppressWarnings("unchecked")
public <T> T call(String serviceMethod, Class<T> responseType, Object... param) {
logger.info("call ufo service : {}", serviceMethod);
ServiceMethod sm = serviceMap.get(serviceMethod);
if (sm == null) {
... ... @@ -55,7 +56,7 @@ public class UfoServiceCaller implements ApplicationListener<ContextRefreshedEve
}
try {
return (T)sm.call(param);
return (T) sm.call(param);
} catch (Exception e) {
logger.error("service error : " + serviceMethod, e);
return null;
... ...
package com.yohoufo.dal.order;
import com.yohoufo.dal.order.model.SellerOrder;
import org.apache.ibatis.annotations.Param;
public interface SellerOrderMapper {
int deleteByPrimaryKey(Integer id);
... ... @@ -11,6 +12,9 @@ public interface SellerOrderMapper {
SellerOrder selectByPrimaryKey(Integer id);
SellerOrder selectBySkup(@Param("skup") Integer skup);
int updateByPrimaryKeySelective(SellerOrder record);
int updateByPrimaryKey(SellerOrder record);
... ...
... ... @@ -3,8 +3,11 @@ package com.yohoufo.dal.product.model;
import java.math.BigDecimal;
public class StoragePrice {
private Integer id;
private Integer skup;
private Integer productId;
private Integer goodsId;
... ... @@ -17,6 +20,7 @@ public class StoragePrice {
private BigDecimal price;
/** 1:可售,2卖出,3鉴定失败,4卖家取消. */
private Integer status;
private Integer updateTime;
... ... @@ -31,6 +35,14 @@ public class StoragePrice {
this.id = id;
}
public Integer getSkup() {
return skup;
}
public void setSkup(Integer skup) {
this.skup = skup;
}
public Integer getProductId() {
return productId;
}
... ...
... ... @@ -3,6 +3,7 @@
<mapper namespace="com.yohoufo.dal.product.StoragePriceMapper">
<resultMap id="BaseResultMap" type="com.yohoufo.dal.product.model.StoragePrice">
<id column="id" jdbcType="INTEGER" property="id" />
<result column="skup" jdbcType="INTEGER" property="skup" />
<result column="product_id" jdbcType="INTEGER" property="productId" />
<result column="goods_id" jdbcType="INTEGER" property="goodsId" />
<result column="storage_id" jdbcType="INTEGER" property="storageId" />
... ... @@ -18,18 +19,19 @@
where id = #{id,jdbcType=INTEGER}
</delete>
<insert id="insert" parameterType="com.yohoufo.dal.product.model.StoragePrice">
insert into storage_price (id, product_id, goods_id,
insert into storage_price (id, skup, product_id, goods_id,
storage_id, depot_num, seller_uid,
price, status, update_time,
create_time)
values (#{id,jdbcType=INTEGER}, #{productId,jdbcType=INTEGER}, #{goodsId,jdbcType=INTEGER},
values (#{id,jdbcType=INTEGER}, #{skup,jdbcType=INTEGER}, #{productId,jdbcType=INTEGER}, #{goodsId,jdbcType=INTEGER},
#{storageId,jdbcType=INTEGER}, #{depotNum,jdbcType=INTEGER}, #{sellerUid,jdbcType=INTEGER},
#{price,jdbcType=DECIMAL}, #{status,jdbcType=INTEGER}, #{updateTime,jdbcType=INTEGER},
#{createTime,jdbcType=INTEGER})
</insert>
<update id="updateByPrimaryKey" parameterType="com.yohoufo.dal.product.model.StoragePrice">
update storage_price
set product_id = #{productId,jdbcType=INTEGER},
set skup = #{skup,jdbcType=INTEGER},
product_id = #{productId,jdbcType=INTEGER},
goods_id = #{goodsId,jdbcType=INTEGER},
storage_id = #{storageId,jdbcType=INTEGER},
depot_num = #{depotNum,jdbcType=INTEGER},
... ... @@ -41,13 +43,13 @@
where id = #{id,jdbcType=INTEGER}
</update>
<select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
select id, product_id, goods_id, storage_id, depot_num, seller_uid, price, status,
select id, skup, product_id, goods_id, storage_id, depot_num, seller_uid, price, status,
update_time, create_time
from storage_price
where id = #{id,jdbcType=INTEGER}
</select>
<select id="selectAll" resultMap="BaseResultMap">
select id, product_id, goods_id, storage_id, depot_num, seller_uid, price, status,
select id, skup, product_id, goods_id, storage_id, depot_num, seller_uid, price, status,
update_time, create_time
from storage_price
</select>
... ...
package com.yohoufo.order.common;
public enum OrderStatus {
// 14:支付前取消,15:超时未支付取消,16:已支付,卖家发货前取消;17:客服取消
WAITING_PAY(0, "未付款"),
HAS_PAYED(1, "已付款"),
SELLER_SEND_OUT(2, "商品调拨中(卖家发货,可查物流)"),
PLATFORM_CHECKING(3, "有货平台鉴定中"),
WAITING_RECEIVE(4, "待收货"),
DONE(5, "已完成"),
SELLER_CANCEL_BEFORE_PAY(10, "买家付款前卖家取消"),
SELLER_CANCEL_AFTER_PAY(11, "买家付款后卖家取消"),
SEND_OUT_TIMEOUT(12, "超时未发货取消"),
CHECKING_FAKE(13, "鉴定不通过取消"),
BUYER_CANCEL_BEFORE_PAY(14, "支付前取消"),
BUYER_CANCEL_TIMEOUT(15, "超时未支付取消"),
CUSTOMER_SERVICE(17, "客服取消")
;
int code;
String desc;
OrderStatus(int code, String desc) {
this.code = code;
this.desc = desc;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
... ...
... ... @@ -28,5 +28,9 @@ public interface OrderConstant {
String MONEY_UNIT = "元";
String N_STR= "N";
String Y_STR= "Y";
}
... ...
package com.yohoufo.order.controller;
import com.yohoufo.common.ApiResponse;
import com.yohoufo.order.request.ShoppingRequest;
import com.yohoufo.order.response.OrderSubmitResponse;
import com.yohoufo.order.response.ShoppingPaymentResponse;
import com.yohoufo.order.service.IShoppingService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
... ... @@ -21,14 +24,31 @@ public class ShoppingController {
/**
* 结算
* @param shoppingRequest
* @return
*/
@RequestMapping("/payment")
@ResponseBody
public ShoppingPaymentResponse payment(@RequestBody ShoppingRequest shoppingRequest){
@RequestMapping(params = "params=ufo.order.payment")
public ApiResponse payment(@RequestParam(name = "uid") int uid,
@RequestParam(name = "skup") int skup){
return shoppingService.payment(shoppingRequest);
ShoppingPaymentResponse paymentResponse = shoppingService.payment(ShoppingRequest.builder().uid(uid).skup(skup).build());
return new ApiResponse.ApiResponseBuilder().code(200).data(paymentResponse).message("结算成功").build();
}
/**
* 提交订单成功
* @return
*/
@RequestMapping(params = "params=ufo.order.submit")
public ApiResponse submit(@RequestParam(name = "uid") int uid,
@RequestParam(name = "skup") int skup,
@RequestParam(name = "channelNo") String channelNo,
@RequestParam(name = "addressId") String addressId,
@RequestParam(name = "clientType") String clientType){
ShoppingRequest request = ShoppingRequest.builder().uid(uid).skup(skup).channelNo(channelNo).clientType(clientType).addressId(addressId).build();
OrderSubmitResponse paymentResponse = shoppingService.submit(request);
return new ApiResponse.ApiResponseBuilder().code(200).data(paymentResponse).message("提交订单SUCCESS").build();
}
... ...
package com.yohoufo.order.model.dto;
import lombok.Data;
import lombok.ToString;
import lombok.experimental.Builder;
import java.math.BigDecimal;
@Data
@Builder
@ToString
public class OrderBuilder {
int skup;
int productId;
long orderCode;
int uid;
String clientType;
int paymentType;
int deliverWay;
String channelNo;
/**
* 实付金额
*/
BigDecimal amount;
/**
* 运费
*/
BigDecimal shipFee;
}
... ...
package com.yohoufo.order.request;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Builder;
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class ShoppingRequest {
private int skup;
... ... @@ -19,6 +25,11 @@ public class ShoppingRequest {
*/
private String clientType;
/**
* 渠道号
*/
private String channelNo;
}
... ...
package com.yohoufo.order.response;
import lombok.Data;
import lombok.experimental.Builder;
@Data
@Builder
public class OrderSubmitResponse {
long orderCode;
String paymentStatus;
}
... ...
package com.yohoufo.order.service;
import com.yohoufo.order.model.dto.OrderBuilder;
import org.springframework.transaction.annotation.Transactional;
public interface IOrdeCreationService {
/**
* 创建订单
* @param orderBuilder
*/
public void create(OrderBuilder orderBuilder);
}
... ...
... ... @@ -2,14 +2,23 @@ package com.yohoufo.order.service;
import com.yohoufo.order.request.ShoppingRequest;
import com.yohoufo.order.response.OrderSubmitResponse;
import com.yohoufo.order.response.ShoppingPaymentResponse;
public interface IShoppingService {
/**
* 结算页数据
* @param request
* @return
*/
public ShoppingPaymentResponse payment(ShoppingRequest request);
/**
* 提交订单
* @param shoppingRequest
* @return
*/
public ShoppingPaymentResponse payment(ShoppingRequest shoppingRequest);
public OrderSubmitResponse submit(ShoppingRequest shoppingRequest);
}
... ...
package com.yohoufo.order.service.impl;
import com.yoho.core.common.utils.DateUtil;
import com.yoho.error.ServiceError;
import com.yoho.error.exception.ServiceException;
import com.yohoufo.common.caller.UfoServiceCaller;
import com.yohoufo.dal.order.*;
import com.yohoufo.dal.order.model.BuyerOrder;
import com.yohoufo.dal.order.model.BuyerOrderGoods;
import com.yohoufo.dal.order.model.SellerOrder;
import com.yohoufo.dal.order.model.SellerOrderGoods;
import com.yohoufo.order.common.OrderStatus;
import com.yohoufo.order.common.SellerOrderStatus;
import com.yohoufo.order.common.SkupStatus;
import com.yohoufo.order.model.dto.OrderBuilder;
import com.yohoufo.order.service.IOrdeCreationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class OrdeCreationServiceImpl {
public class OrdeCreationServiceImpl implements IOrdeCreationService {
@Autowired
SellerOrderGoodsMapper sellerOrderGoodsMapper;
... ... @@ -32,11 +37,16 @@ public class OrdeCreationServiceImpl {
BuyerOrderMetaMapper buyerOrderMetaMapper;
@Autowired
private UfoServiceCaller serviceCaller;
/**
* 创建订单
* @param orderBuilder
*/
@Transactional
public void create(int skup, int productId, long orderCode, int uid, String clientType){
public void create(OrderBuilder orderBuilder){
try{
... ... @@ -44,32 +54,47 @@ public class OrdeCreationServiceImpl {
// 1.skup不可售
// 2.卖家订单,skup已经售出
SellerOrderGoods sellerOrderGoods = new SellerOrderGoods();
sellerOrderGoods.setId(skup);
sellerOrderGoods.setId(orderBuilder.getSkup());
sellerOrderGoods.setStatus((byte)SkupStatus.CAN_NOT_SELL.getCode());
sellerOrderGoodsMapper.updateByPrimaryKeySelective(sellerOrderGoods);
// 根据skup 查询卖家订单 ==> 卖家uid
// SellerOrder sellerOrder = sellerOrderMapper.selectBySkup(skup);
// sellerOrder.setUpdateTime(DateUtil.getCurrentTimeSecond());
// sellerOrder.setStatus((byte) SellerOrderStatus.HAS_SELLED.getCode());
// sellerOrderMapper.updateByPrimaryKeySelective(sellerOrder);
SellerOrder sellerOrder = sellerOrderMapper.selectBySkup(orderBuilder.getSkup());
sellerOrder.setUpdateTime(DateUtil.getCurrentTimeSecond());
sellerOrder.setStatus((byte) SellerOrderStatus.HAS_SELLED.getCode());
sellerOrderMapper.updateByPrimaryKeySelective(sellerOrder);
// 订单入口
BuyerOrder buyerOrder = new BuyerOrder();
buyerOrder.setUid(uid);
buyerOrder.setOrderCode(orderCode);
// buyerOrder.setSellerUid(sellerOrder.getUid());
// buyerOrder.setPaymentType();
buyerOrder.setUid(orderBuilder.getUid());
buyerOrder.setOrderCode(orderBuilder.getOrderCode());
buyerOrder.setSellerUid(sellerOrder.getUid());
buyerOrder.setPaymentType((byte) orderBuilder.getPaymentType());
buyerOrder.setAmount(orderBuilder.getAmount());
buyerOrder.setShipFee(orderBuilder.getShipFee());
buyerOrder.setChannelNo(orderBuilder.getChannelNo());
int now = DateUtil.getCurrentTimeSecond();
buyerOrder.setCreateTime(now);
buyerOrder.setUpdateTime(now);
buyerOrder.setStatus((byte)OrderStatus.WAITING_PAY.getCode());
buyerOrderMapper.insert(buyerOrder);
}catch (Exception e){
throw e;
BuyerOrderGoods buyerOrderGoods = new BuyerOrderGoods();
buyerOrderGoods.setSkup(orderBuilder.getSkup());
buyerOrderGoods.setOrderCode(orderBuilder.getOrderCode());
buyerOrderGoods.setGoodsPrice(sellerOrderGoods.getGoodsPrice());
buyerOrderGoods.setGoodsAmount(sellerOrderGoods.getGoodsPrice());
buyerOrderGoodsMapper.insertSelective(buyerOrderGoods);
}catch (Exception ex){
if (!(ex instanceof ServiceException)) {
//上报告警事件
// EventBusPublisher.publishEvent(new SmsAlarmEvent(OrderHanders.submit, "submit", "订单(" + order.getOrderCode() + ")创建失败"));
throw new ServiceException(ServiceError.SHOPPING_SUBMIT_ORDER_FAIL, ex);
}
throw ex;
}
}
... ...
... ... @@ -2,16 +2,19 @@ package com.yohoufo.order.service.impl;
import com.yoho.error.ServiceError;
import com.yoho.error.exception.ServiceException;
import com.yoho.service.model.consts.Constant;
import com.yohoufo.dal.order.SellerOrderGoodsMapper;
import com.yohoufo.dal.order.model.SellerOrderGoods;
import com.yohoufo.order.common.SkupStatus;
import com.yohoufo.order.constants.OrderConstant;
import com.yohoufo.order.model.dto.OrderBuilder;
import com.yohoufo.order.request.ShoppingRequest;
import com.yohoufo.order.response.OrderSubmitResponse;
import com.yohoufo.order.response.ShoppingPaymentResponse;
import com.yohoufo.order.service.IOrdeCreationService;
import com.yohoufo.order.service.IShoppingService;
import com.yohoufo.order.service.support.ShoppingSupport;
import com.yohoufo.order.service.support.codegenerator.OrderCodeGenerator;
import com.yohoufo.order.service.support.codegenerator.OrderCodeType;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
... ... @@ -31,44 +34,46 @@ public class ShoppingServiceImpl implements IShoppingService{
@Autowired
SellerOrderGoodsMapper sellerOrderGoodsMapper;
//@Autowired
@Autowired
OrderCodeGenerator orderCodeGenerator;
@Autowired
IOrdeCreationService ordeCreationService;
/**
* 结算页数据
* @param shoppingRequest
* @return
*/
public ShoppingPaymentResponse payment(ShoppingRequest shoppingRequest){
public ShoppingPaymentResponse payment(ShoppingRequest request){
// 入口参数检查
if (shoppingRequest.getUid()<0
|| shoppingRequest.getSkup()<0){
if (request.getSkup() < 0 || request.getUid() <0){
logger.warn("payment uid or skup is null");
// TODO
throw new ServiceException(ServiceError.ORDER_REQUEST_ERROR);
}
// skup是否是可售状态
SellerOrderGoods skup = sellerOrderGoodsMapper.selectByPrimaryKey(shoppingRequest.getSkup());
if (skup == null){
logger.warn("payment skup not exist, skup is {}", shoppingRequest.getSkup());
SellerOrderGoods skupGood = sellerOrderGoodsMapper.selectByPrimaryKey(request.getSkup());
if (skupGood == null){
logger.warn("payment skup not exist, skup is {}", request.getSkup());
// TODO
throw new ServiceException(ServiceError.ORDER_REQUEST_ERROR);
}
// 不可售
if (skup.getStatus()!=null
&& skup.getStatus().intValue() != SkupStatus.CAN_SELL.getCode()){
logger.warn("payment skup status, skup is {}", shoppingRequest.getSkup());
if (skupGood.getStatus()!=null
&& skupGood.getStatus().intValue() != SkupStatus.CAN_SELL.getCode()){
logger.warn("payment skup status, skup is {}", request.getSkup());
throw new ServiceException(ServiceError.ORDER_REQUEST_ERROR);
}
ShoppingPaymentResponse response = new ShoppingPaymentResponse();
response.setPaymentWay(shoppingSupport.getPaymentWay());
response.setDeliveryWay(shoppingSupport.getDeliverWay());
response.setGoodsList(Arrays.asList(shoppingSupport.getGoodsInfo(skup)));
response.setPromotionFormulaList(shoppingSupport.getPromotionFormula(skup));
response.setGoodsList(Arrays.asList(shoppingSupport.getGoodsInfo(skupGood)));
response.setPromotionFormulaList(shoppingSupport.getPromotionFormula(skupGood));
// TODO 赔偿金计算
response.setDamagesDesc(shoppingSupport.getDamagesDesc(null));
... ... @@ -77,7 +82,12 @@ public class ShoppingServiceImpl implements IShoppingService{
}
public void submit(ShoppingRequest shoppingRequest){
/**
* 提交订单
* @param shoppingRequest
* @return
*/
public OrderSubmitResponse submit(ShoppingRequest shoppingRequest){
// 入口参数检查
if (shoppingRequest.getSkup() < 0
... ... @@ -105,16 +115,29 @@ public class ShoppingServiceImpl implements IShoppingService{
}
// 生成订单号 TODO
long orderCode = orderCodeGenerator.generate();
// 减库存,skup更新成不可售,入库
long orderCode = orderCodeGenerator.generate(OrderCodeType.BUYER_TYPE);
// 减库存,skup更新成不可售,入库
BigDecimal shipFee = new BigDecimal(OrderConstant.DELIVERY_WAY_SF_COST);
OrderBuilder orderBuilder = OrderBuilder.builder()
.uid(shoppingRequest.getUid())
.skup(shoppingRequest.getSkup())
.orderCode(orderCode)
.productId(skup.getProductId())
.paymentType(OrderConstant.PAYMENT_ONLINE)
.deliverWay(OrderConstant.DELIVERY_WAY_SF)
.channelNo(shoppingRequest.getChannelNo())
.amount(skup.getGoodsPrice().add(shipFee))
.shipFee(shipFee)
.build();
// 收货地址信息, 入口参数:uid, address_id TODO
ordeCreationService.create(orderBuilder);
// 返回结果
return OrderSubmitResponse.builder().orderCode(orderCode).paymentStatus(OrderConstant.N_STR).build();
}
... ...
package com.yohoufo.order.service.support.codegenerator;
import com.yohoufo.order.service.support.codegenerator.bean.CodeMeta;
/**
* 订单号生成
*/
public interface OrderCodeGenerator {
/**
* 根据产生订单号
* @return
*/
long generate();
/**
* 生成订单号
* @param type
* @return
*/
long generate(OrderCodeType type);
/**
* 反解
* @param id
* @return
*/
CodeMeta expId(long id);
}
... ...
package com.yohoufo.order.service.support.codegenerator;
public enum OrderCodeType {
SELLER_TYPE(0),
BUYER_TYPE(1);
int type;
OrderCodeType(int type) {
this.type = type;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
}
... ...
package com.yohoufo.order.service.support.codegenerator.bean;
import lombok.Data;
/**
* 生成订单号bit元数据
* 订单号:
* 1位: 类型(标识是卖家订单号or买家订单号)
* 30位: 时间戳(秒级时间) 2的30次方/60/6024/365=34,也就是说可以用30年
* 10位: 序列号, 也就是说 每秒支持1K多的id
* 3位: 服务节点,支持8个服务节点
*/
@Data
public class CodeBitMeta {
/**
* 类型
*/
private byte typeBits = 1;
/**
* 时间戳
*/
private byte timeBits = 30;
/**
* 序列号
*/
private byte seqBits = 10;
/**
* 机器位
*/
private byte machineBits = 3;
public long getMachineBitsMask() {
return -1L ^ -1L << machineBits;
}
public long getSeqBitsStartPos() {
return machineBits;
}
public long getSeqBitsMask() {
return -1L ^ (-1L << seqBits);
}
public long getTimeBitsStartPos() {
return machineBits + seqBits;
}
public long getTimeBitsMask() {
return -1L ^ (-1L << timeBits);
}
public long getTypeBitsStartPos() {
return machineBits + seqBits + timeBits;
}
public long getTypeBitsMask() {
return -1L ^ (-1L << typeBits);
}
}
\ No newline at end of file
... ...
package com.yohoufo.order.service.support.codegenerator.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import java.io.Serializable;
@Data
@ToString
@NoArgsConstructor
@AllArgsConstructor
public class CodeMeta implements Serializable {
private static final long serialVersionUID = 6870931236218221183L;
private long machine;
private long seq;
private long time;
private long type;
}
... ...
package com.yohoufo.order.service.support.codegenerator.converter;
import com.yohoufo.order.service.support.codegenerator.bean.CodeBitMeta;
import com.yohoufo.order.service.support.codegenerator.bean.CodeMeta;
public class IdConverterHelper {
/**
* 根据元数据获取id
* @param id
* @param idBitMeta
* @return
*/
public static long convert(CodeMeta id, CodeBitMeta idBitMeta) {
long ret = 0;
ret |= id.getMachine();
ret |= id.getSeq() << idBitMeta.getSeqBitsStartPos();
ret |= id.getTime() << idBitMeta.getTimeBitsStartPos();
ret |= id.getType() << idBitMeta.getTypeBitsStartPos();
return ret;
}
/**
* 根据id反解元数据
* @param id
* @param idBitMeta
* @return
*/
public static CodeMeta convert(long id, CodeBitMeta idBitMeta) {
CodeMeta ret = new CodeMeta();
ret.setMachine(id & idBitMeta.getMachineBitsMask());
ret.setSeq((id >>> idBitMeta.getSeqBitsStartPos()) & idBitMeta.getSeqBitsMask());
ret.setTime((id >>> idBitMeta.getTimeBitsStartPos()) & idBitMeta.getTimeBitsMask());
ret.setType((id >>> idBitMeta.getTypeBitsStartPos()) & idBitMeta.getTypeBitsMask());
return ret;
}
}
... ...
package com.yohoufo.order.service.support.codegenerator.impl;
import com.yohoufo.order.service.support.codegenerator.OrderCodeGenerator;
import com.yohoufo.order.service.support.codegenerator.OrderCodeType;
import com.yohoufo.order.service.support.codegenerator.bean.CodeBitMeta;
import com.yohoufo.order.service.support.codegenerator.bean.CodeMeta;
import com.yohoufo.order.service.support.codegenerator.converter.IdConverterHelper;
import com.yohoufo.order.service.support.codegenerator.populater.AtomicIdPopulator;
import com.yohoufo.order.service.support.codegenerator.timer.SimpleTimer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class OrderCodeGeneratorImpl implements OrderCodeGenerator {
protected final Logger log = LoggerFactory.getLogger(this.getClass());
CodeBitMeta idBitMeta = new CodeBitMeta();
SimpleTimer timer ;
AtomicIdPopulator idPopulator;
/**
* 机器节点: 取值0-7
*/
@Value("${env.machineId:0}")
private long machineId;
@PostConstruct
public void init() {
if (timer == null){
timer = new SimpleTimer();
}
if (idPopulator == null){
idPopulator = new AtomicIdPopulator();
}
// 3位 0~8
validateMachineId(this.machineId);
}
public long generate(OrderCodeType type) {
CodeMeta id = new CodeMeta();
id.setMachine(machineId);
id.setType(type.getType());
idPopulator.populateId(timer, id, idBitMeta);
long time = id.getTime() & idBitMeta.getTimeBitsMask();
id.setTime(time);
long ret = IdConverterHelper.convert(id, this.idBitMeta);
return ret;
}
public CodeMeta expId(long id) {
return IdConverterHelper.convert(id, this.idBitMeta);
}
private void validateMachineId(long machineId){
if (machineId < 0) {
log.error("The machine ID is not configured properly (" + machineId + " < 0) so that Vesta Service refuses to start.");
throw new IllegalStateException(
"The machine ID is not configured properly (" + machineId + " < 0) so that Vesta Service refuses to start.");
} else if (machineId >= (1 << this.idBitMeta.getMachineBits())) {
log.error("The machine ID is not configured properly ("
+ machineId + " >= " + (1 << this.idBitMeta.getMachineBits()) + ") so that Vesta Service refuses to start.");
throw new IllegalStateException("The machine ID is not configured properly ("
+ machineId + " >= " + (1 << this.idBitMeta.getMachineBits()) + ") so that Vesta Service refuses to start.");
}
}
}
\ No newline at end of file
... ...
package com.yohoufo.order.service.support.codegenerator.populater;
import com.yohoufo.order.service.support.codegenerator.bean.CodeBitMeta;
import com.yohoufo.order.service.support.codegenerator.bean.CodeMeta;
import com.yohoufo.order.service.support.codegenerator.timer.SimpleTimer;
import java.util.concurrent.atomic.AtomicReference;
/**
* 生成秒级时间戳+序列号
*/
public class AtomicIdPopulator implements IdPopulator {
class Variant {
private long sequence = 0;
private long lastTimestamp = -1;
}
private AtomicReference<Variant> variant = new AtomicReference<Variant>(new Variant());
public AtomicIdPopulator() {
super();
}
public void populateId(SimpleTimer timer, CodeMeta id, CodeBitMeta idBitMeta) {
Variant varOld, varNew;
long timestamp, sequence;
while (true) {
// Save the old variant
varOld = variant.get();
// populate the current variant
timestamp = timer.genTime();
timer.validateTimestamp(varOld.lastTimestamp, timestamp);
sequence = varOld.sequence;
if (timestamp == varOld.lastTimestamp) {
sequence++;
sequence &= idBitMeta.getSeqBitsMask();
if (sequence == 0) {
timestamp = timer.tillNextTimeUnit(varOld.lastTimestamp);
}
} else {
sequence = 0;
}
// Assign the current variant by the atomic tools
varNew = new Variant();
varNew.sequence = sequence;
varNew.lastTimestamp = timestamp;
if (variant.compareAndSet(varOld, varNew)) {
id.setSeq(sequence);
id.setTime(timestamp);
break;
}
}
}
}
... ...
package com.yohoufo.order.service.support.codegenerator.populater;
import com.yohoufo.order.service.support.codegenerator.bean.CodeBitMeta;
import com.yohoufo.order.service.support.codegenerator.bean.CodeMeta;
import com.yohoufo.order.service.support.codegenerator.timer.SimpleTimer;
public interface IdPopulator {
void populateId(SimpleTimer timer, CodeMeta id, CodeBitMeta idBitMeta);
}
... ...
package com.yohoufo.order.service.support.codegenerator.timer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
public class SimpleTimer {
protected static final Logger log = LoggerFactory.getLogger(SimpleTimer.class);
long EPOCH = 1420041600000L;
public void validateTimestamp(long lastTimestamp, long timestamp) {
if (timestamp < lastTimestamp) {
if (log.isErrorEnabled())
log.error(String
.format("Clock moved backwards. Refusing to generate id for %d second/milisecond.",
lastTimestamp - timestamp));
throw new IllegalStateException(
String.format(
"Clock moved backwards. Refusing to generate id for %d second/milisecond.",
lastTimestamp - timestamp));
}
}
public long tillNextTimeUnit(final long lastTimestamp) {
long timestamp = genTime();
while (timestamp <= lastTimestamp) {
timestamp = genTime();
}
return timestamp;
}
public long genTime() {
long time = (System.currentTimeMillis() - EPOCH) / 1000;
return time;
}
}
... ...
package com.yohoufo.order.service.code;
public class CodeTest {
public static void main(String[] args){
// OrderCodeGenerator orderCodeGenerator = new OrderCodeGeneratorImpl();
//
// for (int i =0; i <100 ; i++){
// long result = orderCodeGenerator.genId();
// System.out.println(result);
// System.out.println("反解结果: "+ orderCodeGenerator.expId(result));
// System.out.println("========================");
// }
System.out.println(1<<3);
}
}
... ...
package com.yoho.pay;
import com.yoho.core.common.utils.TimeUtils;
public class IdWorker{
private long workerId;
private long datacenterId;
private long sequence=0;
public IdWorker(long workerId, long datacenterId){
// sanity check for workerId
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker CodeMeta can't be greater than %d or less than 0",maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(String.format("datacenter CodeMeta can't be greater than %d or less than 0",maxDatacenterId));
}
System.out.printf("worker starting. timestamp left shift %d, datacenter id bits %d, worker id bits %d, sequence bits %d, workerid %d",
timestampLeftShift, datacenterIdBits, workerIdBits, sequenceBits, workerId);
this.workerId = workerId;
this.datacenterId = datacenterId;
}
private long twepoch = 1288834974657L; // 起始时间戳
// 1位(固定值0) 21位(时间戳) 2+2位(工作机器id=5位的datacenterId + 5位的workerId) 12位(序号位)
// 一共64位,2的64位,最大能表示多少啊
private long workerIdBits = 2L;
private long datacenterIdBits = 2L;
// 可以表示的最大正整数 2的N次方-1, 值为31
private long maxWorkerId = -1L ^ (-1L << workerIdBits);
// 可以标识的最大正整数 2的N次方-1,值为31
private long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
private long sequenceBits = 12L;
// 左移12位
private long workerIdShift = sequenceBits;
// 左移 12+5位
private long datacenterIdShift = sequenceBits + workerIdBits;
// 左移 12+5+5 位
private long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private long tmp = timestampLeftShift + 41L;
// 4095 2的n次方-1
private long sequenceMask = -1L ^ (-1L << sequenceBits);
private long tmpMask = -1L ^ (-1L << tmp);;
private long lastTimestamp = -1L;
public long getWorkerId(){
return workerId;
}
public long getDatacenterId(){
return datacenterId;
}
public long getTimestamp(){
return System.currentTimeMillis();
}
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {
System.err.printf("clock is moving backwards. Rejecting requests until %d.", lastTimestamp);
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds",
lastTimestamp - timestamp));
}
// 获取当前时间,如果此刻的毫秒和 上次的毫秒是同一个时间点,则序号+1
if (lastTimestamp == timestamp) {
// 如果原来的序号+1 以后的结果是0,则说明这一毫秒可用的序列号都用完了,sequenceMask 都为0,要结果为0, 只能说明,左边的数据是0
// 一个数 与 一个全1的数字,不是还是等于左边的数吗?
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
timestamp = tilNextMillis(lastTimestamp);
}
} else {
sequence = 0;
}
lastTimestamp = timestamp;
// 最后返回 返回的结果如下:
return
(((timestamp - twepoch) << timestampLeftShift) & tmpMask)|
(datacenterId << datacenterIdShift) |
(workerId << workerIdShift) |
sequence;
}
private long tilNextMillis(long lastTimestamp) {
// 等待下一个毫秒
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
private long timeGen(){
return System.currentTimeMillis();
}
//---------------测试---------------
public static void main(String[] args) {
// IdWorker worker = new IdWorker(1,1);
// for (int i = 0; i < 30; i++) {
// System.out.println(worker.nextId());
// }
// 现在的生成结果
// 1041255103765549057
// 生成一个时间错 左移22位, long 64位,左移以后超过64位的都被只设置未0, 毫秒需要41位来表示吗
// xxxxxx
// 00000000xxxxxxx22位 则相当于时间戳位 有20位
// 与 位数最高位,全一的数据与一下 ====> 超过最高位的左边都会与成0,从而将数限制在范围内
System.out.println((System.currentTimeMillis() - 1288834974657L)<<22);
// // 1000000000010
// // 0111111111111
// // 这样起到的作用,将位数超过的高位直接设置成0了,保证数据在 指定的位数之内,防止位数溢出
// System.out.println(4095 & 4098);
}
}
\ No newline at end of file
... ...
package com.yohoufo.product.controller;
import com.yoho.tools.docs.ApiOperation;
import com.yohoufo.common.utils.UfoJsonUtil;
import com.yohoufo.product.response.ProductDetailResp;
import com.yohoufo.product.response.ProductSeriesTemplateResp;
import com.yohoufo.product.response.StorageDataResp;
import com.yohoufo.product.response.StorageLeastPriceResp;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.yoho.tools.docs.ApiOperation;
import com.yohoufo.common.ApiResponse;
import com.yohoufo.common.annotation.IgnoreSession;
import com.yohoufo.common.annotation.IgnoreSignature;
import com.yohoufo.common.caller.UfoServiceCaller;
import org.springframework.web.bind.annotation.RestController;
import com.yohoufo.common.utils.UfoJsonUtil;
import com.yohoufo.product.request.StoragePriceBo;
import com.yohoufo.product.response.ProductDetailResp;
import com.yohoufo.product.response.ProductSeriesTemplateResp;
import com.yohoufo.product.response.StorageDataResp;
import com.yohoufo.product.response.StorageLeastPriceResp;
@RestController
... ... @@ -79,15 +77,15 @@ public class ProductController {
@IgnoreSession
@RequestMapping(params = "method=ufo.product.storage.leastprice")
//@Cachable(expire=600)
public StorageLeastPriceResp queryStorageLeastprice(
public ApiResponse queryStorageLeastprice(
@RequestParam(value = "storage_id", required = true) Integer storageId) {
return queryStorageLeastprice();
return new ApiResponse.ApiResponseBuilder().data(queryStorageLeastprice()).code(200).message("storage.leastprice").build();
}
private StorageLeastPriceResp queryStorageLeastprice(){
String mockJson = "{\"storage_least_price\":{\"storage_id\":2,\"least_price\":124.3}}";
String mockJson = "{\"storage_least_price\":[{\"storage_id\":2,\"least_price\":124.3}]}";
StorageLeastPriceResp resp = UfoJsonUtil.safelyParseObject(mockJson, StorageLeastPriceResp.class);
return resp;
}
... ... @@ -97,10 +95,10 @@ public class ProductController {
@IgnoreSession
@RequestMapping(params = "method=ufo.product.storage.data")
//@Cachable(expire=600)
public StorageDataResp queryStorageInfo(
public ApiResponse queryStorageInfo(
@RequestParam(value = "storage_id", required = true) Integer storageId) {
return queryStorageData();
return new ApiResponse.ApiResponseBuilder().data(queryStorageData()).code(200).message("storage.data").build();
}
... ... @@ -113,11 +111,7 @@ public class ProductController {
@IgnoreSignature
@IgnoreSession
@RequestMapping(params = "method=ufo.product.createSkup")
public ApiResponse createSkup(
@RequestParam(value = "skup", required = false) Integer skup,
@RequestParam(value = "seller_uid", required = false) Integer sellerUid,
@RequestParam(value = "price", required = false) String price) {
public ApiResponse createSkup(@RequestBody StoragePriceBo skupBo) {
return new ApiResponse(200, "创建成功!", Boolean.TRUE);
}
... ... @@ -125,6 +119,7 @@ public class ProductController {
@IgnoreSession
@RequestMapping(params = "method=ufo.product.saleSkup")
public ApiResponse saleSkup(
@RequestParam(value = "product_id", required = false) Integer productId,
@RequestParam(value = "skup", required = false) Integer skup) {
return new ApiResponse(200, "卖出成功!", Boolean.TRUE);
}
... ... @@ -134,8 +129,8 @@ public class ProductController {
@RequestMapping(params = "method=ufo.product.cancelSaleSkup")
public ApiResponse cancelSaleSkup(
@RequestParam(value = "skup", required = false) Integer skup) {
serviceCaller.call("ufo.product.data", 99);
return new ApiResponse(200, "取消卖出成功!", Boolean.TRUE);
ApiResponse resp = serviceCaller.call("ufo.product.data", ApiResponse.class ,99);
return new ApiResponse(200, "取消卖出成功!", resp);
}
}
\ No newline at end of file
... ...
package com.yohoufo.product.request;
import java.math.BigDecimal;
public class StoragePriceBo {
private Integer skup;
private Integer productId;
private Integer goodsId;
private Integer storageId;
private Integer depotNum;
private Integer sellerUid;
private BigDecimal price;
private Integer status;
private Integer updateTime;
private Integer createTime;
public Integer getProductId() {
return productId;
}
public void setProductId(Integer productId) {
this.productId = productId;
}
public Integer getGoodsId() {
return goodsId;
}
public void setGoodsId(Integer goodsId) {
this.goodsId = goodsId;
}
public Integer getStorageId() {
return storageId;
}
public void setStorageId(Integer storageId) {
this.storageId = storageId;
}
public Integer getDepotNum() {
return depotNum;
}
public void setDepotNum(Integer depotNum) {
this.depotNum = depotNum;
}
public Integer getSellerUid() {
return sellerUid;
}
public void setSellerUid(Integer sellerUid) {
this.sellerUid = sellerUid;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public Integer getStatus() {
return status;
}
public void setStatus(Integer status) {
this.status = status;
}
public Integer getUpdateTime() {
return updateTime;
}
public void setUpdateTime(Integer updateTime) {
this.updateTime = updateTime;
}
public Integer getCreateTime() {
return createTime;
}
public void setCreateTime(Integer createTime) {
this.createTime = createTime;
}
public Integer getSkup() {
return skup;
}
public void setSkup(Integer skup) {
this.skup = skup;
}
}
\ No newline at end of file
... ...
... ... @@ -3,12 +3,20 @@ package com.yohoufo.product.response;
import com.alibaba.fastjson.annotation.JSONField;
import java.math.BigDecimal;
import java.util.List;
public class StorageLeastPriceResp {
@JSONField(name = "storage_least_price")
private StorageLeastPrice storageLeastPrice;
private List<StorageLeastPrice> storageLeastPrice;
public List<StorageLeastPrice> getStorageLeastPrice() {
return storageLeastPrice;
}
public void setStorageLeastPrice(List<StorageLeastPrice> storageLeastPrice) {
this.storageLeastPrice = storageLeastPrice;
}
public static class StorageLeastPrice{
@JSONField(name = "storage_id")
... ...
... ... @@ -63,3 +63,5 @@ sdk.instanceName=yohopic
yoho.recovery.zkPath=/yh/config/recovery
env.machineId=0
... ...