21 files modified
11 files added
| New file |
| | |
| | | -- 给角色补授权:交割合约单 + 场控设置 + 父目录 1864 |
| | | -- 把 @role_id 换成你登录账号的角色 id(见下方查询) |
| | | |
| | | -- 查当前用户角色 |
| | | -- SELECT ur.user_id, u.username, ur.role_id, r.role_name |
| | | -- FROM tz_sys_user_role ur |
| | | -- JOIN tz_sys_user u ON u.user_id = ur.user_id |
| | | -- JOIN tz_sys_role r ON r.role_id = ur.role_id |
| | | -- WHERE u.username = '你的登录名'; |
| | | |
| | | SET @role_id = 1; |
| | | |
| | | INSERT INTO tz_sys_role_menu (role_id, menu_id) |
| | | SELECT @role_id, m.menu_id |
| | | FROM tz_sys_menu m |
| | | WHERE m.menu_id IN (1864, 1981, 1982) |
| | | AND NOT EXISTS ( |
| | | SELECT 1 FROM tz_sys_role_menu x WHERE x.role_id = @role_id AND x.menu_id = m.menu_id |
| | | ); |
| | | |
| | | -- 验证 |
| | | -- SELECT rm.role_id, m.menu_id, m.name, m.url |
| | | -- FROM tz_sys_role_menu rm |
| | | -- JOIN tz_sys_menu m ON m.menu_id = rm.menu_id |
| | | -- WHERE rm.role_id = @role_id AND m.menu_id IN (1864, 1981, 1982); |
| New file |
| | |
| | | -- 菜单「有记录但前端不显示」常见修复 |
| | | -- 请在【管理后台实际连接的数据库】执行(本地 zh-jys-admin 默认连 https://zhapi.bitget-jp-us.cyou 对应库) |
| | | |
| | | -- 1) 诊断:菜单是否存在 |
| | | SELECT menu_id, parent_id, name, url, type, app_type, order_num |
| | | FROM tz_sys_menu |
| | | WHERE menu_id IN (1864, 1981, 1982) |
| | | OR url IN ('us-spots/us-pickAddr', 'us-spots/us-indexImg', 'us-spots/us-markets-config'); |
| | | |
| | | -- 2) 诊断:当前角色是否授权(把 @role_id 改成你的角色) |
| | | SET @role_id = 1; |
| | | SELECT rm.role_id, m.menu_id, m.name, m.url |
| | | FROM tz_sys_role_menu rm |
| | | JOIN tz_sys_menu m ON m.menu_id = rm.menu_id |
| | | WHERE rm.role_id = @role_id |
| | | AND m.menu_id IN (1864, 1981, 1982); |
| | | |
| | | -- 3) 同步 app_type:与父目录 1864 保持一致(避免 app_type 精确匹配查不到父节点) |
| | | UPDATE tz_sys_menu child |
| | | JOIN tz_sys_menu parent ON parent.menu_id = 1864 |
| | | SET child.app_type = IFNULL(NULLIF(parent.app_type, ''), '1') |
| | | WHERE child.menu_id IN (1981, 1982); |
| | | |
| | | -- 4) 确保 type=1(页面菜单),icon 不为空 |
| | | UPDATE tz_sys_menu |
| | | SET type = 1, icon = IFNULL(NULLIF(icon, ''), 'sql') |
| | | WHERE menu_id IN (1981, 1982); |
| | | |
| | | -- 5) 给角色补授权(1864 + 两个子菜单) |
| | | INSERT INTO tz_sys_role_menu (role_id, menu_id) |
| | | SELECT @role_id, m.menu_id |
| | | FROM tz_sys_menu m |
| | | WHERE m.menu_id IN (1864, 1981, 1982) |
| | | AND NOT EXISTS ( |
| | | SELECT 1 FROM tz_sys_role_menu x WHERE x.role_id = @role_id AND x.menu_id = m.menu_id |
| | | ); |
| | | |
| | | -- 6) 若 1864 本身未授权,补全 1864 全部子菜单给该角色 |
| | | INSERT INTO tz_sys_role_menu (role_id, menu_id) |
| | | SELECT @role_id, m.menu_id |
| | | FROM tz_sys_menu m |
| | | WHERE m.parent_id = 1864 |
| | | AND NOT EXISTS ( |
| | | SELECT 1 FROM tz_sys_role_menu x WHERE x.role_id = @role_id AND x.menu_id = m.menu_id |
| | | ); |
| New file |
| | |
| | | -- 美股交割合约单 + 交割场控设置 — 挂到「股票-显示-美股」(menu_id=1864) 下 |
| | | -- 若你库中美股父菜单 id 不是 1864,先查:SELECT menu_id, name FROM tz_sys_menu WHERE name LIKE '%美股%'; |
| | | -- 执行后退出后台重新登录 |
| | | |
| | | -- 【可选】查看 1864 下现有子菜单 |
| | | -- SELECT menu_id, parent_id, name, url, order_num FROM tz_sys_menu WHERE parent_id = 1864; |
| | | |
| | | -- 1) 交割合约单(US) |
| | | INSERT INTO tz_sys_menu (menu_id, parent_id, name, url, perms, type, icon, order_num, app_type) |
| | | SELECT |
| | | (SELECT IFNULL(MAX(s.menu_id), 0) + 1 FROM tz_sys_menu s), |
| | | 1864, |
| | | '交割合约单(US)', |
| | | 'us-spots/us-pickAddr', |
| | | '', |
| | | 1, |
| | | 'sql', |
| | | IFNULL((SELECT MAX(c.order_num) FROM tz_sys_menu c WHERE c.parent_id = 1864), 0) + 2, |
| | | '1' |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM tz_sys_menu p WHERE p.url = 'us-spots/us-pickAddr' |
| | | ); |
| | | |
| | | -- 2) 交割场控设置(美股) |
| | | INSERT INTO tz_sys_menu (menu_id, parent_id, name, url, perms, type, icon, order_num, app_type) |
| | | SELECT |
| | | (SELECT IFNULL(MAX(s.menu_id), 0) + 1 FROM tz_sys_menu s), |
| | | 1864, |
| | | '交割场控设置(美股)', |
| | | 'us-spots/us-indexImg', |
| | | '', |
| | | 1, |
| | | 'sql', |
| | | IFNULL((SELECT MAX(c.order_num) FROM tz_sys_menu c WHERE c.parent_id = 1864), 0) + 3, |
| | | '1' |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM tz_sys_menu p WHERE p.url = 'us-spots/us-indexImg' |
| | | ); |
| | | |
| | | -- 3) 角色授权:已有 1864 权限的角色 |
| | | INSERT INTO tz_sys_role_menu (role_id, menu_id) |
| | | SELECT DISTINCT rm.role_id, m.menu_id |
| | | FROM tz_sys_role_menu rm |
| | | CROSS JOIN tz_sys_menu m |
| | | WHERE rm.menu_id = 1864 |
| | | AND m.url IN ('us-spots/us-pickAddr', 'us-spots/us-indexImg') |
| | | AND NOT EXISTS ( |
| | | SELECT 1 FROM tz_sys_role_menu x WHERE x.role_id = rm.role_id AND x.menu_id = m.menu_id |
| | | ); |
| | | |
| | | -- 4) 角色授权:已有 1864 下任意子菜单权限的角色 |
| | | INSERT INTO tz_sys_role_menu (role_id, menu_id) |
| | | SELECT DISTINCT rm.role_id, m.menu_id |
| | | FROM tz_sys_role_menu rm |
| | | INNER JOIN tz_sys_menu child ON rm.menu_id = child.menu_id AND child.parent_id = 1864 |
| | | CROSS JOIN tz_sys_menu m |
| | | WHERE m.url IN ('us-spots/us-pickAddr', 'us-spots/us-indexImg') |
| | | AND NOT EXISTS ( |
| | | SELECT 1 FROM tz_sys_role_menu x WHERE x.role_id = rm.role_id AND x.menu_id = m.menu_id |
| | | ); |
| | | |
| | | -- 【验证】角色是否已授权(把 YOUR_ROLE_ID 换成你的角色 id) |
| | | -- SELECT rm.role_id, m.menu_id, m.name, m.url FROM tz_sys_role_menu rm |
| | | -- JOIN tz_sys_menu m ON rm.menu_id = m.menu_id |
| | | -- WHERE m.menu_id IN (1864, 1981, 1982); |
| | | |
| | | -- 【若菜单有、角色无】给管理员角色补授权(示例 role_id=1,请按实际修改) |
| | | -- INSERT INTO tz_sys_role_menu (role_id, menu_id) |
| | | -- SELECT 1, m.menu_id FROM tz_sys_menu m |
| | | -- WHERE m.menu_id IN (1864, 1981, 1982) |
| | | -- AND NOT EXISTS (SELECT 1 FROM tz_sys_role_menu x WHERE x.role_id = 1 AND x.menu_id = m.menu_id); |
| New file |
| | |
| | | -- 美股盘前交易配置 |
| | | CREATE TABLE IF NOT EXISTS t_item_pre_market_config ( |
| | | uuid VARCHAR(64) NOT NULL PRIMARY KEY COMMENT '主键', |
| | | symbol VARCHAR(64) NOT NULL COMMENT '股票代码', |
| | | start_time VARCHAR(8) NOT NULL COMMENT '盘前开始时间 HH:mm 24小时制', |
| | | end_time VARCHAR(8) NOT NULL COMMENT '盘前结束时间 HH:mm 24小时制', |
| | | pre_price DECIMAL(20, 8) NOT NULL COMMENT '盘前价格', |
| | | enabled TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否启用 1启用 0禁用', |
| | | create_time DATETIME NULL COMMENT '创建时间', |
| | | create_time_ts BIGINT NULL, |
| | | create_by VARCHAR(64) NULL COMMENT '创建人', |
| | | update_time DATETIME NULL COMMENT '更新时间', |
| | | update_time_ts BIGINT NULL, |
| | | update_by VARCHAR(64) NULL COMMENT '更新人', |
| | | del_flag INT NOT NULL DEFAULT 0 COMMENT '逻辑删除 0正常', |
| | | remarks VARCHAR(255) NULL COMMENT '备注', |
| | | UNIQUE KEY uk_symbol (symbol) |
| | | ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT '美股盘前交易配置'; |
| | | |
| | | -- 交割合约订单手动盈亏百分比(如 -50 表示亏损50%,150 表示盈利150%) |
| | | ALTER TABLE t_futures_order |
| | | ADD COLUMN manual_profit_percent DOUBLE NULL COMMENT '后台设定的盈亏百分比,结算时优先使用'; |
| New file |
| | |
| | | -- 盘前配置菜单 — 挂到「股票-显示-美股」(menu_id=1864) 下 |
| | | -- 执行后退出后台重新登录 |
| | | |
| | | -- 【可选】查看 1864 下现有子菜单 |
| | | -- SELECT menu_id, parent_id, name, url, order_num FROM tz_sys_menu WHERE parent_id = 1864; |
| | | |
| | | -- 1) 新增盘前菜单 |
| | | INSERT INTO tz_sys_menu (menu_id, parent_id, name, url, perms, type, icon, order_num, app_type) |
| | | SELECT |
| | | (SELECT IFNULL(MAX(s.menu_id), 0) + 1 FROM tz_sys_menu s), |
| | | 1864, |
| | | '盘前配置(US)', |
| | | 'us-spots/us-pre-market-config', |
| | | '', |
| | | 1, |
| | | 'bianji', |
| | | IFNULL((SELECT MAX(c.order_num) FROM tz_sys_menu c WHERE c.parent_id = 1864), 0) + 1, |
| | | '1' |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM tz_sys_menu p |
| | | WHERE p.url = 'us-spots/us-pre-market-config' |
| | | OR (p.parent_id = 1864 AND p.name = '盘前配置(US)') |
| | | ); |
| | | |
| | | -- 2) 角色授权:已有「股票-显示-美股」(1864) 权限的角色 |
| | | INSERT INTO tz_sys_role_menu (role_id, menu_id) |
| | | SELECT DISTINCT rm.role_id, pre.menu_id |
| | | FROM tz_sys_role_menu rm |
| | | INNER JOIN tz_sys_menu pre ON pre.url = 'us-spots/us-pre-market-config' |
| | | WHERE rm.menu_id = 1864 |
| | | AND NOT EXISTS ( |
| | | SELECT 1 FROM tz_sys_role_menu x |
| | | WHERE x.role_id = rm.role_id AND x.menu_id = pre.menu_id |
| | | ); |
| | | |
| | | -- 3) 角色授权:已有 1864 下任意子菜单权限的角色 |
| | | INSERT INTO tz_sys_role_menu (role_id, menu_id) |
| | | SELECT DISTINCT rm.role_id, pre.menu_id |
| | | FROM tz_sys_role_menu rm |
| | | INNER JOIN tz_sys_menu child ON rm.menu_id = child.menu_id AND child.parent_id = 1864 |
| | | INNER JOIN tz_sys_menu pre ON pre.url = 'us-spots/us-pre-market-config' |
| | | WHERE NOT EXISTS ( |
| | | SELECT 1 FROM tz_sys_role_menu x |
| | | WHERE x.role_id = rm.role_id AND x.menu_id = pre.menu_id |
| | | ); |
| | | |
| | | -- 【验证】 |
| | | -- SELECT menu_id, parent_id, name, url FROM tz_sys_menu WHERE parent_id = 1864; |
| | |
| | | return Result.ok("操作成过"); |
| | | |
| | | } |
| | | |
| | | @ApiOperation(value = "设定订单场控方向与盈亏百分比(同时生效)") |
| | | @GetMapping("setOrderManualProfitPercent.action") |
| | | public Result<String> setOrderManualProfitPercent(@RequestParam String orderNo, |
| | | @RequestParam Double manualProfitPercent, |
| | | @RequestParam(required = false) String profitLoss) { |
| | | String msg = futuresOrderService.saveOrderManualProfitPercent(orderNo, manualProfitPercent, profitLoss, |
| | | SecurityUtils.getSysUser().getUsername()); |
| | | if (msg != null && !msg.isEmpty()) { |
| | | return Result.failed(msg); |
| | | } |
| | | return Result.ok("操作成功"); |
| | | } |
| | | } |
| New file |
| | |
| | | package com.yami.trading.admin.controller.item; |
| | | |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.yami.trading.bean.item.domain.ItemPreMarketConfig; |
| | | import com.yami.trading.bean.item.dto.ItemPreMarketConfigDTO; |
| | | import com.yami.trading.bean.item.dto.ItemPreMarketConfigSaveModel; |
| | | import com.yami.trading.common.domain.Result; |
| | | import com.yami.trading.service.item.ItemPreMarketService; |
| | | import com.yami.trading.service.item.ItemService; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.web.bind.annotation.*; |
| | | |
| | | import javax.validation.Valid; |
| | | |
| | | @Api(tags = "【管理后台】美股盘前配置") |
| | | @RestController |
| | | @CrossOrigin |
| | | @RequestMapping({"normal/adminItemPreMarketAction!", "normal/adminItemPreMarketAction!/"}) |
| | | public class AdminItemPreMarketController { |
| | | |
| | | @Autowired |
| | | private ItemPreMarketService itemPreMarketService; |
| | | @Autowired |
| | | private ItemService itemService; |
| | | |
| | | @ApiOperation("盘前配置列表") |
| | | @GetMapping("list.action") |
| | | public Result<IPage<ItemPreMarketConfigDTO>> list(@RequestParam(required = false) String symbol, |
| | | Page<ItemPreMarketConfig> page) { |
| | | String cleanSymbol = itemService.getSymbolByKey(symbol); |
| | | return Result.ok(itemPreMarketService.listRecord(page, cleanSymbol)); |
| | | } |
| | | |
| | | @ApiOperation("盘前配置详情") |
| | | @GetMapping("get.action") |
| | | public Result<ItemPreMarketConfigDTO> get(@RequestParam String uuid) { |
| | | return Result.ok(itemPreMarketService.getDetail(uuid)); |
| | | } |
| | | |
| | | @ApiOperation("新增或修改盘前配置") |
| | | @PostMapping({"save.action", "/save.action"}) |
| | | public Result<String> save(@RequestBody @Valid ItemPreMarketConfigSaveModel model) { |
| | | itemPreMarketService.saveConfig(model); |
| | | return Result.ok("保存成功"); |
| | | } |
| | | |
| | | @ApiOperation("删除盘前配置") |
| | | @DeleteMapping("delete.action") |
| | | public Result<String> delete(@RequestParam String uuid) { |
| | | itemPreMarketService.deleteConfig(uuid); |
| | | return Result.ok("删除成功"); |
| | | } |
| | | } |
| | |
| | | List<SysMenu> newMenuList=new ArrayList<>(); |
| | | if (StrUtil.isNotBlank(appType)){ |
| | | for (SysMenu sysMenu:menuList){ |
| | | if (!StrUtil.isEmpty(sysMenu.getAppType())){ |
| | | if (checkAppType(sysMenu.getAppType(),appType)){ |
| | | newMenuList.add(sysMenu); |
| | | } |
| | | if (StrUtil.isBlank(sysMenu.getAppType()) || checkAppType(sysMenu.getAppType(), appType)){ |
| | | newMenuList.add(sysMenu); |
| | | } |
| | | } |
| | | } |
| | |
| | | import com.yami.trading.common.util.StringUtils; |
| | | import com.yami.trading.common.util.ThreadUtils; |
| | | import com.yami.trading.security.common.util.SecurityUtils; |
| | | import com.yami.trading.service.MarketOpenChecker; |
| | | import com.yami.trading.service.RealNameAuthRecordService; |
| | | import com.yami.trading.service.SessionTokenService; |
| | | import com.yami.trading.service.WalletService; |
| | |
| | | } else { |
| | | data.put("amount", 0); |
| | | } |
| | | data.put("open", MarketOpenChecker.isMarketOpenByItemCloseType(bySymbol.getOpenCloseType())); |
| | | data.put("open", itemService.isOpen(bySymbol.getSymbol())); |
| | | return Result.succeed(data); |
| | | } |
| | | |
| | |
| | | if (bySymbol == null) { |
| | | throw new YamiShopBindException("当前币对不存在"); |
| | | } |
| | | boolean isOpen = MarketOpenChecker.isMarketOpenByItemCloseType(bySymbol.getOpenCloseType()); |
| | | if (!isOpen) { |
| | | if (!itemService.isOpen(bySymbol.getSymbol())) { |
| | | throw new YamiShopBindException("当前已经休市"); |
| | | } |
| | | |
| | |
| | | |
| | | lock = true; |
| | | String session_token = futureOpenAction.getSession_token(); |
| | | Object object = this.sessionTokenService.cacheGet(session_token); |
| | | String cachedPartyId = this.sessionTokenService.cacheGet(session_token); |
| | | this.sessionTokenService.del(session_token); |
| | | User party = this.partyService.findUserByUserCode(partyId); |
| | | User party = this.partyService.getById(partyId); |
| | | if (party == null) { |
| | | throw new YamiShopBindException("用户不存在"); |
| | | } |
| | | if (!party.isEnabled()) { |
| | | throw new YamiShopBindException("用户已锁定"); |
| | | } |
| | | realNameAuthRecordService.requireApproved(party, true); |
| | | if (null == object || !party.getUserId().equals((String) object)) { |
| | | throw new BusinessException("请稍后再试"); |
| | | if (cachedPartyId == null || !partyId.equals(cachedPartyId)) { |
| | | log.warn("交割开仓 session_token 无效, partyId={}, token={}, cachedPartyId={}", |
| | | partyId, session_token, cachedPartyId); |
| | | throw new BusinessException("操作已失效,请刷新页面后重试"); |
| | | } |
| | | Syspara syspara = sysparaService.find("stop_user_internet"); |
| | | String stopUserInternet = syspara.getSvalue(); |
| | |
| | | data.put("order_no", order.getOrderNo()); |
| | | data.put("open_price", order.getTradeAvgPrice().toString()); |
| | | return Result.succeed(data); |
| | | } catch (BusinessException | YamiShopBindException e) { |
| | | throw e; |
| | | } catch (Exception e) { |
| | | log.error("开仓异常", e); |
| | | throw new YamiShopBindException(e.getMessage()); |
| | | throw new YamiShopBindException(e.getMessage() != null ? e.getMessage() : "请稍后再试"); |
| | | } finally { |
| | | if (lock) { |
| | | ThreadUtils.sleep(100); |
| | |
| | | import com.yami.trading.common.util.RandomUtil; |
| | | import com.yami.trading.common.util.StringUtils; |
| | | import com.yami.trading.security.common.util.SecurityUtils; |
| | | import com.yami.trading.service.MarketOpenChecker; |
| | | import com.yami.trading.service.data.DataService; |
| | | import com.yami.trading.service.item.ItemService; |
| | | import com.yami.trading.service.item.ItemUserOptionalItemService; |
| | |
| | | @GetMapping(ITEM + "queryBySymbol.action") |
| | | public Result<Item> queryBySymbol(String symbol) { |
| | | Item item = itemService.findBySymbol(symbol); |
| | | item.setOpen(MarketOpenChecker.isMarketOpenByItemCloseType(item.getOpenCloseType())); |
| | | item.setOpen(itemService.isOpen(symbol)); |
| | | return Result.succeed(item); |
| | | } |
| | | |
| | |
| | | import com.yami.trading.huobi.data.TimeZoneConverterService; |
| | | import com.yami.trading.huobi.tradingview.service.TradingViewService; |
| | | import com.yami.trading.service.data.DataService; |
| | | import com.yami.trading.service.item.ItemPreMarketService; |
| | | import com.yami.trading.service.item.ItemService; |
| | | import io.swagger.annotations.Api; |
| | | import io.swagger.annotations.ApiOperation; |
| | |
| | | public class KlineController { |
| | | @Autowired |
| | | private ItemService itemService; |
| | | @Autowired |
| | | private ItemPreMarketService itemPreMarketService; |
| | | @Autowired |
| | | @Qualifier("dataService") |
| | | private DataService dataService; |
| | |
| | | |
| | | // Fetch Kline data from service (for cryptos) |
| | | List<Kline> data = this.dataService.kline(symbol, line); |
| | | itemPreMarketService.applyPreMarketToLatestKline(symbol, data); |
| | | |
| | | // Return an empty response if no data is found |
| | | if (Objects.isNull(data)) { |
| | |
| | | if (cachedData != null && lastUpdateTime != null) { |
| | | long currentTime = System.currentTimeMillis(); |
| | | if ((currentTime - lastUpdateTime) <= TimeUnit.MINUTES.toMillis(5)) { |
| | | itemPreMarketService.applyPreMarketToLatestKline(symbol, cachedData); |
| | | return Result.succeed(this.build(cachedData, twForLine, symbol)); |
| | | } |
| | | } |
| | |
| | | |
| | | // Format Kline data timestamps |
| | | formatKlineTimestamps(data, line); |
| | | itemPreMarketService.applyPreMarketToLatestKline(symbol, data); |
| | | |
| | | return Result.succeed(this.build(data, line, symbol)); |
| | | } |
| | |
| | | @TableField(exist = false) |
| | | private int tableIndex; |
| | | |
| | | @TableField(exist = false) |
| | | @ApiModelProperty("是否处于盘前时段") |
| | | private transient Boolean preMarketActive; |
| | | |
| | | @TableField(exist = false) |
| | | @ApiModelProperty("盘前配置价格") |
| | | private transient Double preMarketPrice; |
| | | |
| | | @Override |
| | | public Object clone() throws CloneNotSupportedException { |
| | | return super.clone(); |
| | |
| | | @ApiModelProperty("t_futures_para") |
| | | private String futuresParaId; |
| | | |
| | | @ApiModelProperty("后台设定的盈亏百分比,结算时优先使用,如-50表示亏损50%,150表示盈利150%") |
| | | private Double manualProfitPercent; |
| | | |
| | | } |
| | |
| | | @ApiModelProperty("订单盈亏控制情况(优先级高于交割场控设置)") |
| | | private String profitLosssStr; |
| | | |
| | | @ApiModelProperty("后台设定盈亏百分比") |
| | | private Double manualProfitPercent; |
| | | |
| | | public String getProfitLosssStr() { |
| | | if("profit".equalsIgnoreCase(profitLoss)){ |
| | | return "盈利"; |
| New file |
| | |
| | | package com.yami.trading.bean.item.domain; |
| | | |
| | | import com.baomidou.mybatisplus.annotation.TableName; |
| | | import com.yami.trading.common.domain.BaseEntity; |
| | | import lombok.Data; |
| | | import lombok.EqualsAndHashCode; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | @Data |
| | | @EqualsAndHashCode(callSuper = false) |
| | | @TableName("t_item_pre_market_config") |
| | | public class ItemPreMarketConfig extends BaseEntity { |
| | | |
| | | private static final long serialVersionUID = 1L; |
| | | |
| | | private String symbol; |
| | | |
| | | /** 盘前开始 HH:mm */ |
| | | private String startTime; |
| | | |
| | | /** 盘前结束 HH:mm */ |
| | | private String endTime; |
| | | |
| | | private BigDecimal prePrice; |
| | | |
| | | /** 1启用 0禁用 */ |
| | | private Integer enabled; |
| | | } |
| New file |
| | |
| | | package com.yami.trading.bean.item.dto; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import java.math.BigDecimal; |
| | | |
| | | @Data |
| | | public class ItemPreMarketConfigDTO { |
| | | |
| | | private String uuid; |
| | | private String symbol; |
| | | private String symbolName; |
| | | private String startTime; |
| | | private String endTime; |
| | | private BigDecimal prePrice; |
| | | private Integer enabled; |
| | | } |
| New file |
| | |
| | | package com.yami.trading.bean.item.dto; |
| | | |
| | | import lombok.Data; |
| | | |
| | | import javax.validation.constraints.NotBlank; |
| | | import javax.validation.constraints.NotNull; |
| | | import java.math.BigDecimal; |
| | | |
| | | @Data |
| | | public class ItemPreMarketConfigSaveModel { |
| | | |
| | | private String uuid; |
| | | |
| | | @NotBlank(message = "股票代码不能为空") |
| | | private String symbol; |
| | | |
| | | @NotBlank(message = "盘前开始时间不能为空") |
| | | private String startTime; |
| | | |
| | | @NotBlank(message = "盘前结束时间不能为空") |
| | | private String endTime; |
| | | |
| | | @NotNull(message = "盘前价格不能为空") |
| | | private BigDecimal prePrice; |
| | | |
| | | private Integer enabled = 1; |
| | | } |
| | |
| | | import com.yami.trading.bean.item.domain.Item; |
| | | import com.yami.trading.huobi.data.DataCache; |
| | | import com.yami.trading.service.data.DataService; |
| | | import com.yami.trading.service.item.ItemPreMarketService; |
| | | import com.yami.trading.service.item.ItemService; |
| | | import com.yami.trading.service.syspara.SysparaService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | ItemService itemService; |
| | | @Autowired |
| | | KlineService klineService; |
| | | @Autowired |
| | | ItemPreMarketService itemPreMarketService; |
| | | |
| | | /** |
| | | * 根据币种分类 获取实时价格数据 |
| | |
| | | if (!StrUtil.isEmpty(symbols) && !symbols.contains(",")) { |
| | | Realtime realtime = DataCache.getRealtime(symbols); |
| | | if (realtime != null) { |
| | | list.add(realtime); |
| | | list.add(applyPreMarket(realtime)); |
| | | } |
| | | } |
| | | // 如果不传参数 返回全部币种行情 |
| | |
| | | for (Item item : items) { |
| | | Realtime realtime = DataCache.getRealtime(item.getSymbol()); |
| | | if (realtime != null) { |
| | | list.add(realtime); |
| | | list.add(applyPreMarket(realtime)); |
| | | } |
| | | } |
| | | } |
| | |
| | | for (String oneSymbol : symbolArr) { |
| | | Realtime realtime = DataCache.getRealtime(oneSymbol); |
| | | if (realtime != null) { |
| | | list.add(realtime); |
| | | list.add(applyPreMarket(realtime)); |
| | | } else { |
| | | log.error("realtime is null; 币种->{}", oneSymbol); |
| | | } |
| | | } |
| | | } |
| | | return list; |
| | | } |
| | | |
| | | private Realtime applyPreMarket(Realtime realtime) { |
| | | if (realtime == null || itemPreMarketService == null) { |
| | | return realtime; |
| | | } |
| | | try { |
| | | Realtime copy = (Realtime) realtime.clone(); |
| | | return itemPreMarketService.applyPreMarketPrice(copy); |
| | | } catch (CloneNotSupportedException e) { |
| | | log.warn("clone realtime failed, symbol={}", realtime.getSymbol(), e); |
| | | return realtime; |
| | | } |
| | | } |
| | | |
| | | /** |
| | |
| | | |
| | | Realtime realtime = DataCache.getLatestRealTime(symbol); |
| | | if (realtime != null) { |
| | | realtime = applyPreMarket(realtime); |
| | | Kline kline = null; |
| | | if (KlineConstant.PERIOD_1MIN.equals(line)) { |
| | | kline = klineService.bulidKline1Minute(realtime, KlineConstant.PERIOD_1MIN); |
| | |
| | | import com.yami.trading.huobi.data.TimeZoneConverterService; |
| | | import com.yami.trading.huobi.websocket.WebSocketServer; |
| | | import com.yami.trading.huobi.websocket.WebSocketSession; |
| | | import com.yami.trading.service.item.ItemPreMarketService; |
| | | import com.yami.trading.service.item.ItemService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | |
| | | public class RealtimePushJob implements Runnable { |
| | | @Autowired |
| | | private ItemService itemService; |
| | | |
| | | @Autowired |
| | | private ItemPreMarketService itemPreMarketService; |
| | | |
| | | @Autowired |
| | | private TimeZoneConverterService timeZoneConverterService; |
| | |
| | | Realtime realtimeData = DataCache.getRealtime(symbol); |
| | | if (realtimeData == null) { |
| | | log.error("realtimeHandle 获取{} 数据为空", symbol); |
| | | } else if (itemPreMarketService != null) { |
| | | try { |
| | | Realtime copy = (Realtime) realtimeData.clone(); |
| | | realtimeData = itemPreMarketService.applyPreMarketPrice(copy); |
| | | } catch (CloneNotSupportedException e) { |
| | | log.warn("clone realtime failed, symbol={}", symbol, e); |
| | | } |
| | | } |
| | | this.realtimeRevise(realtimeResultMap, realtimeData, symbol); |
| | | } |
| | |
| | | import com.yami.trading.bean.item.domain.Item; |
| | | import com.yami.trading.common.constants.RedisKeys; |
| | | import com.yami.trading.common.constants.RedisLockKeyConstants; |
| | | import com.yami.trading.service.MarketOpenChecker; |
| | | import com.yami.trading.common.util.RedisUtil; |
| | | import com.yami.trading.common.util.ThreadUtils; |
| | | import com.yami.trading.service.contract.ContractApplyOrderService; |
| | | import com.yami.trading.service.contract.ContractLock; |
| | | import com.yami.trading.service.contract.ContractOrderService; |
| | | import com.yami.trading.service.data.DataService; |
| | | import com.yami.trading.service.item.ItemPreMarketService; |
| | | import com.yami.trading.service.item.ItemService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.redisson.api.RLock; |
| | |
| | | |
| | | @Autowired |
| | | private ItemService itemService; |
| | | |
| | | @Autowired(required = false) |
| | | private ItemPreMarketService itemPreMarketService; |
| | | |
| | | @Autowired |
| | | private RedissonClient redissonClient; |
| | |
| | | continue; |
| | | } |
| | | |
| | | List<Realtime> realtime_list = this.dataService.realtime(order.getSymbol()); |
| | | Realtime realtime = null; |
| | | if (realtime_list.size() > 0) { |
| | | realtime = realtime_list.get(0); |
| | | //log.info("---> ContractApplyOrderHandleJob.run symbol:{} 的 realTime 值为:{}", order.getSymbol(), realtime); |
| | | } else { |
| | | log.warn("---> ContractApplyOrderHandleJob.run symbol:{} 的 realTime 值为空", order.getSymbol()); |
| | | continue; |
| | | } |
| | | |
| | | // 休市不做撮合 |
| | | Item bySymbol = itemService.findBySymbol(order.getSymbol()); |
| | | if (bySymbol == null) { |
| | | continue; |
| | | } |
| | | boolean isOpen = MarketOpenChecker.isMarketOpenByItemCloseType(bySymbol.getOpenCloseType()); |
| | | boolean preMarketActive = itemPreMarketService != null |
| | | && itemPreMarketService.isPreMarketTradingActive(order.getSymbol()); |
| | | boolean isOpen = itemService.isOpen(order.getSymbol()); |
| | | if (!isOpen) { |
| | | //log.warn("---> ContractApplyOrderHandleJob.run symbol:{} 未open", order.getSymbol()); |
| | | continue; |
| | | } |
| | | |
| | | List<Realtime> realtime_list = this.dataService.realtime(order.getSymbol()); |
| | | Realtime realtime = null; |
| | | if (realtime_list.size() > 0) { |
| | | realtime = realtime_list.get(0); |
| | | } else { |
| | | log.warn("---> ContractApplyOrderHandleJob.run symbol:{} 的 realTime 值为空", order.getSymbol()); |
| | | continue; |
| | | } |
| | | if (preMarketActive && itemPreMarketService != null) { |
| | | Double prePrice = itemPreMarketService.getActivePreMarketPrice(order.getSymbol()); |
| | | if (prePrice != null) { |
| | | realtime.setClose(prePrice); |
| | | } |
| | | } |
| | | |
| | | if ("limit".equals(order.getOrderPriceType())) { |
| | | /** |
| | | * 限价单 |
| | | */ |
| | | if ("buy".equals(order.getDirection())) { |
| | | /** |
| | | * 买涨 |
| | | */ |
| | | if (preMarketActive) { |
| | | processSuccess = this.handle(order, realtime); |
| | | } else if ("buy".equals(order.getDirection())) { |
| | | if (BigDecimal.valueOf(realtime.getClose()).compareTo(order.getPrice()) <= 0) { |
| | | processSuccess = this.handle(order, realtime); |
| | | } |
| | | } else { |
| | | /** |
| | | * 买跌 |
| | | */ |
| | | if (BigDecimal.valueOf(realtime.getClose()).compareTo(order.getPrice()) >= 0) { |
| | | processSuccess = this.handle(order, realtime); |
| | | } |
| | | } |
| | | } else { |
| | | /** |
| | | * 非限制,直接进入市场 |
| | | */ |
| | | processSuccess = this.handle(order, realtime); |
| | | } |
| | | } catch (Exception e) { |
| | |
| | | import com.yami.trading.bean.future.domain.FuturesLock; |
| | | import com.yami.trading.bean.future.domain.FuturesOrder; |
| | | import com.yami.trading.bean.item.domain.Item; |
| | | import com.yami.trading.service.MarketOpenChecker; |
| | | import com.yami.trading.common.util.ThreadUtils; |
| | | import com.yami.trading.service.future.FuturesOrderService; |
| | | import com.yami.trading.service.item.ItemService; |
| | |
| | | if(bySymbol == null){ |
| | | continue; |
| | | } |
| | | boolean isOpen = MarketOpenChecker.isMarketOpenByItemCloseType(bySymbol.getOpenCloseType()); |
| | | boolean isOpen = itemService.isOpen(order.getSymbol()); |
| | | if(!isOpen){ |
| | | continue; |
| | | } |
| New file |
| | |
| | | package com.yami.trading.dao.item; |
| | | |
| | | import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
| | | import com.yami.trading.bean.item.domain.ItemPreMarketConfig; |
| | | |
| | | public interface ItemPreMarketConfigMapper extends BaseMapper<ItemPreMarketConfig> { |
| | | } |
| | |
| | | * @return |
| | | */ |
| | | public static boolean isMarketOpenByItemCloseType(String closeType) { |
| | | // 放开美股测试 |
| | | // if(closeType.equalsIgnoreCase(US_STOCKS)){ |
| | | // return true; |
| | | // } |
| | | List<String> stocksType = Lists.newArrayList(A_STOCKS, HK_STOCKS,MY_STOCKS, US_STOCKS, TW_STOCKS, JP_STOCKS, INDIA_STOCKS, UK_STOCKS, DE_STOCKS, BZ_STOCKS); |
| | | if (StrUtil.isBlank(closeType)) { |
| | | return true; |
| | |
| | | import com.yami.trading.service.WalletService; |
| | | import com.yami.trading.service.data.DataService; |
| | | import com.yami.trading.service.item.ItemLeverageService; |
| | | import com.yami.trading.service.item.ItemPreMarketService; |
| | | import com.yami.trading.service.item.ItemService; |
| | | import com.yami.trading.service.rate.ExchangeRateService; |
| | | import com.yami.trading.service.syspara.SysparaService; |
| | |
| | | private UserService partyService; |
| | | @Autowired |
| | | private ItemService itemService; |
| | | @Autowired(required = false) |
| | | private ItemPreMarketService itemPreMarketService; |
| | | @Qualifier("dataService") |
| | | @Autowired |
| | | private DataService dataService; |
| | |
| | | |
| | | //rose 校验如果是数字货币,则7*24 |
| | | String type = item.getType(); |
| | | if(!type.equalsIgnoreCase(Item.cryptos)){ |
| | | //外汇交易后台接口 |
| | | if (!type.equalsIgnoreCase(Item.cryptos)) { |
| | | boolean preMarketActive = itemPreMarketService != null |
| | | && itemPreMarketService.isPreMarketTradingActive(order.getSymbol()); |
| | | boolean orderOpen = this.sysparaService.find("order_open").getBoolean(); |
| | | if (!orderOpen) { |
| | | if (!orderOpen && !preMarketActive && !itemService.isOpen(order.getSymbol())) { |
| | | throw new YamiShopBindException("不在交易时段"); |
| | | } |
| | | } |
| | |
| | | } |
| | | } |
| | | } |
| | | tryImmediatePreMarketFill(order); |
| | | } |
| | | |
| | | /** 盘前时段:市价/限价委托立即按盘前价成交,避免停留在委托队列 */ |
| | | private void tryImmediatePreMarketFill(ContractApplyOrder order) { |
| | | if (itemPreMarketService == null || !itemPreMarketService.isPreMarketTradingActive(order.getSymbol())) { |
| | | return; |
| | | } |
| | | if (!ContractApplyOrder.OFFSET_OPEN.equals(order.getOffset())) { |
| | | return; |
| | | } |
| | | Double prePrice = itemPreMarketService.getActivePreMarketPrice(order.getSymbol()); |
| | | if (prePrice == null) { |
| | | log.warn("盘前已激活但未取到盘前价 symbol={} orderNo={}", order.getSymbol(), order.getOrderNo()); |
| | | return; |
| | | } |
| | | Realtime realtime = new Realtime(); |
| | | realtime.setSymbol(order.getSymbol()); |
| | | realtime.setClose(prePrice); |
| | | contractOrderService.saveOpen(order, realtime); |
| | | } |
| | | |
| | | /** |
| | |
| | | package com.yami.trading.service.exchange.impl; |
| | | |
| | | import cn.hutool.core.collection.CollectionUtil; |
| | | import cn.hutool.core.util.StrUtil; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
| | | import com.baomidou.mybatisplus.core.toolkit.Wrappers; |
| | |
| | | import com.yami.trading.common.util.StringUtils; |
| | | import com.yami.trading.common.util.UTCDateUtils; |
| | | import com.yami.trading.dao.exchange.ExchangeApplyOrderMapper; |
| | | import com.yami.trading.service.MarketOpenChecker; |
| | | import com.yami.trading.service.MoneyLogService; |
| | | import com.yami.trading.service.WalletService; |
| | | import com.yami.trading.service.data.DataService; |
| | | import com.yami.trading.service.exchange.ExchangeApplyOrderService; |
| | | import com.yami.trading.service.item.ItemPreMarketService; |
| | | import com.yami.trading.service.item.ItemService; |
| | | import com.yami.trading.service.rate.ExchangeRateService; |
| | | import com.yami.trading.service.syspara.SysparaService; |
| | | import com.yami.trading.service.user.UserDataService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.context.annotation.Lazy; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | |
| | | @Autowired |
| | | ItemService itemService; |
| | | @Autowired |
| | | ItemPreMarketService itemPreMarketService; |
| | | @Autowired |
| | | DataService dataService; |
| | | @Autowired |
| | | WalletService walletService; |
| | |
| | | ExchangeApplyOrderMapper exchangeApplyOrderMapper; |
| | | @Autowired |
| | | private ExchangeRateService exchangeRateService; |
| | | @Autowired |
| | | @Lazy |
| | | private ExchangeApplyOrderService self; |
| | | |
| | | @Override |
| | | public List<ExchangeApplyOrder> findSubmitted() { |
| | |
| | | * 现货交易买入 |
| | | */ |
| | | @Override |
| | | @Transactional |
| | | public void saveSpotTradOpen(ExchangeApplyOrder order) { |
| | | order.setOrderNo(DateUtil.getToday("yyMMddHHmmss") + RandomUtil.getRandomNum(8)); |
| | | double fee = Arith.mul(order.getVolume(), sysparaService.find("exchange_apply_order_buy_fee").getDouble()); |
| | | Wallet wallet = this.walletService.saveWalletByPartyId(order.getPartyId()); |
| | | String symbol = order.getSymbol(); |
| | | Item item = itemService.findBySymbol(symbol); |
| | | if (item != null && StrUtil.isNotBlank(item.getSymbol())) { |
| | | symbol = item.getSymbol(); |
| | | order.setSymbol(symbol); |
| | | } |
| | | double amountBefore = wallet.getMoney().doubleValue(); |
| | | double realAmount = order.getVolume() + fee; |
| | | String type = item.getType(); |
| | |
| | | throw new YamiShopBindException("余额不足"); |
| | | } |
| | | // 休市期间,下市价订单, 叫 "竞价单",竞价单订单号长度为21,其他订单号长度为20位 |
| | | boolean preMarketActive = itemPreMarketService != null |
| | | && itemPreMarketService.isPreMarketTradingActive(symbol); |
| | | if (ExchangeApplyOrder.ORDER_PRICE_TYPE_OPPONENT.equals(order.getOrderPriceType())) { |
| | | boolean isOpen = MarketOpenChecker.isMarketOpenByItemCloseType(item.getOpenCloseType()); |
| | | boolean isOpen = preMarketActive || itemService.isOpen(symbol); |
| | | if (!isOpen) { |
| | | order.setOrderNo(DateUtil.getToday("yyMMddHHmmss") + RandomUtil.getRandomNum(9)); |
| | | } |
| | |
| | | order.setCreateTime(new Date()); |
| | | save(order); |
| | | userDataService.saveBuy(order); |
| | | tryImmediatePreMarketFill(order, symbol, preMarketActive); |
| | | } |
| | | |
| | | /** |
| | |
| | | order.setCreateTime(new Date()); |
| | | order.setFee(Arith.mul(order.getVolume(), sysparaService.find("exchange_apply_order_sell_fee").getDouble())); |
| | | Item item = itemService.findBySymbol(order.getSymbol()); |
| | | // 休市期间,不让下市价卖出单 |
| | | String symbol = order.getSymbol(); |
| | | if (item != null && StrUtil.isNotBlank(item.getSymbol())) { |
| | | symbol = item.getSymbol(); |
| | | order.setSymbol(symbol); |
| | | } |
| | | boolean preMarketActive = itemPreMarketService != null |
| | | && itemPreMarketService.isPreMarketTradingActive(symbol); |
| | | // 休市期间,不让下市价卖出单(盘前除外) |
| | | if (ExchangeApplyOrder.ORDER_PRICE_TYPE_OPPONENT.equals(order.getOrderPriceType())) { |
| | | boolean isOpen = MarketOpenChecker.isMarketOpenByItemCloseType(item.getOpenCloseType()); |
| | | if (!isOpen) { |
| | | if (!preMarketActive && !itemService.isOpen(symbol)) { |
| | | throw new YamiShopBindException("The current stock market is closed"); |
| | | } |
| | | } |
| | | WalletExtend walletExtend = walletService.saveExtendByPara(order.getPartyId(), order.getSymbol()); |
| | | WalletExtend walletExtend = walletService.saveExtendByPara(order.getPartyId(), symbol); |
| | | double amount_before = walletExtend.getAmount(); |
| | | walletService.updateExtend(walletExtend.getPartyId(), walletExtend.getWallettype(), Arith.sub(0, order.getVolume())); |
| | | String type = item.getType(); |
| | |
| | | moneylog.setAmountAfter(BigDecimal.valueOf(Arith.sub(amount_before, order.getVolume()))); |
| | | moneylog.setLog(name + type2 + "现货交易卖出,订单号[" + order.getOrderNo() + "]"); |
| | | moneylog.setUserId(order.getPartyId()); |
| | | moneylog.setWalletType(order.getSymbol()); |
| | | moneylog.setSymbol(order.getSymbol()); |
| | | moneylog.setWalletType(symbol); |
| | | moneylog.setSymbol(symbol); |
| | | moneylog.setContentType(type2 + Constants.MONEYLOG_CONTENT_CLOSE); |
| | | moneylog.setCreateTime(new Date()); |
| | | moneylog.setUpdateTime(new Date()); |
| | | moneyLogService.save(moneylog); |
| | | save(order); |
| | | userDataService.saveSell(order); |
| | | tryImmediatePreMarketFill(order, symbol, preMarketActive); |
| | | } |
| | | |
| | | /** 盘前时段:限价/市价均立即按盘前价成交,避免进入委托队列 */ |
| | | private void tryImmediatePreMarketFill(ExchangeApplyOrder order, String symbol, boolean preMarketActive) { |
| | | if (!preMarketActive || itemPreMarketService == null) { |
| | | return; |
| | | } |
| | | Double prePrice = itemPreMarketService.getActivePreMarketPrice(symbol); |
| | | if (prePrice == null) { |
| | | log.warn("盘前已激活但未取到盘前价 symbol={} orderNo={}", symbol, order.getOrderNo()); |
| | | return; |
| | | } |
| | | order.setPrice(prePrice); |
| | | if (ExchangeApplyOrder.OFFSET_OPEN.equals(order.getOffset())) { |
| | | self.saveSpotTradOpenCreated(order, prePrice); |
| | | } else if (ExchangeApplyOrder.OFFSET_CLOSE.equals(order.getOffset())) { |
| | | self.saveSpotTradCloseCreated(order, prePrice); |
| | | } |
| | | } |
| | | //================================================闪兑============================================================== |
| | | |
| | |
| | | import com.yami.trading.bean.purchasing.dto.ExchangeLock; |
| | | import com.yami.trading.common.exception.YamiShopBindException; |
| | | import com.yami.trading.common.util.ThreadUtils; |
| | | import com.yami.trading.service.MarketOpenChecker; |
| | | import com.yami.trading.service.data.DataService; |
| | | import com.yami.trading.service.exchange.ExchangeApplyOrderService; |
| | | import com.yami.trading.service.item.ItemPreMarketService; |
| | | import com.yami.trading.service.item.ItemService; |
| | | import com.yami.trading.service.syspara.SysparaService; |
| | | import lombok.extern.slf4j.Slf4j; |
| | |
| | | @Autowired |
| | | ItemService itemService; |
| | | @Autowired |
| | | ItemPreMarketService itemPreMarketService; |
| | | @Autowired |
| | | SysparaService sysparaService; |
| | | |
| | | public void start() { |
| | |
| | | if (bySymbol == null) { |
| | | throw new YamiShopBindException("当前币对不存在"); |
| | | } |
| | | boolean isOpen = MarketOpenChecker.isMarketOpenByItemCloseType(bySymbol.getOpenCloseType()); |
| | | boolean isOpen = itemService.isOpen(order.getSymbol()); |
| | | if (!isOpen) { |
| | | continue; |
| | | } |
| | | Realtime realtime = this.dataService.realtime(order.getSymbol()).get(0); |
| | | Double fillPrice = realtime.getClose(); |
| | | boolean preMarketActive = itemPreMarketService != null |
| | | && itemPreMarketService.isPreMarketTradingActive(order.getSymbol()); |
| | | if (preMarketActive) { |
| | | Double prePrice = itemPreMarketService.getActivePreMarketPrice(order.getSymbol()); |
| | | if (prePrice != null) { |
| | | fillPrice = prePrice; |
| | | } |
| | | } |
| | | // 限价单 |
| | | if (ExchangeApplyOrder.ORDER_PRICE_TYPE_LIMIT.equals(order.getOrderPriceType())) { |
| | | if (ExchangeApplyOrder.OFFSET_OPEN.equals(order.getOffset())) { |
| | | if (preMarketActive) { |
| | | this.handle(order, fillPrice); |
| | | } else if (ExchangeApplyOrder.OFFSET_OPEN.equals(order.getOffset())) { |
| | | if (realtime.getClose() <= order.getPrice()) { |
| | | this.handle(order, realtime.getClose()); |
| | | } |
| | |
| | | } |
| | | // 市价单 |
| | | else { |
| | | this.handle(order, realtime.getClose()); |
| | | this.handle(order, fillPrice); |
| | | } |
| | | } |
| | | } catch (Exception e) { |
| | |
| | | * @param operaName |
| | | * @return |
| | | */ |
| | | public String saveOrderManualProfitPercent(String orderNo, Double manualProfitPercent, String profitLoss, String operaName) { |
| | | if (manualProfitPercent == null) { |
| | | return "盈亏百分比不能为空"; |
| | | } |
| | | double absPercent = Math.abs(manualProfitPercent); |
| | | if (absPercent <= 0) { |
| | | return "盈亏百分比必须大于0"; |
| | | } |
| | | double signedPercent; |
| | | String resolvedProfitLoss; |
| | | if (StrUtil.isNotBlank(profitLoss)) { |
| | | resolvedProfitLoss = profitLoss.trim().toLowerCase(); |
| | | if ("loss".equals(resolvedProfitLoss)) { |
| | | signedPercent = -absPercent; |
| | | resolvedProfitLoss = "loss"; |
| | | } else if ("profit".equals(resolvedProfitLoss)) { |
| | | signedPercent = absPercent; |
| | | resolvedProfitLoss = "profit"; |
| | | } else { |
| | | return "场控方向无效"; |
| | | } |
| | | } else { |
| | | signedPercent = manualProfitPercent; |
| | | resolvedProfitLoss = signedPercent >= 0 ? "profit" : "loss"; |
| | | } |
| | | String message = ""; |
| | | boolean lock = false; |
| | | while (true) { |
| | | try { |
| | | if (!FuturesLock.add(orderNo)) { |
| | | continue; |
| | | } |
| | | lock = true; |
| | | FuturesOrder futuresOrder = (FuturesOrder) RedisUtil.get(FuturesRedisKeys.FUTURES_SUBMITTED_ORDERNO + orderNo); |
| | | if (futuresOrder == null) { |
| | | futuresOrder = cache.get(orderNo); |
| | | } |
| | | if (futuresOrder == null || FuturesOrder.STATE_CREATED.equals(futuresOrder.getState())) { |
| | | message = "订单已结算或不存在"; |
| | | break; |
| | | } |
| | | Double oldPercent = futuresOrder.getManualProfitPercent(); |
| | | String oldProfitLoss = futuresOrder.getProfitLoss(); |
| | | futuresOrder.setProfitLoss(resolvedProfitLoss); |
| | | futuresOrder.setManualProfitPercent(signedPercent); |
| | | applyManualProfitPercentToOrder(futuresOrder); |
| | | Double close = futuresOrder.getCloseAvgPrice(); |
| | | if (close == null) { |
| | | close = futuresOrder.getTradeAvgPrice(); |
| | | } |
| | | refreshCache(futuresOrder, close); |
| | | RedisUtil.set(FuturesRedisKeys.FUTURES_SUBMITTED_ORDERNO + futuresOrder.getOrderNo(), futuresOrder); |
| | | cache.put(futuresOrder.getOrderNo(), futuresOrder); |
| | | updateById(futuresOrder); |
| | | |
| | | User party = userService.getById(futuresOrder.getPartyId()); |
| | | Log log = new Log(); |
| | | log.setCategory(Constants.LOG_CATEGORY_OPERATION); |
| | | log.setOperator(operaName); |
| | | log.setUsername(party.getUserName()); |
| | | log.setUserId(party.getUserId()); |
| | | log.setCreateTime(new Date()); |
| | | log.setLog("设定交割订单场控与盈亏比例。订单号[" + futuresOrder.getOrderNo() + "],场控[" |
| | | + Constants.PROFIT_LOSS_TYPE.get(oldProfitLoss) + "→" |
| | | + Constants.PROFIT_LOSS_TYPE.get(resolvedProfitLoss) + "],比例[" |
| | | + oldPercent + "→" + signedPercent + "%]."); |
| | | this.logService.save(log); |
| | | ThreadUtils.sleep(100); |
| | | } catch (Throwable e) { |
| | | log.error("error:", e); |
| | | message = "修改错误"; |
| | | } finally { |
| | | if (lock) { |
| | | FuturesLock.remove(orderNo); |
| | | break; |
| | | } |
| | | } |
| | | } |
| | | return message; |
| | | } |
| | | |
| | | public String saveOrderPorfitOrLoss(String orderNo, String porfitOrLoss, String operaName) { |
| | | String message = ""; |
| | | boolean lock = false; |
| | |
| | | Map<String, Double> futuresAssetsOld = this.walletService.getMoneyFuturesByOrder(futuresOrder); |
| | | |
| | | String oldProfitLoss = futuresOrder.getProfitLoss(); |
| | | futuresOrder.setManualProfitPercent(null); |
| | | futuresOrder.setProfitLoss(porfitOrLoss); |
| | | |
| | | RedisUtil.set(FuturesRedisKeys.FUTURES_SUBMITTED_ORDERNO + futuresOrder.getOrderNo(), futuresOrder); |
| | |
| | | realtime = realtime_list.get(0); |
| | | } |
| | | if (null == realtime) { |
| | | throw new BusinessException(1, "请稍后再试"); |
| | | throw new BusinessException(1, "行情获取失败,请稍后再试"); |
| | | } |
| | | |
| | | Double volume = futuresOrder.getVolume(); |
| | |
| | | order.setRemainTime(fomatTime(remain_time)); |
| | | order.setCloseAvgPrice(close); |
| | | |
| | | double futures_loss_part = Double.valueOf(this.sysparaService.find("futures_loss_part").getSvalue()); |
| | | |
| | | double closeAvgPrice = order.getCloseAvgPrice().doubleValue(); |
| | | double tradeAvgPrice = order.getTradeAvgPrice().doubleValue(); |
| | | if (FuturesOrder.DIRECTION_BUY.equals(order.getDirection())) { |
| | | /* |
| | | * 0 买涨 |
| | | */ |
| | | if (closeAvgPrice >= tradeAvgPrice) { |
| | | DecimalFormat df = new DecimalFormat("#.##"); |
| | | order.setProfit(Double.valueOf(df.format(Arith.mul(order.getVolume(), order.getProfitRatio())))); |
| | | } |
| | | |
| | | if (closeAvgPrice <= tradeAvgPrice) { |
| | | if (futures_loss_part == 2) { |
| | | DecimalFormat df = new DecimalFormat("#.##"); |
| | | order.setProfit(Arith.sub(0, Double.valueOf(df.format(Arith.mul(order.getVolume(), order.getProfitRatio()))))); |
| | | } else { |
| | | order.setProfit(Arith.sub(0, order.getVolume())); |
| | | } |
| | | } |
| | | if (order.getManualProfitPercent() != null) { |
| | | applyManualProfitPercentToOrder(order); |
| | | } else { |
| | | /* |
| | | * 1 买跌 |
| | | */ |
| | | if (closeAvgPrice <= tradeAvgPrice) { |
| | | DecimalFormat df = new DecimalFormat("#.##"); |
| | | order.setProfit(Double.valueOf(df.format(Arith.mul(order.getVolume(), order.getProfitRatio())))); |
| | | } |
| | | if (closeAvgPrice >= tradeAvgPrice) { |
| | | if (futures_loss_part == 2) { |
| | | double futures_loss_part = Double.valueOf(this.sysparaService.find("futures_loss_part").getSvalue()); |
| | | |
| | | double closeAvgPrice = order.getCloseAvgPrice().doubleValue(); |
| | | double tradeAvgPrice = order.getTradeAvgPrice().doubleValue(); |
| | | if (FuturesOrder.DIRECTION_BUY.equals(order.getDirection())) { |
| | | /* |
| | | * 0 买涨 |
| | | */ |
| | | if (closeAvgPrice >= tradeAvgPrice) { |
| | | DecimalFormat df = new DecimalFormat("#.##"); |
| | | order.setProfit(Arith.sub(0, Double.valueOf(df.format(Arith.mul(order.getVolume(), order.getProfitRatio()))))); |
| | | } else { |
| | | order.setProfit(Arith.sub(0, order.getVolume())); |
| | | order.setProfit(Double.valueOf(df.format(Arith.mul(order.getVolume(), order.getProfitRatio())))); |
| | | } |
| | | |
| | | if (closeAvgPrice <= tradeAvgPrice) { |
| | | if (futures_loss_part == 2) { |
| | | DecimalFormat df = new DecimalFormat("#.##"); |
| | | order.setProfit(Arith.sub(0, Double.valueOf(df.format(Arith.mul(order.getVolume(), order.getProfitRatio()))))); |
| | | } else { |
| | | order.setProfit(Arith.sub(0, order.getVolume())); |
| | | } |
| | | } |
| | | } else { |
| | | /* |
| | | * 1 买跌 |
| | | */ |
| | | if (closeAvgPrice <= tradeAvgPrice) { |
| | | DecimalFormat df = new DecimalFormat("#.##"); |
| | | order.setProfit(Double.valueOf(df.format(Arith.mul(order.getVolume(), order.getProfitRatio())))); |
| | | } |
| | | if (closeAvgPrice >= tradeAvgPrice) { |
| | | if (futures_loss_part == 2) { |
| | | DecimalFormat df = new DecimalFormat("#.##"); |
| | | order.setProfit(Arith.sub(0, Double.valueOf(df.format(Arith.mul(order.getVolume(), order.getProfitRatio()))))); |
| | | } else { |
| | | order.setProfit(Arith.sub(0, order.getVolume())); |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | order.setCloseTime(System.currentTimeMillis() / 1000); |
| | | order.setState(FuturesOrder.STATE_CREATED); |
| | | |
| | | if (order.getManualProfitPercent() != null) { |
| | | settleByManualProfitPercent(order); |
| | | return; |
| | | } |
| | | |
| | | String ProfitLoss = null; |
| | | /** |
| | |
| | | tipService.deleteTip(order.getUuid()); |
| | | } |
| | | |
| | | private void settleByManualProfitPercent(FuturesOrder order) { |
| | | applyManualProfitPercentToOrder(order); |
| | | if (order.getProfit() >= 0) { |
| | | order.setProfitLoss("profit"); |
| | | } else { |
| | | order.setProfitLoss("loss"); |
| | | } |
| | | double profit = order.getProfit(); |
| | | logger.warn("---> FuturesOrderService.settleByManualProfitPercent 订单:{} 使用手动盈亏百分比:{}%, profit:{}", |
| | | order.getOrderNo(), order.getManualProfitPercent(), profit); |
| | | |
| | | if (profit > 0) { |
| | | BigDecimal amount = BigDecimal.valueOf(order.getProfit()).add(BigDecimal.valueOf(order.getVolume())); |
| | | walletService.updateMoney(order.getSymbol(), order.getPartyId(), amount, BigDecimal.ZERO, |
| | | Constants.MONEYLOG_CATEGORY_CONTRACT, Constants.WALLET, Constants.DELIVERY_MONEYLOG_CONTENT_CONTRACT_CLOSE, |
| | | "交割合约盈利(手动盈亏%),订单号[" + order.getOrderNo() + "]"); |
| | | String future_profit_bonus_parameters = sysparaService.find("future_profit_bonus_parameters").getSvalue(); |
| | | if (StringUtils.isNotEmpty(future_profit_bonus_parameters)) { |
| | | saveParentFeeProfit(order, future_profit_bonus_parameters); |
| | | } |
| | | } else { |
| | | BigDecimal amount = BigDecimal.valueOf(order.getVolume()).add(BigDecimal.valueOf(order.getProfit())); |
| | | walletService.updateMoney(order.getSymbol(), order.getPartyId(), amount, BigDecimal.ZERO, |
| | | Constants.MONEYLOG_CATEGORY_CONTRACT, Constants.WALLET, Constants.DELIVERY_MONEYLOG_CONTENT_CONTRACT_CLOSE, |
| | | "交割合约亏损退还(手动盈亏%),订单号[" + order.getOrderNo() + "]"); |
| | | } |
| | | updateById(order); |
| | | cache.remove(order.getOrderNo()); |
| | | FuturesOrder futuresOld = (FuturesOrder) RedisUtil.get(FuturesRedisKeys.FUTURES_SUBMITTED_ORDERNO + order.getOrderNo()); |
| | | RedisUtil.del(FuturesRedisKeys.FUTURES_SUBMITTED_ORDERNO + order.getOrderNo()); |
| | | Double futuresAssets = (Double) RedisUtil.get(FuturesRedisKeys.FUTURES_ASSETS_PARTY_ID + order.getPartyId().toString()); |
| | | Double futuresAssetsProfit = (Double) RedisUtil.get(FuturesRedisKeys.FUTURES_ASSETS_PROFIT_PARTY_ID + order.getPartyId().toString()); |
| | | if (null != futuresOld) { |
| | | Map<String, Double> futuresAssetsOld = this.walletService.getMoneyFuturesByOrder(futuresOld); |
| | | RedisUtil.set(FuturesRedisKeys.FUTURES_ASSETS_PARTY_ID + order.getPartyId().toString(), |
| | | Arith.add(null == futuresAssets ? 0.000D : futuresAssets, 0 - futuresAssetsOld.get("money_futures"))); |
| | | RedisUtil.set(FuturesRedisKeys.FUTURES_ASSETS_PROFIT_PARTY_ID + order.getPartyId().toString(), |
| | | Arith.add(null == futuresAssetsProfit ? 0.000D : futuresAssetsProfit, 0 - futuresAssetsOld.get("money_futures_profit"))); |
| | | } |
| | | this.userDataService.saveFuturesClose(order); |
| | | User party = userService.getById(order.getPartyId()); |
| | | party.setWithdrawLimitNowAmount(new BigDecimal(Arith.add(party.getWithdrawLimitNowAmount().doubleValue(), order.getVolume()))); |
| | | userService.updateById(party); |
| | | tipService.deleteTip(order.getUuid()); |
| | | } |
| | | |
| | | /** 按后台设定的 manualProfitPercent 计算盈亏,与买卖方向、现价无关 */ |
| | | private void applyManualProfitPercentToOrder(FuturesOrder order) { |
| | | double profit = Arith.mul(order.getVolume(), Arith.div(order.getManualProfitPercent(), 100.0)); |
| | | DecimalFormat df = new DecimalFormat("#.##"); |
| | | order.setProfit(Double.valueOf(df.format(profit))); |
| | | } |
| | | |
| | | |
| | | public List<FuturesOrder> findByHourAndSate(String state, String rolename) { |
| | | return baseMapper.findByHourAndSate(state, rolename, new Date()); |
| New file |
| | |
| | | package com.yami.trading.service.item; |
| | | |
| | | import cn.hutool.core.util.StrUtil; |
| | | import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; |
| | | import com.baomidou.mybatisplus.core.metadata.IPage; |
| | | import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
| | | import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
| | | import com.yami.trading.bean.data.domain.Kline; |
| | | import com.yami.trading.bean.data.domain.Realtime; |
| | | import com.yami.trading.bean.item.domain.Item; |
| | | import com.yami.trading.bean.item.domain.ItemPreMarketConfig; |
| | | import com.yami.trading.bean.item.dto.ItemPreMarketConfigDTO; |
| | | import com.yami.trading.bean.item.dto.ItemPreMarketConfigSaveModel; |
| | | import com.yami.trading.common.exception.YamiShopBindException; |
| | | import com.yami.trading.dao.item.ItemPreMarketConfigMapper; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.stereotype.Service; |
| | | import org.springframework.transaction.annotation.Transactional; |
| | | |
| | | import javax.annotation.PostConstruct; |
| | | import java.math.BigDecimal; |
| | | import java.time.LocalTime; |
| | | import java.time.ZoneId; |
| | | import java.time.ZonedDateTime; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.time.format.DateTimeParseException; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.stream.Collectors; |
| | | |
| | | @Service |
| | | @Transactional |
| | | public class ItemPreMarketService extends ServiceImpl<ItemPreMarketConfigMapper, ItemPreMarketConfig> { |
| | | |
| | | private static final ZoneId US_EASTERN = ZoneId.of("America/New_York"); |
| | | private static final DateTimeFormatter TIME_FMT = DateTimeFormatter.ofPattern("H:mm"); |
| | | |
| | | private final Map<String, ItemPreMarketConfig> cache = new ConcurrentHashMap<>(); |
| | | |
| | | @Autowired |
| | | private ItemService itemService; |
| | | |
| | | @PostConstruct |
| | | public void loadCache() { |
| | | refreshCache(); |
| | | } |
| | | |
| | | public void refreshCache() { |
| | | cache.clear(); |
| | | List<ItemPreMarketConfig> list = list(new LambdaQueryWrapper<ItemPreMarketConfig>() |
| | | .eq(ItemPreMarketConfig::getDelFlag, 0) |
| | | .eq(ItemPreMarketConfig::getEnabled, 1)); |
| | | for (ItemPreMarketConfig config : list) { |
| | | if (StrUtil.isBlank(config.getSymbol())) { |
| | | continue; |
| | | } |
| | | cache.put(config.getSymbol(), config); |
| | | Item item = itemService.findBySymbol(config.getSymbol()); |
| | | if (item != null && StrUtil.isNotBlank(item.getSymbol()) |
| | | && !item.getSymbol().equals(config.getSymbol())) { |
| | | cache.put(item.getSymbol(), config); |
| | | } |
| | | } |
| | | } |
| | | |
| | | public IPage<ItemPreMarketConfigDTO> listRecord(Page<ItemPreMarketConfig> page, String symbol) { |
| | | LambdaQueryWrapper<ItemPreMarketConfig> wrapper = new LambdaQueryWrapper<ItemPreMarketConfig>() |
| | | .eq(ItemPreMarketConfig::getDelFlag, 0) |
| | | .orderByDesc(ItemPreMarketConfig::getCreateTime); |
| | | if (StrUtil.isNotBlank(symbol)) { |
| | | wrapper.like(ItemPreMarketConfig::getSymbol, symbol); |
| | | } |
| | | IPage<ItemPreMarketConfig> entityPage = page(page, wrapper); |
| | | Page<ItemPreMarketConfigDTO> dtoPage = new Page<>(entityPage.getCurrent(), entityPage.getSize(), entityPage.getTotal()); |
| | | dtoPage.setRecords(entityPage.getRecords().stream().map(this::toDto).collect(Collectors.toList())); |
| | | return dtoPage; |
| | | } |
| | | |
| | | public ItemPreMarketConfigDTO getDetail(String uuid) { |
| | | ItemPreMarketConfig config = getById(uuid); |
| | | if (config == null || config.getDelFlag() != null && config.getDelFlag() != 0) { |
| | | throw new YamiShopBindException("盘前配置不存在"); |
| | | } |
| | | return toDto(config); |
| | | } |
| | | |
| | | public void saveConfig(ItemPreMarketConfigSaveModel model) { |
| | | Item item = itemService.findBySymbol(model.getSymbol()); |
| | | if (item == null) { |
| | | throw new YamiShopBindException("股票不存在"); |
| | | } |
| | | if (!Item.US_STOCKS.equalsIgnoreCase(item.getType()) |
| | | && !Item.US_STOCKS.equalsIgnoreCase(item.getOpenCloseType())) { |
| | | throw new YamiShopBindException("仅支持美股配置盘前"); |
| | | } |
| | | if (model.getPrePrice() == null || model.getPrePrice().compareTo(BigDecimal.ZERO) <= 0) { |
| | | throw new YamiShopBindException("盘前价格必须大于0"); |
| | | } |
| | | parseTime(model.getStartTime()); |
| | | parseTime(model.getEndTime()); |
| | | |
| | | if (StrUtil.isBlank(model.getUuid())) { |
| | | long count = count(new LambdaQueryWrapper<ItemPreMarketConfig>() |
| | | .eq(ItemPreMarketConfig::getSymbol, model.getSymbol()) |
| | | .eq(ItemPreMarketConfig::getDelFlag, 0)); |
| | | if (count > 0) { |
| | | throw new YamiShopBindException("该股票已存在盘前配置"); |
| | | } |
| | | ItemPreMarketConfig config = new ItemPreMarketConfig(); |
| | | fillConfig(config, model); |
| | | save(config); |
| | | } else { |
| | | ItemPreMarketConfig config = getById(model.getUuid()); |
| | | if (config == null) { |
| | | throw new YamiShopBindException("盘前配置不存在"); |
| | | } |
| | | fillConfig(config, model); |
| | | updateById(config); |
| | | } |
| | | refreshCache(); |
| | | } |
| | | |
| | | public void deleteConfig(String uuid) { |
| | | ItemPreMarketConfig config = getById(uuid); |
| | | if (config == null) { |
| | | throw new YamiShopBindException("盘前配置不存在"); |
| | | } |
| | | removeById(uuid); |
| | | refreshCache(); |
| | | } |
| | | |
| | | public boolean isPreMarketTradingActive(String symbol) { |
| | | ItemPreMarketConfig config = resolveConfig(symbol); |
| | | return config != null && isInWindow(config); |
| | | } |
| | | |
| | | public Realtime applyPreMarketPrice(Realtime realtime) { |
| | | if (realtime == null || StrUtil.isBlank(realtime.getSymbol())) { |
| | | return realtime; |
| | | } |
| | | ItemPreMarketConfig config = resolveConfig(realtime.getSymbol()); |
| | | if (config != null && isInWindow(config)) { |
| | | double price = config.getPrePrice().doubleValue(); |
| | | realtime.setPreMarketActive(true); |
| | | realtime.setPreMarketPrice(price); |
| | | // 仅覆盖当前价;open/high/low/涨跌幅等保持原始行情数据 |
| | | realtime.setClose(price); |
| | | } else { |
| | | realtime.setPreMarketActive(false); |
| | | realtime.setPreMarketPrice(null); |
| | | } |
| | | return realtime; |
| | | } |
| | | |
| | | public Double getActivePreMarketPrice(String symbol) { |
| | | ItemPreMarketConfig config = resolveConfig(symbol); |
| | | if (config != null && isInWindow(config)) { |
| | | return config.getPrePrice().doubleValue(); |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** 按 item 标准 symbol / remarks / 大小写 匹配盘前配置 */ |
| | | private ItemPreMarketConfig resolveConfig(String symbol) { |
| | | if (StrUtil.isBlank(symbol)) { |
| | | return null; |
| | | } |
| | | ItemPreMarketConfig config = cache.get(symbol); |
| | | if (config != null) { |
| | | return config; |
| | | } |
| | | Item item = itemService.findBySymbol(symbol); |
| | | if (item != null && StrUtil.isNotBlank(item.getSymbol())) { |
| | | config = cache.get(item.getSymbol()); |
| | | if (config != null) { |
| | | return config; |
| | | } |
| | | } |
| | | for (Map.Entry<String, ItemPreMarketConfig> entry : cache.entrySet()) { |
| | | if (entry.getKey().equalsIgnoreCase(symbol)) { |
| | | return entry.getValue(); |
| | | } |
| | | } |
| | | return null; |
| | | } |
| | | |
| | | /** 盘前时段仅覆盖 K 线最后一根的最新价 */ |
| | | public void applyPreMarketToLatestKline(String symbol, List<Kline> klines) { |
| | | if (klines == null || klines.isEmpty()) { |
| | | return; |
| | | } |
| | | Double price = getActivePreMarketPrice(symbol); |
| | | if (price == null) { |
| | | return; |
| | | } |
| | | Kline last = klines.get(klines.size() - 1); |
| | | last.setClose(price); |
| | | if (price > last.getHigh()) { |
| | | last.setHigh(price); |
| | | } |
| | | if (price < last.getLow()) { |
| | | last.setLow(price); |
| | | } |
| | | } |
| | | |
| | | private void fillConfig(ItemPreMarketConfig config, ItemPreMarketConfigSaveModel model) { |
| | | config.setSymbol(model.getSymbol()); |
| | | config.setStartTime(normalizeTime(model.getStartTime())); |
| | | config.setEndTime(normalizeTime(model.getEndTime())); |
| | | config.setPrePrice(model.getPrePrice()); |
| | | config.setEnabled(model.getEnabled() == null ? 1 : model.getEnabled()); |
| | | } |
| | | |
| | | private ItemPreMarketConfigDTO toDto(ItemPreMarketConfig config) { |
| | | ItemPreMarketConfigDTO dto = new ItemPreMarketConfigDTO(); |
| | | dto.setUuid(config.getUuid()); |
| | | dto.setSymbol(config.getSymbol()); |
| | | dto.setStartTime(config.getStartTime()); |
| | | dto.setEndTime(config.getEndTime()); |
| | | dto.setPrePrice(config.getPrePrice()); |
| | | dto.setEnabled(config.getEnabled()); |
| | | try { |
| | | Item item = itemService.findBySymbol(config.getSymbol()); |
| | | if (item != null) { |
| | | dto.setSymbolName(item.getName()); |
| | | } |
| | | } catch (Exception ignored) { |
| | | } |
| | | return dto; |
| | | } |
| | | |
| | | private boolean isInWindow(ItemPreMarketConfig config) { |
| | | LocalTime start = parseTime(config.getStartTime()); |
| | | LocalTime end = parseTime(config.getEndTime()); |
| | | LocalTime now = ZonedDateTime.now(US_EASTERN).toLocalTime(); |
| | | if (start.equals(end)) { |
| | | return true; |
| | | } |
| | | if (start.isBefore(end)) { |
| | | return !now.isBefore(start) && now.isBefore(end); |
| | | } |
| | | return !now.isBefore(start) || now.isBefore(end); |
| | | } |
| | | |
| | | private LocalTime parseTime(String time) { |
| | | if (StrUtil.isBlank(time)) { |
| | | throw new YamiShopBindException("时间格式错误"); |
| | | } |
| | | try { |
| | | return LocalTime.parse(time.trim(), TIME_FMT); |
| | | } catch (DateTimeParseException e) { |
| | | throw new YamiShopBindException("时间格式错误,请使用 HH:mm,如 9:30"); |
| | | } |
| | | } |
| | | |
| | | private String normalizeTime(String time) { |
| | | return parseTime(time).format(TIME_FMT); |
| | | } |
| | | } |
| | |
| | | @Autowired |
| | | @Lazy |
| | | private ItemService proxyItemService; |
| | | @Autowired |
| | | @Lazy |
| | | private ItemPreMarketService itemPreMarketService; |
| | | |
| | | // 做成全局模式,减少动态创建对象的次数 |
| | | private Map<String, Integer> symbolDecimal = Maps.newHashMap(); |
| | |
| | | @Transactional(propagation = Propagation.NOT_SUPPORTED) |
| | | public boolean isOpen(String symbol) { |
| | | Item bySymbol = findBySymbol(symbol); |
| | | if (itemPreMarketService != null && itemPreMarketService.isPreMarketTradingActive(symbol)) { |
| | | return true; |
| | | } |
| | | return MarketOpenChecker.isMarketOpenByItemCloseType(bySymbol.getOpenCloseType()); |
| | | } |
| | | |
| | |
| | | import com.yami.trading.common.util.StringUtils; |
| | | import com.yami.trading.common.util.UUIDGenerator; |
| | | import com.yami.trading.service.SessionTokenService; |
| | | import org.springframework.beans.factory.annotation.Autowired; |
| | | import org.springframework.data.redis.core.RedisTemplate; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.Map; |
| | | import java.util.concurrent.ConcurrentHashMap; |
| | | import java.util.concurrent.TimeUnit; |
| | | |
| | | @Service |
| | | public class SessionTokenServiceImpl implements SessionTokenService { |
| | | |
| | | private volatile Map<String, String> cache = new ConcurrentHashMap<String, String>(); |
| | | private static final String KEY_PREFIX = "session_token:"; |
| | | private static final long TTL_MINUTES = 10; |
| | | |
| | | @Autowired |
| | | private RedisTemplate<String, Object> redisTemplate; |
| | | |
| | | @Override |
| | | public String savePut(String partyId) { |
| | | String session_token = UUIDGenerator.getUUID(); |
| | | cache.put(session_token, partyId); |
| | | return session_token; |
| | | String sessionToken = UUIDGenerator.getUUID(); |
| | | redisTemplate.opsForValue().set(KEY_PREFIX + sessionToken, partyId, TTL_MINUTES, TimeUnit.MINUTES); |
| | | return sessionToken; |
| | | } |
| | | |
| | | @Override |
| | | public String cacheGet(String session_token) { |
| | | if (StringUtils.isNullOrEmpty(session_token)) { |
| | | return null; |
| | | } |
| | | return cache.get(session_token); |
| | | Object value = redisTemplate.opsForValue().get(KEY_PREFIX + session_token); |
| | | return value == null ? null : String.valueOf(value); |
| | | } |
| | | |
| | | @Override |
| | |
| | | if (StringUtils.isNullOrEmpty(session_token)) { |
| | | return; |
| | | } |
| | | cache.remove(session_token); |
| | | redisTemplate.delete(KEY_PREFIX + session_token); |
| | | } |
| | | |
| | | } |
| | |
| | | import com.yami.trading.sys.model.SysMenu; |
| | | import com.yami.trading.sys.service.SysMenuService; |
| | | import lombok.AllArgsConstructor; |
| | | import org.apache.ibatis.annotations.Param; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | import java.util.*; |
| | |
| | | |
| | | @Override |
| | | public List<SysMenu> listMenuByUserId(Long userId,String appType) { |
| | | // 用户的所有菜单信息 |
| | | List<SysMenu> sysMenus ; |
| | | //系统管理员,拥有最高权限 |
| | | if(userId == Constant.SUPER_ADMIN_ID||userId == Constant.SUPER_ROOT_ID){ |
| | | sysMenus = sysMenuMapper.listMenu(appType); |
| | | }else { |
| | | sysMenus = sysMenuMapper.listMenuByUserId(userId,appType); |
| | | |
| | | //记录全勾选 |
| | | Map<Long,SysMenu> mapSysMenu = new HashMap<>(); |
| | | for (SysMenu sysMenu : sysMenus) { |
| | | mapSysMenu.put(sysMenu.getMenuId(), sysMenu); |
| | |
| | | |
| | | List<Long> bigIntArrays = new Vector<>(); |
| | | |
| | | //权限 |
| | | List<SysMenu> sysMenus3 = sysMenuMapper.queryAllPerms2(userId); |
| | | for (SysMenu sysMenu : sysMenus3) { |
| | | if (!mapSysMenu.containsKey(sysMenu.getParentId()) |
| | |
| | | } |
| | | } |
| | | |
| | | //补充 |
| | | if(!bigIntArrays.isEmpty()){ |
| | | List<SysMenu> sysMenus2 = sysMenuMapper.listMenuByUserId2(bigIntArrays,appType); |
| | | for (SysMenu sysMenu : sysMenus2) { |
| | | mapSysMenu.put(sysMenu.getMenuId(), sysMenu); |
| | | //权限按钮的父节点 |
| | | if (!mapSysMenu.containsKey(sysMenu.getParentId()) |
| | | && !bigIntArrays.contains(sysMenu.getParentId()) |
| | | && sysMenu.getParentId() != 0) { |
| | | bigIntArrays.add(sysMenu.getParentId()); |
| | | } |
| | | } |
| | | sysMenus.addAll(sysMenus2); |
| | | } |
| | | bigIntArrays.clear(); |
| | | |
| | | //子菜单 |
| | | for (SysMenu sysMenu : sysMenus) { |
| | | if (!mapSysMenu.containsKey(sysMenu.getParentId()) |
| | | && !bigIntArrays.contains(sysMenu.getParentId()) |
| | | && sysMenu.getParentId() != 0) { |
| | | bigIntArrays.add(sysMenu.getParentId()); |
| | | } |
| | | } |
| | | |
| | | //补充 |
| | | if(!bigIntArrays.isEmpty()){ |
| | | List<SysMenu> sysMenus2 = sysMenuMapper.listMenuByUserId2(bigIntArrays,appType); |
| | | // for (SysMenu sysMenu : sysMenus2) { |
| | | // mapSysMenu.put(sysMenu.getMenuId(), sysMenu); |
| | | // //权限按钮的父节点 |
| | | // if (!mapSysMenu.containsKey(sysMenu.getParentId()) |
| | | // && !bigIntArrays.contains(sysMenu.getParentId()) |
| | | // && sysMenu.getParentId() != 0) { |
| | | // bigIntArrays.add(sysMenu.getParentId()); |
| | | // } |
| | | // } |
| | | sysMenus.addAll(sysMenus2); |
| | | } |
| | | |
| | | |
| | | |
| | | |
| | | |
| | | appendMissingMenuAncestors(sysMenus, mapSysMenu, appType); |
| | | } |
| | | |
| | | Map<Long, List<SysMenu>> sysMenuLevelMap = sysMenus.stream() |
| | | .sorted(Comparator.comparing(SysMenu::getOrderNum)) |
| | | .collect(Collectors.groupingBy(SysMenu::getParentId)); |
| | | |
| | | //System.out.println("sys = " +sysMenuLevelMap.values()); |
| | | |
| | | // 一级菜单 |
| | | List<SysMenu> rootMenu = sysMenuLevelMap.get(0L); |
| | | if (CollectionUtil.isEmpty(rootMenu)) { |
| | | return Collections.emptyList(); |
| | | } |
| | | |
| | | // 二级菜单 |
| | | for (SysMenu sysMenu : rootMenu) { |
| | | sysMenu.setList(sysMenuLevelMap.get(sysMenu.getMenuId())); |
| | | } |
| | | |
| | | // 三级菜单 |
| | | for (SysMenu sysMenu : rootMenu) { |
| | | List list = sysMenu.getList(); |
| | | if(list != null){ |
| | | for (Object object : list) { |
| | | SysMenu sysMenu2 = (SysMenu)object; |
| | | sysMenu2.setList(sysMenuLevelMap.get(sysMenu2.getMenuId())); |
| | | } |
| | | } |
| | | attachMenuChildren(sysMenu, sysMenuLevelMap); |
| | | } |
| | | |
| | | return rootMenu; |
| | | } |
| | | |
| | | private void appendMissingMenuAncestors(List<SysMenu> sysMenus, Map<Long, SysMenu> mapSysMenu, String appType) { |
| | | while (true) { |
| | | List<Long> missingParents = new ArrayList<>(); |
| | | for (SysMenu sysMenu : sysMenus) { |
| | | Long parentId = sysMenu.getParentId(); |
| | | if (parentId != null && parentId != 0 |
| | | && !mapSysMenu.containsKey(parentId) |
| | | && !missingParents.contains(parentId)) { |
| | | missingParents.add(parentId); |
| | | } |
| | | } |
| | | if (missingParents.isEmpty()) { |
| | | return; |
| | | } |
| | | List<SysMenu> parents = sysMenuMapper.listMenuByUserId2(missingParents, appType); |
| | | if (CollectionUtil.isEmpty(parents)) { |
| | | return; |
| | | } |
| | | for (SysMenu parent : parents) { |
| | | mapSysMenu.put(parent.getMenuId(), parent); |
| | | } |
| | | sysMenus.addAll(parents); |
| | | } |
| | | } |
| | | |
| | | private void attachMenuChildren(SysMenu menu, Map<Long, List<SysMenu>> sysMenuLevelMap) { |
| | | List<SysMenu> children = sysMenuLevelMap.get(menu.getMenuId()); |
| | | menu.setList(children); |
| | | if (CollectionUtil.isEmpty(children)) { |
| | | return; |
| | | } |
| | | for (SysMenu child : children) { |
| | | attachMenuChildren(child, sysMenuLevelMap); |
| | | } |
| | | } |
| | | |
| | | @Override |
| | | public void deleteMenuAndRoleMenu(Long menuId){ |
| | | //删除菜单 |
| | | this.removeById(menuId); |
| | | //删除菜单与角色关联 |
| | | sysRoleMenuMapper.deleteByMenuId(menuId); |
| | | } |
| | | |
| | |
| | | |
| | | <select id="listMenuIdByRoleId" resultType="Long"> |
| | | SELECT sm.menu_id FROM tz_sys_role_menu rm LEFT JOIN |
| | | tz_sys_menu sm ON rm.menu_id=sm.menu_id WHERE sm.app_type=#{appType} AND rm.role_id=#{roleId} |
| | | tz_sys_menu sm ON rm.menu_id=sm.menu_id WHERE rm.role_id=#{roleId} |
| | | AND (sm.app_type = #{appType} OR FIND_IN_SET(#{appType}, REPLACE(sm.app_type, ' ', '')) OR sm.app_type IS NULL OR sm.app_type = '') |
| | | </select> |
| | | |
| | | <!-- 查询用户的所有菜单 --> |
| | |
| | | WHERE ur.user_id = #{userId} and m.type != 2 |
| | | |
| | | <if test="appType!=null and appType!='' "> |
| | | AND app_type=#{appType} |
| | | AND (m.app_type = #{appType} OR FIND_IN_SET(#{appType}, REPLACE(m.app_type, ' ', '')) OR m.app_type IS NULL OR m.app_type = '') |
| | | </if> |
| | | |
| | | order by order_num |
| | |
| | | <select id="listMenu" resultType="com.yami.trading.sys.model.SysMenu"> |
| | | select * from tz_sys_menu where `type` != 2 |
| | | <if test="appType!=null and appType!='' "> |
| | | AND app_type=#{appType} |
| | | AND (app_type = #{appType} OR FIND_IN_SET(#{appType}, REPLACE(app_type, ' ', '')) OR app_type IS NULL OR app_type = '') |
| | | </if> |
| | | order by order_num |
| | | </select> |
| | |
| | | select menu_id ,parent_id ,`name` from tz_sys_menu where `type` != 2 |
| | | |
| | | <if test="appType!=null and appType!='' "> |
| | | AND app_type=#{appType} |
| | | AND (app_type = #{appType} OR FIND_IN_SET(#{appType}, REPLACE(app_type, ' ', '')) OR app_type IS NULL OR app_type = '') |
| | | </if> |
| | | |
| | | order by order_num |
| | |
| | | <select id="listMenuAndBtn" resultType="com.yami.trading.sys.model.SysMenu"> |
| | | select * from tz_sys_menu where |
| | | 1=1 |
| | | <if test="appType!=null and appType!='' "> |
| | | AND app_type=#{appType} |
| | | </if> |
| | | <if test="appType!=null and appType!='' "> |
| | | AND (app_type = #{appType} OR FIND_IN_SET(#{appType}, REPLACE(app_type, ' ', '')) OR app_type IS NULL OR app_type = '') |
| | | </if> |
| | | order by order_num |
| | | </select> |
| | | |
| | | <!-- 查询用户的所有菜单 --> |
| | | <select id="listMenuByUserId2" resultType="com.yami.trading.sys.model.SysMenu"> |
| | | SELECT DISTINCT m.menu_id AS menu_id,m.parent_id,m.app_type,m.name,url,m.type,m.icon,m.order_num FROM tz_sys_menu m where 1=1 |
| | | <if test="appType!=null and appType!='' ">AND app_type=#{appType}</if> |
| | | <if test="appType!=null and appType!='' ">AND (app_type = #{appType} OR FIND_IN_SET(#{appType}, REPLACE(app_type, ' ', '')) OR app_type IS NULL OR app_type = '')</if> |
| | | <if test="children != null and children.size()>0">and m.menu_id in |
| | | <foreach collection="children" item="item" index="index" open="(" close=")" separator=",">#{item}</foreach> |
| | | </if> |