一.回顾
邮件发送接收原理
使用JavaMail实现邮件发送
Spring+JavaMail整合实现邮件发送功能
带图片,附件邮件发送
二 什么是购销合同?
购销合同的主要组成部分?
三部分
购销合同主信息
货物信息
附件信息
工厂信息
公司销售和海外客户签订订单(合同),客户订单中的货物,公司就联系这些(多个) 厂家来生产,和生产厂家签订合同,这个合同就叫”购销合同”。购销合同的内容主 要由三部分组成,购销合同的主信息,和多个货物的信息,和多个附件的信息。(附 件实际就是货物)
购销合同打印出后,将每张纸交给对应生产厂家的销售代表(公司)
购销合同打印要求,可以每页只打印一款货物,也可以打印两款货物。用户可以自己 选择。
如果每页打印两款货物,必须是同一个生产厂家,如果不是同一个生产厂家,必须另 起一页。
在需求调研时,客户给我们提供纸质资料,电子资料(doc,excel)
三 数据库设计的三大范式
第一范式1NF :做到每列不可拆分
第二范式2NF :确保一个表只做一件事情
第三范式3NF :在满足2NF ,消除表中的传递依赖(空间最省)
Amstrong推理规则:A->B , B->C则A->C,这种情况如果存在,就要 想办法消除这种关系,它的目标是为了空间最省。
反三范式:
在进行数据库设计时,首先确保你所设计的表结构都达到第三范式的要 求,但是实际开发时,为了满足一些用户的特殊需求,开发时又会在符合第三范式的 表中,加入冗余字段,结果就导致又不符合第三范式要求了,这种情况下,咱们把它 称为反三范式。
因为存在冗余字段,所以就要去维护它,如购销合同中的合同总金额就是一个冗余 字段,就需要在平时添加货物时,添加附件时,都需要去更新购销合同中的总金额。
购销合同的表结构
四.分散计算思想
如果没有分散计算,在查询购销合同总金额时,就需要关联加载购销合同下的所有货 物,并要加载所有货物下的所有附件,这样可能实现购销合同总金额查询消耗时间太 多,而用户等不起,此时就可以在平时添加货物时,添加附件时,分别计算出货物总 金额,附件总金额,再更新购销合同总金额,这样就相当于将一次集中计算的工作量 分散到平时的多次计算过程中,所以查询购销合同总金额时速度就会很快。
早期系统上线,表现良好,反应速度很快,随着系统使用时间的増长
缺点:就是代码的编写量和维护工作量急剧上升
优点:提高了页面数据的检索速度
五.实现购销合同的CRUD
struts-cargo.xml
<package name="cargo" namespace="/cargo" extends="default">
<!-- 购销合同 -->
<action name="contractAction_*" class="contractAction" method="{1}">
<result name="list">/WEB-INF/pages/cargo/contract/jContractList.jsp</result>
<result name="toview">/WEB-INF/pages/cargo/contract/jContractView.jsp</result>
<result name="tocreate">/WEB-INF/pages/cargo/contract/jContractCreate.jsp</result>
<result name="toupdate">/WEB-INF/pages/cargo/contract/jContractUpdate.jsp</result>
<result name="alist" type="redirectAction">contractAction_list</result>
</action>
<!-- 购销合同 货物 -->
<action name="contractProductAction_*" class="contractProductAction" method="{1}">
<result name="tocreate">/WEB-INF/pages/cargo/contract/jContractProductCreate.jsp</result>
<result name="toupdate">/WEB-INF/pages/cargo/contract/jContractProductUpdate.jsp</result>
</action>
<!-- 购销合同 附件 -->
<action name="extCproductAction_*" class="extCproductAction" method="{1}">
<result name="tocreate">/WEB-INF/pages/cargo/contract/jExtCproductCreate.jsp</result>
<result name="toupdate">/WEB-INF/pages/cargo/contract/jExtCproductUpdate.jsp</result>
</action>
<!-- 出货表模块 -->
<action name="outProductAction_*" class="outProductAction" method="{1}">
<result name="toedit">/WEB-INF/pages/cargo/outproduct/jOutProduct.jsp</result>
</action>
</package>
Contract.hbm.xml
<hibernate-mapping package="cn.itcast.jk.domain">
<class name="Contract" table="CONTRACT_C" dynamic-insert="true" dynamic-update="true">
<id name="id" column="CONTRACT_ID">
<generator class="uuid"/>
</id>
<!-- 合同和货物,一对多 -->
<set name="contractProducts" cascade="all" inverse="true">
<key column="CONTRACT_ID"></key>
<one-to-many class="ContractProduct"/>
</set>
</class>
</hibernate-mapping>
ContractProduct.hbm.xml
<hibernate-mapping package="cn.itcast.jk.domain">
<class name="ContractProduct" table="CONTRACT_PRODUCT_C">
<id name="id" column="CONTRACT_PRODUCT_ID">
<generator class="uuid"/>
</id>
<!-- 货物和合同,多对一 -->
<many-to-one name="contract" class="Contract" column="CONTRACT_ID"/>
<!-- 货物和厂家,多对一 -->
<many-to-one name="factory" class="Factory" column="FACTORY_ID"/>
<!-- 货物和附件关联,一对多 -->
<set name="extCproducts" cascade="all" inverse="true">
<key column="CONTRACT_PRODUCT_ID"/>
<one-to-many class="ExtCproduct"/>
</set>
</class>
</hibernate-mapping>
ExtCproduct.hbm.xml
<hibernate-mapping package="cn.itcast.jk.domain">
<!-- 配置类的映射 -->
<class name="ExtCproduct" table="EXT_CPRODUCT_C" dynamic-insert="true" dynamic-update="true">
<!-- 1.配置主键生成策略 -->
<id name="id" column="EXT_CPRODUCT_ID" type="string">
<generator class="uuid"/>
</id>
<!-- 2.配置其他属性 -->
<property name="factoryName" column="FACTORY_NAME" type="string"/>
<!-- 附件和货物,多对一 -->
<many-to-one name="contractProduct" class="ContractProduct" column="CONTRACT_PRODUCT_ID"/>
<!-- 附件和厂家,多对一 -->
<many-to-one name="factory" class="Factory" column="FACTORY_ID"/>
</class>
</hibernate-mapping>
1.购销合同-列表
ContractAction.list() super.push(page);
/**
* 分页查询
*/
public String list() throws Exception {
String hql = "from Contract where 1=1 ";
//如何确定出用户的等级
User user = super.getCurUser();
int degree = user.getUserinfo().getDegree();
if(degree==4){
//说明是员工
hql+=" and createBy='"+user.getId()+"'";
}else if(degree==3){
//说明是部门经理,管理本部门
hql+=" and createDept='"+user.getDept().getId()+"'";
}else if(degree==2){
//说明是管理本部门及下属部门?????
}else if(degree==1){
//说明是副总?????
}else if(degree==0){
//说明是总经理
}
contractService.findPage(hql, page, Contract.class, null);
//设置分页的url地址
page.setUrl("contractAction_list");
//将page对象压入栈顶
super.push(page);
return "list";
}
jContractList.jsp
<tbody class="tableBody" >
${links }
<c:forEach items="${results}" var="o" varStatus="status">
<tr class="odd" onmouseover="this.className='highlight'" onmouseout="this.className='odd'" >
<td><input type="checkbox" name="id" value="${o.id}"/></td>
<td>${status.index+1}</td>
<td>${o.customName}</td>
<td><a href="contractAction_toview?id=${o.id}">${o.contractNo}</a></td>
<td>
${o.contractProducts.size() }
/
<c:set var="extNo" value="0"></c:set>
<c:forEach items="${o.contractProducts }" var="cp" >
<c:if test="${cp.extCproducts.size()!=0 }">
<c:set var="extNo" value="${extNo+cp.extCproducts.size() }"></c:set>
</c:if>
</c:forEach>
${extNo }
</td>
<td>${o.inputBy}</td>
<td>${o.checkBy}</td>
<td>${o.inspector}</td>
<td><fmt:formatDate value="${o.signingDate}" pattern="yyyy-MM-dd"/></td>
<td><fmt:formatDate value="${o.deliveryPeriod}" pattern="yyyy-MM-dd"/></td>
<td><fmt:formatDate value="${o.shipTime}" pattern="yyyy-MM-dd"/></td>
<td>${o.tradeTerms}</td>
<td>${o.totalAmount}</td>
<td><c:if test="${o.state==0}">草稿</c:if>
<c:if test="${o.state==1}"><font color="green">已上报</font></c:if></td>
<td><a href="${ctx }/cargo/contractProductAction_tocreate?contract.id=${o.id}">[货物]</a></td>
</tr>
</c:forEach>
</tbody>
2.购销合同-新增
ContractAction.tocreate()
/**
* 进入新增页面
*/
public String tocreate() throws Exception {
//调用业务方法
//跳页面
return "tocreate";
}
jContractCreate.jsp
<form name="icform" method="post">
<div id="menubar">
<div id="middleMenubar">
<div id="innerMenubar">
<div id="navMenubar">
<ul>
<li id="save"><a href="#" onclick="formSubmit('contractAction_insert','_self');this.blur();">保存</a></li>
<li id="back"><a href="#" onclick="history.go(-1);">返回</a></li>
</ul>
</div>
</div>
</div>
</div>
<div class="textbox" id="centerTextbox">
<div class="textbox-header">
<div class="textbox-inner-header">
<div class="textbox-title">
新增购销合同
</div>
</div>
</div>
<div>
<table class="commonTable" cellspacing="1">
<tr>
<td class="columnTitle">客户名称:</td>
<td class="tableContent"><input type="text" name="customName" value=""/></td>
<td class="columnTitle">打印版式:</td>
<td class="tableContentAuto">
<input type="radio" name="printStyle" value="2" checked class="input">两款
<input type="radio" name="printStyle" value="1" class="input">一款
</td>
</tr>
<tr>
<td class="columnTitle">合同号:</td>
<td class="tableContent"><input type="text" name="contractNo" value=""/></td>
<td class="columnTitle">收购方:</td>
<td class="tableContent"><input type="text" name="offeror" value=""/></td>
</tr>
<tr>
<td class="columnTitle">制单人:</td>
<td class="tableContent"><input type="text" name="inputBy" value=""/></td>
<td class="columnTitle">审单人:</td>
<td class="tableContent"><input type="text" name="checkBy" value=""/></td>
</tr>
<tr>
<td class="columnTitle">验货员:</td>
<td class="tableContent"><input type="text" name="inspector" value=""/></td>
<td class="columnTitle">签单日期:</td>
<td class="tableContent">
<input type="text" style="width:90px;" name="signingDate"
value=""
onclick="WdatePicker({el:this,isShowOthers:true,dateFmt:'yyyy-MM-dd'});"/>
</td>
</tr>
<tr>
<td class="columnTitle">重要程度:</td>
<td class="tableContentAuto">
<input type="radio" name="importNum" value="3" checked class="input">★★★
<input type="radio" name="importNum" value="2" class="input">★★
<input type="radio" name="importNum" value="1" class="input">★
</td>
<td class="columnTitle">船期:</td>
<td class="tableContent">
<input type="text" style="width:90px;" name="shipTime"
value=""
onclick="WdatePicker({el:this,isShowOthers:true,dateFmt:'yyyy-MM-dd'});"/>
</td>
</tr>
<tr>
<td class="columnTitle">贸易条款:</td>
<td class="tableContent"><input type="text" name="tradeTerms" value=""/></td>
<td class="columnTitle">交货期限:</td>
<td class="tableContent">
<input type="text" style="width:90px;" name="deliveryPeriod"
value=""
onclick="WdatePicker({el:this,isShowOthers:true,dateFmt:'yyyy-MM-dd'});"/>
</td>
</tr>
<tr>
<td class="columnTitle">要求:</td>
<td class="tableContent"><textarea name="crequest" style="height:150px;"></textarea>
<td class="columnTitle">说明:</td>
<td class="tableContent"><textarea name="remark" style="height:150px;"></textarea>
</tr>
</table>
</div>
</form>
ContractAction.insert()
/**
* 保存
* <s:select name="parent.id"
* <input type="text" name="deptName" value=""/>
* model对象能接收
* parent
* id
* deptName
*/
public String insert() throws Exception {
//1.加入细粒度权限控制的数据
User user = super.getCurUser();
model.setCreateBy(user.getId());//设置创建者的id
model.setCreateDept(user.getDept().getId());//设置创建者所在部门的id
//1.调用业务方法,实现保存
contractService.saveOrUpdate(model);
//跳页面
return "alist";
}
3.购销合同-修改
ContractAction.toupdate() super.push(obj);
/**
* 进入修改页面
*/
public String toupdate() throws Exception {
//1.根据id,得到一个对象
Contract obj = contractService.get(Contract.class, model.getId());
//2.将对象放入值栈中
super.push(obj);
//5.跳页面
return "toupdate";
}
jContractUpdate.jsp
<input type="hidden" name="id" value="${id }">
ContractAction.update()
/**
* 修改
*/
public String update() throws Exception {
//调用业务
Contract obj = contractService.get(Contract.class, model.getId());//根据id,得到一个数据库中保存的对象
//2.设置修改的属性
obj.setCustomName(model.getCustomName());
obj.setPrintStyle(model.getPrintStyle());
obj.setContractNo(model.getContractNo());
obj.setOfferor(model.getOfferor());
obj.setInputBy(model.getInputBy());
obj.setCheckBy(model.getCheckBy());
obj.setInspector(model.getInspector());
obj.setSigningDate(model.getSigningDate());
obj.setImportNum(model.getImportNum());
obj.setShipTime(model.getShipTime());
obj.setTradeTerms(model.getTradeTerms());
obj.setDeliveryPeriod(model.getDeliveryPeriod());
obj.setCrequest(model.getCrequest());
obj.setRemark(model.getRemark());
contractService.saveOrUpdate(obj);
return "alist";
}
4.购销合同-查看
ContractAction.toview() 准备数据 super.push(con);
public String toview() throws Exception {
//1.调用业务方法,根据id,得到对象
Contract con = contractService.get(Contract.class, model.getId());
//放入栈顶
super.push(con);
//3.跳页面
return "toview";
}
浏览购销合同
<div class="textbox-title">
<img src="${ctx }/skin/default/images/icon/currency_yen.png"/>
浏览购销合同
</div>
<div>
<table class="commonTable" cellspacing="1">
<tr>
<td class="columnTitle">客户名称:</td>
<td class="tableContent">${customName }</td>
<td class="columnTitle">打印版式:</td>
<td class="tableContentAuto">
${printStyle=='2'?"两款":"一款" }
</td>
</tr>
<tr>
<td class="columnTitle">合同号:</td>
<td class="tableContent">${contractNo }</td>
<td class="columnTitle">收购方:</td>
<td class="tableContent">${offeror }</td>
</tr>
<tr>
<td class="columnTitle">制单人:</td>
<td class="tableContent">${inputBy }</td>
<td class="columnTitle">审单人:</td>
<td class="tableContent">${checkBy }</td>
</tr>
<tr>
<td class="columnTitle">验货员:</td>
<td class="tableContent">${inspector }</td>
<td class="columnTitle">签单日期:</td>
<td class="tableContent">
<fmt:formatDate value='${signingDate }' pattern='yyyy-MM-dd' />
</td>
</tr>
<tr>
<td class="columnTitle">重要程度:</td>
<td class="tableContentAuto">
${importNum==3?"★★★":(importNum==2?"★★":"★") }
</td>
<td class="columnTitle">船期:</td>
<td class="tableContent">
<fmt:formatDate value='${shipTime }' pattern='yyyy-MM-dd' />
</td>
</tr>
<tr>
<td class="columnTitle">贸易条款:</td>
<td class="tableContent">${tradeTerms }</td>
<td class="columnTitle">交货期限:</td>
<td class="tableContent">
<fmt:formatDate value='${deliveryPeriod }' pattern='yyyy-MM-dd' />
</td>
</tr>
<tr>
<td class="columnTitle">要求:</td>
<td class="tableContent"><textarea cols="30">${crequest }</textarea></td>
<td class="columnTitle">说明:</td>
<td class="tableContent"><pre>${remark }</pre></td>
</tr>
</table>
</div>
</form>
货物列表 contractProducts cp cp.extCproducts
<div class="textbox-title">
<img src="${ctx }/skin/default/images/icon/currency_yen.png"/>
货物列表
</div>
<div class="eXtremeTable" >
<table id="ec_table" class="tableRegion" width="98%" >
<thead>
<tr>
<td class="tableHeader">序号</td>
<td class="tableHeader">厂家</td>
<td class="tableHeader">货号</td>
<td class="tableHeader">装率</td>
<td class="tableHeader">箱数</td>
<td class="tableHeader">包装单位</td>
<td class="tableHeader">数量</td>
<td class="tableHeader">单价</td>
<td class="tableHeader">总金额</td>
</tr>
</thead>
<tbody class="tableBody" >
<c:forEach items="${contractProducts}" var="cp" varStatus="status">
<tr bgcolor="#c3f3c3" height="30" class="odd" onmouseover="this.className='highlight'" onmouseout="this.className='odd'" >
<td>${status.index+1}</td>
<td>${cp.factoryName}</td>
<td>${cp.productNo}</td>
<td>${cp.loadingRate}</td>
<td>${cp.boxNum}</td>
<td>${cp.packingUnit}</td>
<td>${cp.cnumber}</td>
<td>${cp.price}</td>
<td>${cp.amount}</td>
</tr>
<c:forEach items="${cp.extCproducts}" var="ext" varStatus="status">
<tr height="30" class="odd" onmouseover="this.className='highlight'" onmouseout="this.className='odd'" >
<td align="right"><font color="blue">附件:${status.index+1} </font></td>
<td>${ext.factoryName}</td>
<td>${ext.productNo}</td>
<td> </td>
<td> </td>
<td>${ext.packingUnit}</td>
<td>${ext.cnumber}</td>
<td>${ext.price}</td>
<td>${ext.amount}</td>
</tr>
</c:forEach>
</c:forEach>
</tbody>
</table>
</div>
5.购销合同-提交
状态
ContractAction.submit()
public String submit() throws Exception {
String ids [] = model.getId().split(", ");
//2.遍历ids,并加载出每个购销合同对象,再修改购销合同的状态
contractService.changeState(ids, 1);
return "alist";
}
ContractServiceImpl.changeState()
@Override
public void changeState(String[] ids, Integer state) {
for(String id :ids){
Contract contract = baseDao.get(Contract.class, id);
contract.setState(state);
baseDao.saveOrUpdate(contract);//可以不写
}
}
5.购销合同-删除
ContractAction.delete()
/**
* 删除
*/
public String delete() throws Exception {
String ids[] = model.getId().split(", ");
//调用业务方法,实现批量删除
contractService.delete(Contract.class, ids);
return "alist";
}
ContractServiceImpl.delete()
public void deleteById(Class<Contract> entityClass, Serializable id) {
baseDao.deleteById(entityClass, id);//删除一个对象
}
public void delete(Class<Contract> entityClass, Serializable[] ids) {
for(Serializable id :ids){
this.deleteById(Contract.class,id);
}
}
六.实现货物的CRUD
购销合同-货物(新增、修改,删除,附件)
新增货物
厂家信息 ContractProductAction.tocreate() super.put(“factoryList”, factoryList);// 放入值栈中
/**
* 进入新增页面 <a href=
* "/jk28_web/cargo/contractProductAction_tocreate?contract.id=4028d3db5662dfb4015662f0ecbc0001">
* [货物]</a>
*/
public String tocreate() throws Exception {
// 1.调用业务方法,查询出生产货物的厂家
String hql = "from Factory where ctype='货物' and state=1";
List<Factory> factoryList = factoryService.find(hql, Factory.class, null);
super.put("factoryList", factoryList);// 放入值栈中
// 2.查询出当前购销合同下的货物列表
contractProductService.findPage("from ContractProduct where contract.id=?", page, ContractProduct.class,
new String[] { model.getContract().getId() });
//设置page的url
page.setUrl("contractProductAction_tocreate");
//将page放入栈顶
super.push(page);
// 跳页面
return "tocreate";
}
jContractProductCreate.jsp
<tr>
<td class="columnTitle">生产厂家:</td>
<td class="tableContent">
<s:select name="factory.id" list="factoryList"
onchange="setFactoryName(this.options[this.selectedIndex].text);"
listKey="id" listValue="factoryName"
headerKey="" headerValue="--请选择--"/>
<input type="hidden" id="factoryName" name="factoryName" value=""/>
</td>
<td class="columnTitle">货号:</td>
<td class="tableContentAuto"><input type="text" name="productNo" value=""/></td>
</tr>
新增货物,计算购销合同货物总金额
ContractProductServiceImpl.saveOrUpdate()
public void saveOrUpdate(ContractProduct entity) {
double amount = 0d;
if(UtilFuns.isEmpty(entity.getId())){
//新增
if(UtilFuns.isNotEmpty(entity.getPrice())&& UtilFuns.isNotEmpty(entity.getCnumber())){
amount= entity.getPrice()*entity.getCnumber();//货物总金额
entity.setAmount(amount);
}
//修改购销合同的总金额
Contract contract = baseDao.get(Contract.class, entity.getContract().getId());//根据购销合同的id,得到购销合同对象
contract.setTotalAmount(contract.getTotalAmount()+amount);
//保存购销合同的总金额
baseDao.saveOrUpdate(contract);
}else{
//修改
double oldAmount = entity.getAmount();//取出货物的原有总金额
if(UtilFuns.isNotEmpty(entity.getPrice())&& UtilFuns.isNotEmpty(entity.getCnumber())){
amount= entity.getPrice()*entity.getCnumber();//货物总金额
entity.setAmount(amount);
}
Contract contract = baseDao.get(Contract.class, entity.getContract().getId());//根据购销合同的id,得到购销合同对象
contract.setTotalAmount(contract.getTotalAmount()-oldAmount+amount);
}
baseDao.saveOrUpdate(entity);
}
货物 列表
货物 修改
货物 删除
删除货物,计算购销合同货物总金额
ContractProductServiceImpl.delete()
@Override
public void delete(Class<ContractProduct> entityClass, ContractProduct model) {
//1.加载要删除的货物对象
ContractProduct cp = baseDao.get(ContractProduct.class, model.getId());
//2.通过关联级别的数据加载,得到当前货物下的所有附件列表
Set<ExtCproduct> extCSet = cp.getExtCproducts();
//3.加载购销合同对象
Contract contract = baseDao.get(Contract.class, model.getContract().getId());
//4.遍历附件列表,并修改购销合同的总金额
for(ExtCproduct ec :extCSet){
contract.setTotalAmount(contract.getTotalAmount()-ec.getAmount());
}
//4.购销合同总金额 -货物总金额
contract.setTotalAmount(contract.getTotalAmount()-cp.getAmount());
//5.更新购销合同总金额
baseDao.saveOrUpdate(contract);
//6.删除货物对象 级联删除附件 <set name="extCproducts" cascade="all" inverse="true">
baseDao.deleteById(ContractProduct.class, model.getId());
}
七.附件的CURD
购销合同-货物-附件(修改,删除)
附件-新增
ExtCproductAction.tocreate() super.put(“factoryList”, factoryList);// 放入值栈中
/**
* 进入新增页面 <a href=
* "/jk28_web/cargo/contractProductAction_tocreate?contract.id=4028d3db5662dfb4015662f0ecbc0001">
* [货物]</a>
*/
public String tocreate() throws Exception {
// 1.调用业务方法,查询出生产附件的厂家
String hql = "from Factory where ctype='附件' and state=1";
List<Factory> factoryList = factoryService.find(hql, Factory.class, null);
super.put("factoryList", factoryList);// 放入值栈中
// 2.查询出当前货物下的附件列表
extCproductService.findPage("from ExtCproduct where contractProduct.id=?", page, ExtCproduct.class,
new String[] { model.getContractProduct().getId() });
//设置page的url
page.setUrl("extCproductAction_tocreate");
//将page放入栈顶
super.push(page);
// 跳页面
return "tocreate";
}
新增附件时,需要更新合同总金额
附件-列表
附件-修改
八.日历控件的补充
<script type="text/javascript" src="${ctx }/js/datepicker/WdatePicker.js""></script>
<td class="columnTitle">签单日期:</td>
<td class="tableContent">
<input type="text" style="width:90px;" name="signingDate"
value=""
onclick="WdatePicker({el:this,isShowOthers:true,dateFmt:'yyyy-MM-dd'});"/>
</td>
九.下拉框的取值
十.相关问题的思考
实现购销合同查看
1 货物数/附件数
<td>
${o.contractProducts.size() }
/
<c:set var="extNo" value="0"></c:set>
<c:forEach items="${o.contractProducts }" var="cp" >
<c:if test="${cp.extCproducts.size()!=0 }">
<c:set var="extNo" value="${extNo+cp.extCproducts.size() }"></c:set>
</c:if>
</c:forEach>
${extNo }
</td>
上面的代码是可以优化的!
如何优化?
分散计算原理指导下,就可以在购销合同表中,加入两个冗余字段(货物数,附
件数)
添加货物时就要同步更新购销合同的货物数, 添加附件时就要同步更新购销合同的附件数。