Merge remote-tracking branch 'origin/main'

# Conflicts:
#	ruoyi-ui/src/views/market/whole/index.vue
This commit is contained in:
Lenovo 2024-07-15 18:40:21 +08:00
commit 49cb9c82ba
23 changed files with 519 additions and 169 deletions

View File

@ -66,10 +66,9 @@ public class BusAgentInfoController extends BaseController {
public TableDataInfo list(BusAgentInfo busAgentInfo) { public TableDataInfo list(BusAgentInfo busAgentInfo) {
startPage(); startPage();
LoginUser user = SecurityUtils.getLoginUser(); LoginUser user = SecurityUtils.getLoginUser();
// if(!"admin".equals(user.getUsername())){ if(!"admin".equals(user.getUsername())){
// busAgentInfo.setSuperiorAgentCode(user.getUserId());
// }
busAgentInfo.setSuperiorAgentCode(user.getUserId()); busAgentInfo.setSuperiorAgentCode(user.getUserId());
}
List<BusAgentInfo> list = busAgentInfoService.selectBusAgentInfoList(busAgentInfo); List<BusAgentInfo> list = busAgentInfoService.selectBusAgentInfoList(busAgentInfo);
List<BusAgentInfo> listData = list.stream().map(bean -> { List<BusAgentInfo> listData = list.stream().map(bean -> {
BusStoreInfo busStoreInfo = new BusStoreInfo(); BusStoreInfo busStoreInfo = new BusStoreInfo();

View File

@ -109,7 +109,7 @@ public class BusStoreInfoController extends BaseController {
BusStoreInfo busStoreInfo = new BusStoreInfo(); BusStoreInfo busStoreInfo = new BusStoreInfo();
BeanUtils.copyProperties(reqBusStoreInfo, busStoreInfo); BeanUtils.copyProperties(reqBusStoreInfo, busStoreInfo);
LoginUser user = SecurityUtils.getLoginUser(); LoginUser user = SecurityUtils.getLoginUser();
if (!"OK777".equals(user.getUsername())||!"OK999".equals(user.getUsername())) { if (!"OK777".equals(user.getUsername())) {
busStoreInfo.setBindUser(user.getUsername()); busStoreInfo.setBindUser(user.getUsername());
} }
List<BusStoreInfo> list = busStoreInfoService.selectBusStoreInfoList(busStoreInfo); List<BusStoreInfo> list = busStoreInfoService.selectBusStoreInfoList(busStoreInfo);

View File

@ -62,6 +62,10 @@ public class ReqBusStoreConfigInfo {
@JsonFormat(shape = JsonFormat.Shape.STRING) @JsonFormat(shape = JsonFormat.Shape.STRING)
private int selfDeliveryDuration; private int selfDeliveryDuration;
/** 营销限额 */
@ApiModelProperty(name = "营销限额开关")
private String quotaStatus;
/** 营销限额 */ /** 营销限额 */
@ApiModelProperty(name = "营销限额") @ApiModelProperty(name = "营销限额")
@JsonFormat(shape = JsonFormat.Shape.STRING) @JsonFormat(shape = JsonFormat.Shape.STRING)

View File

@ -1,28 +1,62 @@
package com.ruoyi.web.controller.common; package com.ruoyi.web.controller.common;
import com.ruoyi.common.constant.CacheConstants;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.R; import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.sign.Base64;
import com.ruoyi.common.utils.uuid.IdUtils;
import com.ruoyi.system.service.ISmsService; import com.ruoyi.system.service.ISmsService;
import com.ruoyi.system.service.ISysConfigService;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation; import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@Api("短信模块") @Api("短信模块")
@RestController @RestController
@RequestMapping("/sms")
public class SmsController { public class SmsController {
/**
* 短信验证码长度
*/
private final Integer LENGTH = 6;
@Autowired
private RedisCache redisCache;
@Autowired
private ISysConfigService configService;
@Autowired @Autowired
private ISmsService iSmsService; private ISmsService iSmsService;
@PostMapping("sendSms") @PostMapping("sendSms")
@ApiOperation("短信发送接口") @ApiOperation("短信发送接口")
public R sendSms(@RequestPart String phone) { public AjaxResult sendSms(@RequestPart String phone) {
iSmsService.sendSmsOne(phone); AjaxResult ajax = AjaxResult.success();
return R.ok(); boolean captchaEnabled = configService.selectCaptchaEnabled();
ajax.put("captchaEnabled", captchaEnabled);
if (!captchaEnabled)
{
return ajax;
}
// 保存验证码信息
String uuid = IdUtils.simpleUUID();
String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + uuid;
String smsCode = generateCode();
redisCache.setCacheObject(verifyKey, smsCode, Constants.CAPTCHA_EXPIRATION, TimeUnit.MINUTES);
iSmsService.sendSmsOne(phone, smsCode);
ajax.put("uuid", uuid);
return ajax;
} }
@PostMapping("validationCode") @PostMapping("validationCode")
@ -32,4 +66,13 @@ public class SmsController {
return R.ok(iSmsService.validationCode(phone, code)); return R.ok(iSmsService.validationCode(phone, code));
} }
/**
* 生成随机的验证码
*
* @return
*/
public String generateCode() {
return RandomStringUtils.randomNumeric(LENGTH);
}
} }

View File

@ -51,6 +51,17 @@ public class SysLoginController
return ajax; return ajax;
} }
@PostMapping("/loginSms")
public AjaxResult loginSms(@RequestBody LoginBody loginBody)
{
AjaxResult ajax = AjaxResult.success();
// 生成令牌
String token = loginService.loginSms(loginBody.getUsername(), loginBody.getCode(),
loginBody.getUuid());
ajax.put(Constants.TOKEN, token);
return ajax;
}
/** /**
* 获取用户信息 * 获取用户信息
* *

View File

@ -42,7 +42,7 @@ public class TestController extends BaseController
@ApiOperation("短信测试接口") @ApiOperation("短信测试接口")
public R sendSMS() { public R sendSMS() {
String phone = "17612400322"; String phone = "17612400322";
iSmsService.sendSmsOne(phone); iSmsService.sendSmsOne(phone, "123456");
return R.ok(); return R.ok();
} }

View File

@ -132,7 +132,7 @@ tencent:
cloud: cloud:
secretId: AKID48T2eHtniosmm0vz59CPZyUgzIlNGKV2 secretId: AKID48T2eHtniosmm0vz59CPZyUgzIlNGKV2
secretKey: bJuNk3B7tW1QqEXjg5faULJBwwpgMo8y secretKey: bJuNk3B7tW1QqEXjg5faULJBwwpgMo8y
signName: 优势管家 signName: 优势管家
templateId: 2200612 templateId: 2200612
smsSdkAppId: 1400921153 smsSdkAppId: 1400921153
region: ap-beijing region: ap-beijing

View File

@ -4,6 +4,7 @@ user.jcaptcha.error=验证码错误
user.jcaptcha.expire=验证码已失效 user.jcaptcha.expire=验证码已失效
user.not.exists=用户不存在/密码错误 user.not.exists=用户不存在/密码错误
user.password.not.match=用户不存在/密码错误 user.password.not.match=用户不存在/密码错误
phone.code.not.match=手机短信验证码错误
user.password.retry.limit.count=密码输入错误{0}次 user.password.retry.limit.count=密码输入错误{0}次
user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟 user.password.retry.limit.exceed=密码输入错误{0}次,帐户锁定{1}分钟
user.password.delete=对不起,您的账号已被删除 user.password.delete=对不起,您的账号已被删除

View File

@ -21,6 +21,33 @@ public class LoginBody
* 验证码 * 验证码
*/ */
private String code; private String code;
private String phone;
private String smsCode;
private String loginType;
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getSmsCode() {
return smsCode;
}
public void setSmsCode(String smsCode) {
this.smsCode = smsCode;
}
public String getLoginType() {
return loginType;
}
public void setLoginType(String loginType) {
this.loginType = loginType;
}
/** /**
* 唯一标识 * 唯一标识

View File

@ -111,11 +111,12 @@ public class SecurityConfig
.authorizeHttpRequests((requests) -> { .authorizeHttpRequests((requests) -> {
permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll()); permitAllUrl.getUrls().forEach(url -> requests.antMatchers(url).permitAll());
// 对于登录login 注册register 验证码captchaImage 允许匿名访问 // 对于登录login 注册register 验证码captchaImage 允许匿名访问
requests.antMatchers("/login", "/register", "/captchaImage","/sms").permitAll() requests.antMatchers("/login", "/register", "/captchaImage","/sendSms","/loginSms").permitAll()
// 静态资源可匿名访问 // 静态资源可匿名访问
.antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll() .antMatchers(HttpMethod.GET, "/", "/*.html", "/**/*.html", "/**/*.css", "/**/*.js", "/profile/**").permitAll()
.antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll() .antMatchers("/swagger-ui.html", "/swagger-resources/**", "/webjars/**", "/*/api-docs", "/druid/**").permitAll()
.antMatchers("/mt/cookies").permitAll() .antMatchers("/mt/cookies").permitAll()
.antMatchers("/test/**").permitAll()
// 除上面外的所有请求全部需要鉴权认证 // 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated(); .anyRequest().authenticated();

View File

@ -0,0 +1,39 @@
package com.ruoyi.framework.security.context;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.util.Assert;
import java.util.Collection;
public class SmsCodeAuthenticationToken extends AbstractAuthenticationToken {
private final Object principal;
public SmsCodeAuthenticationToken(Object principal) {
super((Collection)null);
this.principal = principal;
this.setAuthenticated(false);
}
public SmsCodeAuthenticationToken(Object principal, Collection<? extends GrantedAuthority> authorities) {
super(authorities);
this.principal = principal;
super.setAuthenticated(true);
}
public Object getCredentials() {
return null;
}
public Object getPrincipal() {
return this.principal;
}
public void setAuthenticated(boolean isAuthenticated) throws IllegalArgumentException {
Assert.isTrue(!isAuthenticated, "Cannot set this token to trusted - use constructor which takes a GrantedAuthority list instead");
super.setAuthenticated(false);
}
}

View File

@ -1,6 +1,8 @@
package com.ruoyi.framework.web.service; package com.ruoyi.framework.web.service;
import javax.annotation.Resource; import javax.annotation.Resource;
import com.ruoyi.framework.security.context.SmsCodeAuthenticationToken;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException; import org.springframework.security.authentication.BadCredentialsException;
@ -100,6 +102,50 @@ public class SysLoginService
return tokenService.createToken(loginUser); return tokenService.createToken(loginUser);
} }
/**
* 登录验证
*
* @param phone 用户名
* @param smsCode 短信验证码
* @param uuid 唯一标识
* @return 结果
*/
public String loginSms(String phone, String smsCode, String uuid)
{
// 验证码校验
validateCaptcha(phone, smsCode, uuid);
// 用户验证
Authentication authentication = null;
try
{
SmsCodeAuthenticationToken authenticationToken = new SmsCodeAuthenticationToken(phone);
AuthenticationContextHolder.setContext(authenticationToken);
// 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
authentication = authenticationManager.authenticate(authenticationToken);
}
catch (Exception e)
{
if (e instanceof BadCredentialsException)
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(phone, Constants.LOGIN_FAIL, MessageUtils.message("phone.code.not.match")));
throw new UserPasswordNotMatchException();
}
else
{
AsyncManager.me().execute(AsyncFactory.recordLogininfor(phone, Constants.LOGIN_FAIL, e.getMessage()));
throw new ServiceException(e.getMessage());
}
}
finally
{
AuthenticationContextHolder.clearContext();
}
AsyncManager.me().execute(AsyncFactory.recordLogininfor(phone, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
LoginUser loginUser = (LoginUser) authentication.getPrincipal();
recordLoginInfo(loginUser.getUserId());
// 生成token
return tokenService.createToken(loginUser);
}
/** /**
* 校验验证码 * 校验验证码
* *

View File

@ -1,51 +1,51 @@
package com.ruoyi.quartz.task; //package com.ruoyi.quartz.task;
//
import com.ruoyi.business.service.IAiService; //import com.ruoyi.business.service.IAiService;
import com.ruoyi.business.service.IMeituanService; //import com.ruoyi.business.service.IMeituanService;
import org.springframework.beans.factory.annotation.Autowired; //import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; //import org.springframework.stereotype.Component;
//
/** ///**
* 定时任务调度测试 // * 定时任务调度测试
* // *
* @author ruoyi // * @author ruoyi
*/ // */
@Component("ryTask") //@Component("ryTask")
public class RyTask { //public class RyTask {
@Autowired // @Autowired
private IAiService aiService; // private IAiService aiService;
@Autowired // @Autowired
private IMeituanService iMeituanService; // private IMeituanService iMeituanService;
//
/** // /**
* 获取订单并解析号码 5分钟 // * 获取订单并解析号码 5分钟
*/ // */
public void getOrders() { // public void getOrders() {
iMeituanService.orderInfoList(); // iMeituanService.orderInfoList();
} // }
//
/** // /**
* 发送AI 1分钟 // * 发送AI 1分钟
* // *
* @throws Exception // * @throws Exception
*/ // */
public void sendToAi() throws Exception { // public void sendToAi() throws Exception {
aiService.sendToAi(); // aiService.sendToAi();
} // }
//
/** // /**
* 获取AI结果 5分钟 // * 获取AI结果 5分钟
*/ // */
public void getAiResult() { // public void getAiResult() {
aiService.queryAiTask(); // aiService.queryAiTask();
} // }
//
/** // /**
* 统计昨日营销数 每天8点执行一次 // * 统计昨日营销数 每天8点执行一次
*/ // */
public void getReturnInfo() { // public void getReturnInfo() {
iMeituanService.getReturnInfo(); // iMeituanService.getReturnInfo();
//
} // }
//
} //}

View File

@ -70,6 +70,10 @@ public class BusStoreConfigInfo extends BaseEntity
@Excel(name = "自配送营销时长") @Excel(name = "自配送营销时长")
private int selfDeliveryDuration; private int selfDeliveryDuration;
/** 营销限额 */
@Excel(name = "营销限额开关")
private String quotaStatus;
/** 营销限额 */ /** 营销限额 */
@Excel(name = "营销限额") @Excel(name = "营销限额")
private int quota; private int quota;
@ -80,6 +84,14 @@ public class BusStoreConfigInfo extends BaseEntity
@Excel(name = "归属销售id") @Excel(name = "归属销售id")
private Long saleBindId; private Long saleBindId;
public String getQuotaStatus() {
return quotaStatus;
}
public void setQuotaStatus(String quotaStatus) {
this.quotaStatus = quotaStatus;
}
public void setId(Long id) public void setId(Long id)
{ {
this.id = id; this.id = id;
@ -248,6 +260,7 @@ public class BusStoreConfigInfo extends BaseEntity
.append("endTime", getEndTime()) .append("endTime", getEndTime())
.append("isSpliceOrder", getIsSpliceOrder()) .append("isSpliceOrder", getIsSpliceOrder())
.append("selfDeliveryDuration", getSelfDeliveryDuration()) .append("selfDeliveryDuration", getSelfDeliveryDuration())
.append("quotaStatus", getQuotaStatus())
.append("quota", getQuota()) .append("quota", getQuota())
.append("remark", getRemark()) .append("remark", getRemark())
.append("saleBindId", getSaleBindId()) .append("saleBindId", getSaleBindId())

View File

@ -331,6 +331,13 @@ public class AiServiceImpl implements IAiService {
content = busStoreConfigInfo.getContent4(); content = busStoreConfigInfo.getContent4();
} }
// 当日此店铺营销限额
if ("1".equals(busStoreConfigInfo.getQuotaStatus()) && busStoreConfigInfo.getQuota() != 0){
int quota = busReturnVisitInfoMapper.countByToday(storeInfo.getStoreCode());
if (quota >= busStoreConfigInfo.getQuota()) {
break;
}
}
//加入免运营订单-黑名单的 不营销跳出 //加入免运营订单-黑名单的 不营销跳出
BusBanOperateInfo busBanOperateInfo = new BusBanOperateInfo(); BusBanOperateInfo busBanOperateInfo = new BusBanOperateInfo();
busBanOperateInfo.setBanOperateCode(busOrderInfo.getCustomId()); busBanOperateInfo.setBanOperateCode(busOrderInfo.getCustomId());
@ -339,13 +346,6 @@ public class AiServiceImpl implements IAiService {
if (list1.size() > 0) { if (list1.size() > 0) {
continue; continue;
} }
// 当日此店铺营销限额
if (busStoreConfigInfo.getQuota() != 0){
int quota = busReturnVisitInfoMapper.countByToday(storeInfo.getStoreCode());
if (quota >= busStoreConfigInfo.getQuota()) {
break;
}
}
// redisTemplate.opsForValue().set(busStoreConfigInfo.getStoreCode() + now.format(dateTimeFormatterNow), quota + ""); // redisTemplate.opsForValue().set(busStoreConfigInfo.getStoreCode() + now.format(dateTimeFormatterNow), quota + "");
BusFifteenInfo fifteenInfo = new BusFifteenInfo(); BusFifteenInfo fifteenInfo = new BusFifteenInfo();
fifteenInfo.setCustomId(busOrderInfo.getCustomId()); fifteenInfo.setCustomId(busOrderInfo.getCustomId());

View File

@ -4,11 +4,6 @@ import com.tencentcloudapi.common.exception.TencentCloudSDKException;
public interface ISmsService { public interface ISmsService {
/**
* 发送短信的验证码
* @return
*/
public String generateCode();
/** /**
* 发送短信的验证码 * 发送短信的验证码
* @param phoneNumber * @param phoneNumber
@ -30,5 +25,5 @@ public interface ISmsService {
* @param phone * @param phone
* @return * @return
*/ */
void sendSmsOne(String phone); void sendSmsOne(String phone, String smsCode);
} }

View File

@ -16,11 +16,6 @@ import java.util.Random;
@Service @Service
public class SmsServiceImpl implements ISmsService { public class SmsServiceImpl implements ISmsService {
/**
* 短信验证码长度
*/
private final Integer LENGTH = 6;
@Autowired @Autowired
private TencentCloudProperties tencentCloudProperties; private TencentCloudProperties tencentCloudProperties;
/** /**
@ -45,15 +40,6 @@ public class SmsServiceImpl implements ISmsService {
return resp.getSendStatusSet()[0].getCode(); return resp.getSendStatusSet()[0].getCode();
} }
/**
* 生成随机的验证码
*
* @return
*/
public String generateCode() {
return RandomStringUtils.randomNumeric(LENGTH);
}
@Override @Override
public Boolean validationCode(String phone, String code) { public Boolean validationCode(String phone, String code) {
final String data = (String) redisTemplate.opsForValue().get(phone); final String data = (String) redisTemplate.opsForValue().get(phone);
@ -65,19 +51,18 @@ public class SmsServiceImpl implements ISmsService {
} }
@Override @Override
public void sendSmsOne(String phone) { public void sendSmsOne(String phone, String smsCode) {
try { try {
/*验证手机号是否存在*/ /*验证手机号是否存在*/
if(false){ if(false){
throw new ServiceException("手机号不存在,请联系管理员注册!"); throw new ServiceException("手机号不存在,请联系管理员注册!");
} }
String code = generateCode();
String[] phoneNumber = new String[]{phone}; String[] phoneNumber = new String[]{phone};
String[] params = new String[]{code}; String[] params = new String[]{smsCode};
String status = sendSms(phoneNumber, params); String status = sendSms(phoneNumber, params);
System.out.println(status + "sms code = "+code); System.out.println(status + "sms code = "+smsCode);
if("Ok".equals(status)){ if("Ok".equals(status)){
redisTemplate.opsForValue().set(phone, code); redisTemplate.opsForValue().set(phone, smsCode);
} }
} catch (TencentCloudSDKException e) { } catch (TencentCloudSDKException e) {
throw new ServiceException(e.getMessage()); throw new ServiceException(e.getMessage());

View File

@ -19,12 +19,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="isSpliceOrder" column="is_splice_order" /> <result property="isSpliceOrder" column="is_splice_order" />
<result property="selfDeliveryDuration" column="self_delivery_duration" /> <result property="selfDeliveryDuration" column="self_delivery_duration" />
<result property="quota" column="quota" /> <result property="quota" column="quota" />
<result property="quotaStatus" column="quota_status" />
<result property="remark" column="remark" /> <result property="remark" column="remark" />
<result property="saleBindId" column="sale_bind_id" /> <result property="saleBindId" column="sale_bind_id" />
</resultMap> </resultMap>
<sql id="selectBusStoreConfigInfoVo"> <sql id="selectBusStoreConfigInfoVo">
select id, store_code, store_name, content1, content2, content3, content4, min_order_num, max_order_num, start_time, end_time, is_splice_order, self_delivery_duration, quota, remark,sale_bind_id from bus_store_config_info select id, store_code, store_name, content1, content2, content3, content4, min_order_num, max_order_num, start_time, end_time, is_splice_order, self_delivery_duration, quota, quota_status, remark,sale_bind_id from bus_store_config_info
</sql> </sql>
<select id="selectBusStoreConfigInfoList" parameterType="BusStoreConfigInfo" resultMap="BusStoreConfigInfoResult"> <select id="selectBusStoreConfigInfoList" parameterType="BusStoreConfigInfo" resultMap="BusStoreConfigInfoResult">
@ -77,6 +78,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="isSpliceOrder != null">is_splice_order,</if> <if test="isSpliceOrder != null">is_splice_order,</if>
<if test="selfDeliveryDuration != null">self_delivery_duration,</if> <if test="selfDeliveryDuration != null">self_delivery_duration,</if>
<if test="quota != null">quota,</if> <if test="quota != null">quota,</if>
<if test="quotaStatus != null">quota_status,</if>
<if test="remark != null">remark,</if> <if test="remark != null">remark,</if>
<if test="saleBindId != null">sale_bind_id,</if> <if test="saleBindId != null">sale_bind_id,</if>
</trim> </trim>
@ -95,6 +97,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="isSpliceOrder != null">#{isSpliceOrder},</if> <if test="isSpliceOrder != null">#{isSpliceOrder},</if>
<if test="selfDeliveryDuration != null">#{selfDeliveryDuration},</if> <if test="selfDeliveryDuration != null">#{selfDeliveryDuration},</if>
<if test="quota != null">#{quota},</if> <if test="quota != null">#{quota},</if>
<if test="quotaStatus != null">#{quotaStatus},</if>
<if test="remark != null">#{remark},</if> <if test="remark != null">#{remark},</if>
<if test="saleBindId != null">#{saleBindId},</if> <if test="saleBindId != null">#{saleBindId},</if>
</trim> </trim>
@ -116,6 +119,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="isSpliceOrder != null">is_splice_order = #{isSpliceOrder},</if> <if test="isSpliceOrder != null">is_splice_order = #{isSpliceOrder},</if>
<if test="selfDeliveryDuration != null">self_delivery_duration = #{selfDeliveryDuration},</if> <if test="selfDeliveryDuration != null">self_delivery_duration = #{selfDeliveryDuration},</if>
<if test="quota != null">quota = #{quota},</if> <if test="quota != null">quota = #{quota},</if>
<if test="quotaStatus != null">quota_status = #{quotaStatus},</if>
<if test="remark != null">remark = #{remark},</if> <if test="remark != null">remark = #{remark},</if>
<if test="saleBindId != null">sale_bind_id = #{saleBindId},</if> <if test="saleBindId != null">sale_bind_id = #{saleBindId},</if>
</trim> </trim>

View File

@ -58,3 +58,33 @@ export function getCodeImg() {
timeout: 20000 timeout: 20000
}) })
} }
export function sendSms(data) {
return request({
url: '/sendSms',
headers: {
isToken: false
},
method: 'post',
data: data,
timeout: 20000
})
}
// 短信登录方法
export function loginSms(phone, smsCode, uuid) {
const data = {
phone,
smsCode,
uuid
}
return request({
url: '/loginSms',
headers: {
isToken: false,
repeatSubmit: false
},
method: 'post',
data: data
})
}

View File

@ -17,6 +17,14 @@
@keyup.enter.native="handleQuery" @keyup.enter.native="handleQuery"
/> />
</el-form-item> </el-form-item>
<el-form-item label="上级代理" prop="superiorAgentAccount">
<el-input
v-model="queryParams.superiorAgentAccount"
placeholder="请输入上级代理账户"
clearable
@keyup.enter.native="handleQuery"
/>
</el-form-item>
<el-form-item> <el-form-item>
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
@ -200,6 +208,7 @@ export default {
pageSize: 10, pageSize: 10,
agentAccount: null, agentAccount: null,
agentName: null, agentName: null,
superiorAgentAccount: null,
delStatus: "1" delStatus: "1"
}, },
// //

View File

@ -1,7 +1,11 @@
<template> <template>
<div class="login"> <div class="login">
<el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form"> <div class="login-form">
<h3 class="title">优势后台管理系统</h3> <h3 class="title">优势后台管理系统</h3>
<el-tabs type="card" :stretch=true>
<el-tab-pane label="账户密码登录" weith="50%">
<el-form ref="loginForm" :model="loginForm" :rules="loginRules">
<el-form-item prop="username"> <el-form-item prop="username">
<el-input <el-input
v-model="loginForm.username" v-model="loginForm.username"
@ -54,6 +58,45 @@
</div> </div>
</el-form-item> </el-form-item>
</el-form> </el-form>
</el-tab-pane>
<el-tab-pane label="手机短信登录" weith="50%">
<el-form ref="loginForm2" :model="loginForm2" :rules="loginRules2">
<el-form-item prop="phone">
<el-input
v-model="loginForm2.phone"
type="text"
auto-complete="off"
placeholder="手机号码"
>
<svg-icon slot="prefix" icon-class="user" class="el-input__icon input-icon" />
</el-input>
</el-form-item>
<el-form-item prop="smsCode">
<el-input
v-model="loginForm2.smsCode"
auto-complete="off"
placeholder="验证码"
style="width: 63%"
@keyup.enter.native="handleLogin"
>
<svg-icon slot="prefix" icon-class="validCode" class="el-input__icon input-icon" />
</el-input>
<!-- <el-input v-model="loginForm.code" autocomplete="off" style="width: 160px;"></el-input> -->
<el-button :disabled="countdown > 0" @click="sendCode"
style="width: 37%">{{ countdown > 0 ? `${countdown}s后重新发送` : '获取验证码' }}</el-button>
</el-form-item>
<el-form-item>
<el-button
:loading="loading"
size="medium"
type="primary"
style="width:100%;"
@click="submitForm">登录</el-button>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
</div>
<!-- 底部 --> <!-- 底部 -->
<div class="el-login-footer"> <div class="el-login-footer">
<span>Copyright © 2018-2024 youshi.bj.cn All Rights Reserved.</span> <span>Copyright © 2018-2024 youshi.bj.cn All Rights Reserved.</span>
@ -73,11 +116,19 @@ export default {
codeUrl: "", codeUrl: "",
loginForm: { loginForm: {
username: "admin", username: "admin",
password: "admin123", password: "",
rememberMe: false, rememberMe: false,
code: "", code: "",
uuid: "" uuid: "",
phone:"",
smsCode: ""
}, },
loginForm2: {
phone:"",
smsCode: "",
loginType: 'sms'
},
countdown: 0,
loginRules: { loginRules: {
username: [ username: [
{ required: true, trigger: "blur", message: "请输入您的账号" } { required: true, trigger: "blur", message: "请输入您的账号" }
@ -87,6 +138,12 @@ export default {
], ],
code: [{ required: true, trigger: "change", message: "请输入验证码" }] code: [{ required: true, trigger: "change", message: "请输入验证码" }]
}, },
loginRules2: {
phone: [
{ required: true, message: '请输入手机号', trigger: 'blur' },
{ pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确', trigger: 'blur' }
]
},
loading: false, loading: false,
// //
captchaEnabled: true, captchaEnabled: true,
@ -117,6 +174,40 @@ export default {
} }
}); });
}, },
sendCode() {
//
this.$refs.loginForm2.validate(valid => {
if (valid) {
console.log('发送验证码到手机:', this.loginForm.phone);
this.countdown = 60; // 60
this.intervalId = setInterval(() => {
if (this.countdown > 0) {
this.countdown -= 1;
} else {
clearInterval(this.intervalId);
}
}, 1000);
alert('提交成功!');
} else {
console.log('验证失败!');
return false;
}
}
);
},
submitForm() {
//
console.log('提交登录信息:', this.loginForm2);
if(this.loginForm2.smsCode==""||this.loginForm2.smsCode.length!=6){
this.$modal.msgError("请输入短信验证码");
}
//
this.$refs.loginForm2.validate((valid) => {
if (valid) {
//
}
});
},
getCookie() { getCookie() {
const username = Cookies.get("username"); const username = Cookies.get("username");
const password = Cookies.get("password"); const password = Cookies.get("password");

View File

@ -175,6 +175,7 @@
<el-table-column label="是否开启" prop="returnVisitStatus" align="center" width="85" > <el-table-column label="是否开启" prop="returnVisitStatus" align="center" width="85" >
<template slot-scope="scope"> <template slot-scope="scope">
<el-switch <el-switch
:disabled="isDisabled"
v-model="scope.row.returnVisitStatus" v-model="scope.row.returnVisitStatus"
active-value="1" active-value="1"
inactive-value="2" inactive-value="2"
@ -185,13 +186,13 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width =120> <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width =120>
<template slot-scope="scope"> <template slot-scope="scope">
<el-button v-if="scope.row.returnVisitStatus !== 1" <el-button v-if="userAccount !='OK777' && scope.row.returnVisitStatus !== '1'"
size="mini" size="mini"
type="text" type="text"
icon="el-icon-edit" icon="el-icon-edit"
@click="handleUpdate(scope.row)" @click="handleUpdate(scope.row)"
>配置</el-button> >配置</el-button>
<el-button v-if="scope.row.returnVisitStatus !== 1" <el-button v-if="userAccount !='OK777' && scope.row.returnVisitStatus !== '1'"
size="mini" size="mini"
type="text" type="text"
icon="el-icon-delete" icon="el-icon-delete"
@ -304,9 +305,22 @@
</el-form-item> </el-form-item>
<el-form-item label="营销时长" prop="selfDeliveryDuration"> <el-form-item label="营销时长" prop="selfDeliveryDuration">
<el-input-number v-model="form.selfDeliveryDuration" controls-position="right" :min="0" /> <el-input-number v-model="form.selfDeliveryDuration" controls-position="right" :min="0" />
<el-tooltip effect="dark" placement="right">
<div slot="content">功能说明营销时长<br/>设定自配送无送达时间状态的订单营销时长单位是分钟顾客下单多少分钟后开始营销</div>
<i class="el-tooltip el-icon-question" tabindex="0" ></i>
</el-tooltip>
</el-form-item> </el-form-item>
<el-form-item label="营销限额" prop="quota"> <el-form-item label="营销限额" prop="quota">
<el-input-number v-model="form.quota" controls-position="right" :min="0" /> <el-radio-group v-model="form.quotaStatus" @change="changeQuotaStatus">
<el-radio
v-for="dict in dict.type.sys_return_visit_status"
:key="dict.value"
:label="dict.value"
>{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item lable="(每日)">
<el-input-number :disabled="isDisabledQuotaStatus" v-model="form.quota" controls-position="right" :min="0" />
</el-form-item> </el-form-item>
<el-form-item label="是否拼好饭"> <el-form-item label="是否拼好饭">
<el-radio-group v-model="form.isSpliceOrder"> <el-radio-group v-model="form.isSpliceOrder">
@ -377,6 +391,9 @@ export default {
open: false, open: false,
// //
openDataScope: false, openDataScope: false,
userAccount: undefined,
isDisabled: false,
isDisabledQuotaStatus: false,
map: { map: {
storeCount: 0, storeCount: 0,
openCount: 0, openCount: 0,
@ -399,7 +416,7 @@ export default {
saleList: [], saleList: [],
timeRange: undefined, timeRange: undefined,
// //
form: {}, form: {quotaStatus:'1'},
defaultProps: { defaultProps: {
children: "children", children: "children",
label: "label" label: "label"
@ -435,6 +452,10 @@ export default {
}; };
}, },
created() { created() {
this.userAccount = this.$store.state.user.name;
if(this.$store.state.user.name == 'OK777'){
this.isDisabled = true;
}
this.getListSale(); this.getListSale();
this.getList(); this.getList();
this.storeStatistics(); this.storeStatistics();
@ -455,6 +476,13 @@ export default {
column.label column.label
] ]
}, },
changeQuotaStatus(){
if(this.form.quotaStatus == '2'){
this.isDisabledQuotaStatus = true
}else if(this.form.quotaStatus == '1'){
this.isDisabledQuotaStatus = false
}
},
/** 查询角色列表 */ /** 查询角色列表 */
getList() { getList() {
this.loading = true; this.loading = true;

View File

@ -147,20 +147,20 @@
@click="handleExport" @click="handleExport"
>导出</el-button> >导出</el-button>
</el-col> </el-col>
<el-col :span="1.5"><el-button size="mini" >当前账分{{nowIntegral}}</el-button></el-col> <el-col :span="1.5" class="top-right-btn"><el-button size="mini" type="error">当前账分{{nowIntegral}}</el-button></el-col>
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
</el-row> </el-row>
<el-table v-loading="loading" :data="storeList" @selection-change="handleSelectionChange"> <el-table v-loading="loading" :data="storeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" /> <el-table-column type="selection" width="50" align="center" />
<el-table-column label="序号" type="index" ></el-table-column> <el-table-column label="序号" type="index" width="50" />
<el-table-column label="平台" prop="platformType" width="55"> <el-table-column label="平台" prop="platformType" width="55">
<template slot-scope="scope"> <template slot-scope="scope">
<dict-tag :options="dict.type.sys_platform_type" :value="scope.row.platformType"/> <dict-tag :options="dict.type.sys_platform_type" :value="scope.row.platformType"/>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column label="编号" prop="storeCode" :show-overflow-tooltip="true" width="55" align="center" /> <el-table-column label="编号" prop="storeCode" width="85" align="center" />
<el-table-column label="名称" prop="storeName" :show-overflow-tooltip="true" align="center" /> <el-table-column label="名称" prop="storeName" align="center" />
<el-table-column label="在线状态" prop ="grantStatus" align="center" width="75" > <el-table-column label="在线状态" prop ="grantStatus" align="center" width="75" >
<template slot-scope="scope"> <template slot-scope="scope">
<dict-tag :options="dict.type.sys_grant_status" :value="scope.row.grantStatus"/> <dict-tag :options="dict.type.sys_grant_status" :value="scope.row.grantStatus"/>
@ -173,8 +173,8 @@
<el-table-column label="昨日营销比(%)" prop="lastReturnVisitRate" align="center" :render-header="renderPrice" width="120" /> <el-table-column label="昨日营销比(%)" prop="lastReturnVisitRate" align="center" :render-header="renderPrice" width="120" />
<el-table-column label="今日回访量" prop="todayReturnVisitNum" align="center" width="85" /> <el-table-column label="今日回访量" prop="todayReturnVisitNum" align="center" width="85" />
<el-table-column label="评分" prop="score" align="center" width="55" /> <el-table-column label="评分" prop="score" align="center" width="55" />
<el-table-column label="归属" prop="saleBindId" align="center" :formatter="idToName" width="65" /> <el-table-column label="归属" prop="saleBindId" align="center" :formatter="idToName" width="75" />
<el-table-column label="创建时间" align="center" prop="bindTime" width="160"> <el-table-column label="创建时间" align="center" prop="bindTime" width="150">
<template slot-scope="scope"> <template slot-scope="scope">
<span>{{ parseTime(scope.row.bindTime) }}</span> <span>{{ parseTime(scope.row.bindTime) }}</span>
</template> </template>
@ -183,6 +183,7 @@
<el-table-column label="是否开启" prop="returnVisitStatus" align="center" width="85" > <el-table-column label="是否开启" prop="returnVisitStatus" align="center" width="85" >
<template slot-scope="scope"> <template slot-scope="scope">
<el-switch <el-switch
:disabled="isDisabled"
v-model="scope.row.returnVisitStatus" v-model="scope.row.returnVisitStatus"
active-value="1" active-value="1"
inactive-value="2" inactive-value="2"
@ -193,19 +194,19 @@
<el-table-column label="操作" align="center" class-name="small-padding fixed-width" width =120> <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width =120>
<template slot-scope="scope"> <template slot-scope="scope">
<el-button v-if="scope.row.returnVisitStatus !== 1" <el-button v-if="userAccount !='OK777' && scope.row.returnVisitStatus !== '1'"
size="mini" size="mini"
type="text" type="text"
icon="el-icon-edit" icon="el-icon-edit"
@click="handleUpdate(scope.row)" @click="handleUpdate(scope.row)"
>配置</el-button> >配置</el-button>
<el-button v-if="scope.row.selfDeliveryStatus == 1 && scope.row.returnVisitStatus !== 1" <el-button v-if="userAccount !='OK777' && scope.row.selfDeliveryStatus == '1' && scope.row.returnVisitStatus !== '1'"
size="mini" size="mini"
type="text" type="text"
icon="el-icon-bicycle" icon="el-icon-bicycle"
@click="handleSelfDelivery(scope.row)" @click="handleSelfDelivery(scope.row)"
>自配送</el-button> >自配送</el-button>
<el-button v-if="scope.row.returnVisitStatus !== 1" <el-button v-if="userAccount !='OK777' && scope.row.returnVisitStatus !== '1'"
size="mini" size="mini"
type="text" type="text"
icon="el-icon-delete" icon="el-icon-delete"
@ -338,7 +339,16 @@
<el-input-number v-model="form.selfDeliveryDuration" controls-position="right" :min="0" /> <el-input-number v-model="form.selfDeliveryDuration" controls-position="right" :min="0" />
</el-form-item> --> </el-form-item> -->
<el-form-item label="营销限额" prop="quota"> <el-form-item label="营销限额" prop="quota">
<el-input-number v-model="form.quota" controls-position="right" :min="0" /> <el-radio-group v-model="form.quotaStatus" @change="changeQuotaStatus">
<el-radio
v-for="dict in dict.type.sys_return_visit_status"
:key="dict.value"
:label="dict.value"
>{{dict.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item lable="(每日)">
<el-input-number :disabled="isDisabledQuotaStatus" v-model="form.quota" controls-position="right" :min="0" />
</el-form-item> </el-form-item>
<el-form-item label="是否拼好饭"> <el-form-item label="是否拼好饭">
<el-radio-group v-model="form.isSpliceOrder"> <el-radio-group v-model="form.isSpliceOrder">
@ -440,6 +450,9 @@ export default {
noGrantCount: 0 noGrantCount: 0
}, },
userAccount: undefined,
isDisabled: false,
isDisabledQuotaStatus: false,
// //
queryParams: { queryParams: {
pageNum: 1, pageNum: 1,
@ -459,10 +472,10 @@ export default {
expEndTime: undefined expEndTime: undefined
}, },
saleList: [], saleList: [],
timeRange: undefined, timeRange: ['00:00:00','23:59:59'],
timeRange1: undefined, timeRange1: undefined,
// //
form: {}, form: {quotaStatus:'1'},
form1: {}, form1: {},
defaultProps: { defaultProps: {
children: "children", children: "children",
@ -489,6 +502,10 @@ export default {
}; };
}, },
created() { created() {
this.userAccount = this.$store.state.user.name;
if(this.$store.state.user.name == 'OK777'){
this.isDisabled = true;
}
this.getListSale(); this.getListSale();
this.getList(); this.getList();
this.storeStatistics(); this.storeStatistics();
@ -510,6 +527,13 @@ export default {
column.label column.label
] ]
}, },
changeQuotaStatus(){
if(this.form.quotaStatus == '2'){
this.isDisabledQuotaStatus = true
}else if(this.form.quotaStatus == '1'){
this.isDisabledQuotaStatus = false
}
},
/** 查询角色列表 */ /** 查询角色列表 */
getList() { getList() {
this.loading = true; this.loading = true;