记一次会员联通ERP开卡需求

先说说智慧门店会员联通ERP,这个功能就是实现商家的会员在手机淘宝上开卡成为商家的会员,会员身份信息与商家自有ERP打通一体。开卡渠道一般分为两种,1.顾客扫描淘宝智慧门店的门店二维码,二维码对于ERP来说是带有门店信息2.就是会员自主搜索或打开开卡链接进行开卡活动,这个操作没有门店信息传入ERP。
废话少说,先见如下需求调整:
记一次会员联通ERP开卡需求_第1张图片

看到这个需求,先分析一下内容,看是不是有更简洁的方法实现。
1.卡号规则,虽然比较多内容,但是我们可以确认就是 固定字符+to_char(sysdate,‘YYDDMM’)+流水(使用for循环实现流水)
2.默认开卡类型,即默认传值
3.开卡店仓和手淘开卡店仓,实际上这两个字段和店仓表就是主外键关系。在传值时候可以仿照给开卡店仓传值的方法,来给手淘开卡店仓传值,关于扫码开卡或自选开卡,对于线下而言就是:'扫码门店’字段是否为空,我们可以使用这个字段进行验证,通俗来说,我用一下两张图就能解释开卡的这种逻辑

开卡门店 手淘开卡门店 脚本 我有女朋友了 国家规定这种情况我跟开卡门店的女朋友必须是同一个 好吧真tm可怜,你俩共用一个 真好,咱俩都是一家人 开卡门店 手淘开卡门店 脚本 ***新会员扫门店码绑定天猫卡***
开卡门店 手淘开卡门店 脚本 我没有女朋友 国家规定咱俩都没有的话,国家包发 好吧真tm可怜,你俩落魄到国家发放女朋友 真好,还是国家对咱们好 开卡门店 手淘开卡门店 脚本 ***新会员未扫门店码绑定天猫卡***

4.因为都是通过手机淘宝开卡,我们就默认传值:‘Y’,或者更合理的方式,在where条件后面判断来源,传’Y’。
5.关于绑卡。逻辑与开卡类似,其实就是在第三步上进行改动,将insert改为update
我们整理一下,其实都是可以从线下脚本调整实现,大致列为下面几类:
传门店值>新会员即insert,老会员即update>固定值传固定字符>卡号流水使用for循环实现递增。我还是用下面两个图表达这种绑卡会员身份的逻辑

sequenceDiagram
Title: ***老会员扫门店码绑定天猫卡***
手淘开卡门店-开卡门店: 我有女朋友啦!
Note right of 手淘开卡门店:开卡门店愣了一下,说:你tm有还跟我说?没有我借你
手淘开卡门店--手淘开卡门店:自给自足,衣食无忧。
手淘开卡门店 脚本 开卡门店 我有没有女朋友 他没有对象,你的借给他装逼吧 好的,大哥你说了算 老屌丝,开卡门店的女朋友借给你 手淘开卡门店 脚本 开卡门店 ***老会员未扫门店码绑定天猫卡***

先来第一步,卡号规则,难点在于流水号生成如何递增,先确定流水号,则有初始值。要将初始值存于表中,还要将递增后的值也更新进去。
在we_os表中设置流水卡号初始值为1。新增字段

alter table we_os  add card_no NUMBER(10);
update we_os set card_no=1;
commit;
cardnolength := length(v_cardno);--获取流水号的长度2018/08/21
  if cardnolength <> 5 then            --流水号初始值为0,长度为1
    while (5-cardnolength) >0 loop -- 判断流水号如果低于5位,就执行循环
      v_cardno := 0||v_cardno;        --每次循环都在流水号上加0,即直到为00001
      cardnolength := cardnolength +1; --每次循环,都将卡号长度值加1
    end loop;
  end if;    --循环结束,此时card_no变更为00001_cardno := cardprefix
   ||to_char(sysdate,'yymmdd')|| v_cardno; --卡号规则2018/08/22
		--卡号=TM+180822+00001  即TM18082200001
 update we_os set card_no = v_cardno+1 where appid=p_appid;--在每生成一张会员卡,card_no就加1,实现流水号递增

现在放入json参数进行测试
记一次会员联通ERP开卡需求_第2张图片

返回0,执行成功,现在我们看下卡号生成结果
卡号生成结果
但是考虑一个业务场景,如果有门店手工设置了卡号为TM18082300001,在18年8月23号如果有会员当天第一个开卡,系统生成的卡号势必和手工设置的卡号冲突。实际情况中还真发现了一例
记一次会员联通ERP开卡需求_第3张图片
我们可以看到触发时间,在毫秒以下,开卡提示违反数据库卡号唯一约束的这个人,实际上系统预生成的卡号就是TM18090900456,但是这个人的卡号在毫秒以前被上一个人开卡占用了。这种情况个人预估是数据库并发原因。
我们可以再加一层判断,预生成的卡号是否在系统中存在。存在,则流水号加1,直到预生成的卡号为唯一。

cardnolength := length(v_cardno);--获取流水号的长度
  s_count :=5-cardnolength;--5减去流水号的长度,赋值
  p_cardnostring:=NULL;--需要与流水号拼接成卡号的字符串,初始化为null
  
  for i IN 1..s_count LOOP--for循环实现拼接的卡号字符串生成
       p_cardnostring :=p_cardnostring||'0';
       --如s_count为3,则p_cardnostring为000
  end loop;
  
   select count(cv.id) into c_count  from c_client_vip cv
    where cv.cardno=cardprefix||to_char(sysdate,'yymmdd')
    ||p_cardnostring||v_cardno;
     --判断即将生成的会员卡号在系统中是否存在,并将结果赋值
     
   while c_count!=0 loop
     v_cardno:=v_cardno+1;
     --如果卡号存在,在流水号上加1,继续循环,直到即将生成的卡号在系统不存在
     select count(cv.id) into c_count from c_client_vip cv where
     cv.cardno=cardprefix||to_char(sysdate,'yymmdd')
     ||p_cardnostring|| v_cardno;
     --每循环一次,都对变量c_count重新赋值
    end loop;
     p_cardno := cardprefix ||to_char(sysdate,'yymmdd')||p_cardnostring|| v_cardno; 
      --卡号规则2018/09/10 

现在开始实现开卡门店与手淘门店传值。
判断传入的开卡门店并赋值给开卡门店和手淘开卡门店

IF   P_OPENCARDSTORE_CODE IS NOT NULL THEN--如果门店参数不为空
       SELECT c.ID INTO  P_OPENCARDSTORE_ID FROM C_STORE C WHERE C.CODE= P_OPENCARDSTORE_CODE; --将参数传给手淘开卡门店
     ELSE --如果为空,即业务上理解为未扫门店码
       SELECT W.c_Store_Id INTO P_OPENCARDSTORE_ID FROM c_client_vip W  WHERE W.Id=p_c_vip_id;--将这个会员的手淘开卡门店设置为默认门店
     END IF;--给手淘开卡门店P_OPENCARDSTORE_ID 赋值
      update c_client_vip cc set cc.st_opencardstore_id=P_OPENCARDSTORE_ID,cc.wisdom_store='Y' where cc.mobil=p_phonenum;--将手淘开卡门店字段传给VIP档案,将是否天猫会员变更为'Y'

我们将生成卡号步骤挖深详细说明。五位流水号,如果开卡人数达到99999个,第100000个人卡号就是’TM180824100000 '流水号达到6位数了。所以再次与客户确认卡号规则。
最终确定每天凌晨,五位流水号还原,那么就可以满足:'日期+5位流水号’为唯一数值
还原动作分两步:
1.设定还原数据的脚本,以存储过程最佳
2.设置job任务调用数据库存储过程
直接来个简单粗暴的,不传参的存储过程

CREATE OR REPLACE PROCEDURE UPDATE_WEOS_CARDNUMBER IS
BEGIN
  UPDATE WE_OS SET CARD_NO=0,MODIFIEDDATE=SYSDATE WHERE APPID='20180820115920';--每次被调用时候,把调用时间更新到we_os表中
  COMMIT;
END;

测试一下!

记一次会员联通ERP开卡需求_第4张图片
测试结果成功!
脚本设定好了,接下来就是job定时器设置,直接贴上代码

declare JOBAUTO PLS_INTEGER;

BEGIN 
   sys.DBMS_JOB.submit(job=>JOBAUTO,
   what=>'UPDATE_WEOS_CARDNUMBER;',--调用的存储过程名
   NEXT_DATE=>TO_DATE('25-08-2018 00:00:00','DD-MM-YYYY HH24:MI:SS'),--下次执行时间
   interval=>'trunc(sysdate+1)') ;--执行周期每24H一次
   commit;
end;

至此在每天凌晨对流水号清零任务已启动。
以上关于oracle定时器的语法,如果是mysql定时器,请查阅文档
mysql创建定时器

改代码测试过程中遇到疑难问题一:
老的会员绑卡时候,按照要求,如果传参,保留原开卡门店,参数传给手淘开卡店仓,但是实际测试中发现,传参同时都更新了开卡门店和手淘开卡门店。
检查代码并将传参打印出来,确定是不是传参问题

jor.put('errMessage', '本次扫码传入门店参数是'||P_OPENCARDSTORE_ID);

确定不是参数问题,发现问题,见如下代码

--判断门店是以线上为准
if isonlinestore='Y' and v_storeid is not null then
    update c_client_vip c set c.c_store_id=v_storeid,c.c_customer_id=v_customerid where c.id=p_c_vip_id;
  else
  if c_openstorecode is null and v_storeid is not null then  --线下是空,就已线上为准
      update c_client_vip c set c.c_store_id=v_storeid,c.c_customer_id=v_customerid where c.id=p_c_vip_id;
  else
    p_openstorecode:=c_openstorecode;
  end IF;

以上代码是说,如果传入参数为空,门店以线上为准,但是这个会员的线下开卡门店有,那么就将参数更新进开卡门店字段。再来看看我们的测试请求参数,果然是以线上门店为准
请求参数
ok,再次简单粗暴,确认以上代码不影响其它业务场景和流程后,赶紧注释注释注释掉!~

问题二:
设置会员表的手淘开卡门店与门店档案表主外键关联,无法创建主外键约束

alter table C_CLIENT_VIP
  add constraint FK_STC_STORE foreign key (ST_OPENCARDSTORE_ID)
  references C_STORE (ID) on delete set null;--设置主外键关系名FK_STC_STORE 当删除门店数据后,就将ST_OPENCARDSTORE_ID设置为null(空)

记一次会员联通ERP开卡需求_第5张图片
原来会员表的ST_OPENCARDSTORE_ID有部分数据外键值为0,排查发现0不在主键表的ID中,所以创建主外键时候,0无法在主键表的ID中找到,将ST_OPENCARDSTORE_ID设置为null

update C_CLIENT_VIP set ST_OPENCARDSTORE_ID=null where id=19088

通过以上的案例,我们整理一下流程:
1.对用户需求进行合理化分析,包含业务场景,需要怎么实现功能,对不合理的需求提出反驳意见以及修改建议
2.对需求按先后排序的方法,一步一步实现
3.对实际情况中发生未考虑全面的问题进行剖析和商讨解决方案
4.出现的各种新的问题,先自学,查百度,查资料,最后向专业请教,尽量独立完成
5.对每次的挑战,对问题和解决办法的汇报整理

你可能感兴趣的:(数据库,IT,数据库,需求,代码)